Created
November 28, 2021 23:12
-
-
Save jesperlandberg/c4654e838651513fc081fd0c11e7de6b to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import gsap from 'gsap' | |
import ScrollTrigger from 'gsap/ScrollTrigger' | |
import { evt, utils, store } from '@/core' | |
const { qs, qsa, rect } = utils | |
const { bounds } = store | |
export default function (el) { | |
const container = qs('.js-carousel-slides', el) | |
const slides = qsa('.js-carousel-slide', el) | |
let tX = 0 | |
let cX = 0 | |
let max = 0 | |
let cancelX = 0 | |
let cancelY = 0 | |
let on = 0 | |
let idxLast = 0 | |
let idxCurrent = 0 | |
let isDragging = false | |
let isResizing = false | |
let isTicking = false | |
let cache | |
let offset | |
let snaps | |
const total = slides.length - 1 | |
function setSlides() { | |
offset = rect(container).left | |
snaps = [] | |
cache = slides.map((slide, i) => { | |
gsap.set(slide, { x: 0 }) | |
const { left, right, width } = rect(slide) | |
const xSet = gsap.quickSetter(slide, 'x', 'px') | |
i === total && (max = left - offset) | |
snaps.push(left - offset) | |
return { | |
el, left, width, xSet, | |
start: left - bounds.ww, end: right, | |
out: true, | |
} | |
}) | |
} | |
function down({ x, y, target }) { | |
if (!target.closest('.js-carousel')) return | |
isDragging = true | |
cancelX = x | |
cancelY = y | |
on = tX + x * 2 | |
} | |
function move({ x, y, e }) { | |
if (!isDragging) return | |
if ((Math.abs(x - cancelX) > Math.abs(y - cancelY)) && e.cancelable) { | |
e.preventDefault() | |
e.stopPropagation() | |
} | |
tX = on - x * 2 | |
clamp() | |
} | |
function up() { | |
if (!isDragging) return | |
isDragging = false | |
snap() | |
} | |
function snap() { | |
tX = gsap.utils.snap(snaps, tX) | |
idxLast = idxCurrent | |
idxCurrent = snaps.indexOf(tX) | |
} | |
function clamp() { | |
tX = gsap.utils.clamp(0, max, tX) | |
} | |
function inView(start, end, width, left) { | |
const isVisible = cX > start && cX < end | |
let progress = 0 | |
isVisible && (progress = gsap.utils.clamp(0, 1, | |
1 + (cX - left - width) / (bounds.ww + width))) | |
return { | |
isVisible, progress | |
} | |
} | |
function tick() { | |
if (!isTicking) return | |
cX += (tX - cX) * 0.075 | |
cX = Math.round(cX * 100) / 100 | |
!isResizing && transformSlides() | |
} | |
function transformSlides() { | |
cache.length && | |
cache.forEach(c => { | |
const { start, end, width, left, xSet } = c | |
const { isVisible, progress } = inView(start, end, width, left) | |
if (isVisible || isResizing) { | |
c.out && (c.out = false) | |
xSet(-cX) | |
} else if (!c.out) { | |
c.out = true | |
xSet(-cX) | |
} | |
}) | |
} | |
function resize() { | |
isResizing = true | |
setSlides() | |
transformSlides() | |
isResizing = false | |
} | |
function mount() { | |
evt.on('mouseup', up) | |
evt.on('mousedown', down) | |
evt.on('mousemove', move) | |
evt.on('resize', resize) | |
evt.on('tick', tick) | |
ScrollTrigger.create({ | |
trigger: el, | |
onToggle: self => isTicking = self.isActive | |
}) | |
} | |
function unmount() { | |
evt.off('mouseup', up) | |
evt.off('mousedown', down) | |
evt.off('mousemove', move) | |
evt.off('resize', resize) | |
evt.off('tick', tick) | |
} | |
setSlides() | |
mount() | |
return unmount | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment