diff --git a/src/Carousel/Carousel.svelte b/src/Carousel/Carousel.svelte index 72954fc..69beef8 100644 --- a/src/Carousel/Carousel.svelte +++ b/src/Carousel/Carousel.svelte @@ -12,6 +12,16 @@ import Dots from '../Dots/Dots.svelte' import Arrow from '../Arrow/Arrow.svelte' import { NEXT, PREV } from '../direction' + import { swipeable } from '../swipeable' + import { + addResizeEventListener, + removeResizeEventListener + } from '../utils/event' + + const directionFnDescription = { + [NEXT]: showNextPage, + [PREV]: showPrevPage + } /** * Enable Next/Prev arrows @@ -37,6 +47,7 @@ * Transition speed (ms) */ export let speed = 500 + let _speed = speed /** * Enables auto play of slides @@ -61,8 +72,8 @@ let currentPageIndex = 0 let pagesCount = 0 let pageWidth = 0 - let offset - let pageWindow + let offset = 0 + let pageWindowElement let pagesElement const unsubscribe = store.subscribe(value => { @@ -71,7 +82,7 @@ function applySlideSizes() { const children = pagesElement ? pagesElement.children : [] - pageWidth = pageWindow.clientWidth + pageWidth = pageWindowElement.clientWidth const slidesCount = children.length pagesCount = getPagesCount({ slidesCount, slidesToShow }) @@ -87,14 +98,10 @@ } function applyAutoplay() { - const autoplayDirectionFnDescription = { - [NEXT]: showNextPage, - [PREV]: showPrevPage - } let interval if (autoplay) { interval = setInterval(() => { - autoplayDirectionFnDescription[autoplayDirection]() + directionFnDescription[autoplayDirection]() }, autoplaySpeed) } return { @@ -105,14 +112,15 @@ } onMount(() => { - store.init(initialPageIndex) applySlideSizes() + store.init(initialPageIndex) + applyOffset() const { teardownAutoplay } = applyAutoplay() - window.addEventListener('resize', applySlideSizes) + addResizeEventListener(applySlideSizes) return () => { - window.removeEventListener('resize', applySlideSizes) + removeResizeEventListener(applySlideSizes) teardownAutoplay() } }) @@ -141,6 +149,21 @@ store.next({ infinite, pagesCount }) applyOffset() } + + function handleSwipeStart() { + _speed = 0 + } + function handleThreshold(event) { + _speed = speed + directionFnDescription[event.detail.direction]() + } + function handleSwipeMove(event) { + offset += event.detail.dx + } + function handleSwipeEnd() { + _speed = speed + showPage(currentPageIndex) + }
@@ -154,17 +177,22 @@ {/if}
-
+
{#if arrows} diff --git a/src/Carousel/CarouselView.svelte b/src/Carousel/CarouselView.svelte index fc57a52..bb29ba3 100644 --- a/src/Carousel/CarouselView.svelte +++ b/src/Carousel/CarouselView.svelte @@ -94,6 +94,7 @@ display: flex; align-items: center; justify-content: center; + user-select: none; } .color-container > p { font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; diff --git a/src/swipeable.js b/src/swipeable.js new file mode 100644 index 0000000..e32b74e --- /dev/null +++ b/src/swipeable.js @@ -0,0 +1,77 @@ +import { NEXT, PREV } from './direction' +import { + addStartEventListener, + removeStartEventListener, + addMoveEventListener, + removeMoveEventListener, + addEndEventListener, + removeEndEventListener, + createDispatcher +} from './utils/event' + +function getCoords(event) { + if (event instanceof TouchEvent) { + const touch = event.touches[0] + return { + x: touch ? touch.clientX : 0, + y: touch ? touch.clientY : 0 + } + } + return { + x: event.clientX, + y: event.clientY + } +} + +// TODO: rename to slidable +export function swipeable(node, { thresholdProvider }) { + const dispatch = createDispatcher(node) + let x + let y + let moved = 0 + + function handleMousedown(event) { + moved = 0 + const coords = getCoords(event) + x = coords.x + y = coords.y + dispatch('start', { x, y }) + addMoveEventListener(window, handleMousemove) + addEndEventListener(window, handleMouseup) + } + + function handleMousemove(event) { + const coords = getCoords(event) + const dx = coords.x - x + const dy = coords.y - y + x = coords.x + y = coords.y + dispatch('move', { x, y, dx, dy }) + + if (dx !== 0 && Math.sign(dx) !== Math.sign(moved)) { + moved = 0 + } + moved += dx + if (Math.abs(moved) > thresholdProvider()) { + dispatch('threshold', { direction: moved > 0 ? PREV : NEXT }) + removeEndEventListener(window, handleMouseup) + removeMoveEventListener(window, handleMousemove) + } + } + + function handleMouseup(event) { + const coords = getCoords(event) + x = coords.x + y = coords.y + dispatch('end', { x, y }) + removeEndEventListener(window, handleMouseup) + removeMoveEventListener(window, handleMousemove) + } + + addStartEventListener(node, handleMousedown) + return { + destroy() { + removeStartEventListener(node, handleMousedown) + } + } +} diff --git a/src/utils/event.js b/src/utils/event.js new file mode 100644 index 0000000..4b488c6 --- /dev/null +++ b/src/utils/event.js @@ -0,0 +1,46 @@ +// start event +export function addStartEventListener(source, cb) { + source.addEventListener('mousedown', cb) + source.addEventListener('touchstart', cb) +} +export function removeStartEventListener(source, cb) { + source.removeEventListener('mousedown', cb) + source.removeEventListener('touchstart', cb) +} + +// end event +export function addEndEventListener(source, cb) { + source.addEventListener('mouseup', cb) + source.addEventListener('touchend', cb) +} +export function removeEndEventListener(source, cb) { + source.removeEventListener('mouseup', cb) + source.removeEventListener('touchend', cb) +} + +// move event +export function addMoveEventListener(source, cb) { + source.addEventListener('mousemove', cb) + source.addEventListener('touchmove', cb) +} +export function removeMoveEventListener(source, cb) { + source.removeEventListener('mousemove', cb) + source.removeEventListener('touchmove', cb) +} + +// resize event +export function addResizeEventListener(cb) { + window.addEventListener('resize', cb) +} +export function removeResizeEventListener(cb) { + window.removeEventListener('resize', cb) +} + +export function createDispatcher(source) { + function dispatch(event, data) { + source.dispatchEvent(new CustomEvent(event, { + detail: data + })) + } + return dispatch +} \ No newline at end of file