{"id":3451,"date":"2022-07-04T07:19:30","date_gmt":"2022-07-04T07:19:30","guid":{"rendered":"https:\/\/rengga.dev\/blog\/?p=3451"},"modified":"2023-06-10T11:36:38","modified_gmt":"2023-06-10T11:36:38","slug":"js-tutorial-drag-and-scroll-carousel-slider","status":"publish","type":"post","link":"https:\/\/rengga.dev\/blog\/js-tutorial-drag-and-scroll-carousel-slider\/","title":{"rendered":"JS Tutorial &#8211; Drag and Scroll Carousel Slider"},"content":{"rendered":"<p><span style=\"color: #ef3207;\"><a style=\"color: #ef3207;\" href=\"https:\/\/rengga.dev\/\" target=\"_blank\" rel=\"noopener\"><strong>Rengga Dev<\/strong><\/a> <\/span>&#8211; Try playing with the parameters on the gui to the right. The Drag and Scroll Carousel Slider is just one effect of javascript.<\/p>\n<p><iframe style=\"width: 100%;\" title=\"JS Tutorial - Drag and Scroll Carousel Slider\" src=\"https:\/\/codepen.io\/renggagumilar\/embed\/qBYEOrO?default-tab=result&amp;theme-id=dark\" height=\"600\" frameborder=\"no\" scrolling=\"no\" allowfullscreen=\"allowfullscreen\"><br \/>\nSee the Pen <a href=\"https:\/\/codepen.io\/renggagumilar\/pen\/qBYEOrO\"><br \/>\nJS Tutorial &#8211; Drag and Scroll Carousel Slider<\/a> by Rengga Gumilar (<a href=\"https:\/\/codepen.io\/renggagumilar\">@renggagumilar<\/a>)<br \/>\non <a href=\"https:\/\/codepen.io\">CodePen<\/a>.<br \/>\n<\/iframe><\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"js\">const lerp = (f0, f1, t) =&gt; (1 - t) * f0 + t * f1\r\nconst clamp = (val, min, max) =&gt; Math.max(min, Math.min(val, max))\r\n\r\nclass DragScroll {\r\n  constructor(obj) {\r\n    this.$el = document.querySelector(obj.el)\r\n    this.$wrap = this.$el.querySelector(obj.wrap)\r\n    this.$items = this.$el.querySelectorAll(obj.item)\r\n    this.$bar = this.$el.querySelector(obj.bar)\r\n    this.init()\r\n  }\r\n  \r\n  init() {\r\n    this.progress = 0\r\n    this.speed = 0\r\n    this.oldX = 0\r\n    this.x = 0\r\n    this.playrate = 0\r\n    \/\/\r\n    this.bindings()\r\n    this.events()\r\n    this.calculate()\r\n    this.raf()\r\n  }\r\n  \r\n  bindings() {\r\n    [\r\n     'events', \r\n     'calculate',\r\n     'raf', \r\n     'handleWheel', \r\n     'move', \r\n     'raf', \r\n     'handleTouchStart',\r\n     'handleTouchMove', \r\n     'handleTouchEnd'\r\n    ].forEach(i =&gt; { this[i] = this[i].bind(this) })\r\n  }\r\n  \r\n  calculate() {\r\n    this.progress = 0\r\n    this.wrapWidth = this.$items[0].clientWidth * this.$items.length\r\n    this.$wrap.style.width = `${this.wrapWidth}px`\r\n    this.maxScroll = this.wrapWidth - this.$el.clientWidth\r\n  }\r\n  \r\n  handleWheel(e) {\r\n    this.progress += e.deltaY\r\n    this.move()\r\n  }\r\n  \r\n  handleTouchStart(e) {\r\n    e.preventDefault()\r\n    this.dragging = true\r\n    this.startX = e.clientX || e.touches[0].clientX\r\n    this.$el.classList.add('dragging')\r\n  }\r\n\r\n  handleTouchMove(e) {\r\n    if (!this.dragging) return false\r\n    const x = e.clientX || e.touches[0].clientX\r\n    this.progress += (this.startX - x) * 2.5\r\n    this.startX = x\r\n    this.move()\r\n  }\r\n\r\n  handleTouchEnd() {\r\n    this.dragging = false\r\n    this.$el.classList.remove('dragging')\r\n  }\r\n  \r\n  move() {\r\n    this.progress = clamp(this.progress, 0, this.maxScroll)\r\n  }\r\n  \r\n  events() {\r\n    window.addEventListener('resize', this.calculate)\r\n    window.addEventListener('wheel', this.handleWheel)\r\n    \/\/\r\n    this.$el.addEventListener('touchstart', this.handleTouchStart)\r\n    window.addEventListener('touchmove', this.handleTouchMove)\r\n    window.addEventListener('touchend', this.handleTouchEnd)\r\n    \/\/\r\n    window.addEventListener('mousedown', this.handleTouchStart)\r\n    window.addEventListener('mousemove', this.handleTouchMove)\r\n    window.addEventListener('mouseup', this.handleTouchEnd)\r\n    document.body.addEventListener('mouseleave', this.handleTouchEnd)\r\n  }\r\n  \r\n  raf() {\r\n    \/\/ requestAnimationFrame(this.raf)\r\n    this.x = lerp(this.x, this.progress, 0.1)\r\n    this.playrate = this.x \/ this.maxScroll\r\n    \/\/\r\n    this.$wrap.style.transform = `translateX(${-this.x}px)`\r\n    this.$bar.style.transform = `scaleX(${.18 + this.playrate * .82})`\r\n    \/\/\r\n    this.speed = Math.min(100, this.oldX - this.x)\r\n    this.oldX = this.x\r\n    \/\/\r\n    this.scale = lerp(this.scale, this.speed, 0.1)\r\n    this.$items.forEach(i =&gt; {\r\n      i.style.transform = `scale(${1 - Math.abs(this.speed) * 0.002})`\r\n      i.querySelector('img').style.transform = `scaleX(${1 + Math.abs(this.speed) * 0.004})`\r\n    })\r\n  }\r\n}\r\n\r\n\r\n\/*--------------------\r\nInstances\r\n--------------------*\/\r\nconst scroll = new DragScroll({\r\n  el: '.carousel',\r\n  wrap: '.carousel--wrap',\r\n  item: '.carousel--item',\r\n  bar: '.carousel--progress-bar',\r\n})\r\n\r\n\r\n\/*--------------------\r\nOne raf to rule em all\r\n--------------------*\/\r\nconst raf = () =&gt; {\r\n  requestAnimationFrame(raf)\r\n  scroll.raf()\r\n}\r\nraf()\r\n<\/pre>\n<p>&nbsp;<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Rengga Dev &#8211; Try playing with the parameters on the gui to <a class=\"read-more\" href=\"https:\/\/rengga.dev\/blog\/js-tutorial-drag-and-scroll-carousel-slider\/\" title=\"JS Tutorial &#8211; Drag and Scroll Carousel Slider\" itemprop=\"url\"><\/a><\/p>\n","protected":false},"author":1,"featured_media":3552,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[20,12],"tags":[325,339,338,264,263,103,227],"newstopic":[],"class_list":{"0":"post-3451","1":"post","2":"type-post","3":"status-publish","4":"format-standard","5":"has-post-thumbnail","7":"category-javascript","8":"category-web-development","9":"tag-background-effects","10":"tag-carousel-slider","11":"tag-drag-and-scroll","12":"tag-javascript-tutorial","13":"tag-js-tutorial","14":"tag-web-design","15":"tag-web-designer"},"_links":{"self":[{"href":"https:\/\/rengga.dev\/blog\/wp-json\/wp\/v2\/posts\/3451","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/rengga.dev\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/rengga.dev\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/rengga.dev\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/rengga.dev\/blog\/wp-json\/wp\/v2\/comments?post=3451"}],"version-history":[{"count":1,"href":"https:\/\/rengga.dev\/blog\/wp-json\/wp\/v2\/posts\/3451\/revisions"}],"predecessor-version":[{"id":3453,"href":"https:\/\/rengga.dev\/blog\/wp-json\/wp\/v2\/posts\/3451\/revisions\/3453"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/rengga.dev\/blog\/wp-json\/wp\/v2\/media\/3552"}],"wp:attachment":[{"href":"https:\/\/rengga.dev\/blog\/wp-json\/wp\/v2\/media?parent=3451"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/rengga.dev\/blog\/wp-json\/wp\/v2\/categories?post=3451"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/rengga.dev\/blog\/wp-json\/wp\/v2\/tags?post=3451"},{"taxonomy":"newstopic","embeddable":true,"href":"https:\/\/rengga.dev\/blog\/wp-json\/wp\/v2\/newstopic?post=3451"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}