Merge pull request #11 from vadimkorr/feature/#10_Add-touch-gestures

feature/#10 Add touch gestures
This commit is contained in:
Vadim
2021-01-23 00:46:45 +03:00
committed by GitHub
4 changed files with 166 additions and 14 deletions

View File

@@ -12,6 +12,16 @@
import Dots from '../Dots/Dots.svelte' import Dots from '../Dots/Dots.svelte'
import Arrow from '../Arrow/Arrow.svelte' import Arrow from '../Arrow/Arrow.svelte'
import { NEXT, PREV } from '../direction' 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 * Enable Next/Prev arrows
@@ -37,6 +47,7 @@
* Transition speed (ms) * Transition speed (ms)
*/ */
export let speed = 500 export let speed = 500
let _speed = speed
/** /**
* Enables auto play of slides * Enables auto play of slides
@@ -61,8 +72,8 @@
let currentPageIndex = 0 let currentPageIndex = 0
let pagesCount = 0 let pagesCount = 0
let pageWidth = 0 let pageWidth = 0
let offset let offset = 0
let pageWindow let pageWindowElement
let pagesElement let pagesElement
const unsubscribe = store.subscribe(value => { const unsubscribe = store.subscribe(value => {
@@ -71,7 +82,7 @@
function applySlideSizes() { function applySlideSizes() {
const children = pagesElement ? pagesElement.children : [] const children = pagesElement ? pagesElement.children : []
pageWidth = pageWindow.clientWidth pageWidth = pageWindowElement.clientWidth
const slidesCount = children.length const slidesCount = children.length
pagesCount = getPagesCount({ slidesCount, slidesToShow }) pagesCount = getPagesCount({ slidesCount, slidesToShow })
@@ -87,14 +98,10 @@
} }
function applyAutoplay() { function applyAutoplay() {
const autoplayDirectionFnDescription = {
[NEXT]: showNextPage,
[PREV]: showPrevPage
}
let interval let interval
if (autoplay) { if (autoplay) {
interval = setInterval(() => { interval = setInterval(() => {
autoplayDirectionFnDescription[autoplayDirection]() directionFnDescription[autoplayDirection]()
}, autoplaySpeed) }, autoplaySpeed)
} }
return { return {
@@ -105,14 +112,15 @@
} }
onMount(() => { onMount(() => {
store.init(initialPageIndex)
applySlideSizes() applySlideSizes()
store.init(initialPageIndex)
applyOffset()
const { teardownAutoplay } = applyAutoplay() const { teardownAutoplay } = applyAutoplay()
window.addEventListener('resize', applySlideSizes) addResizeEventListener(applySlideSizes)
return () => { return () => {
window.removeEventListener('resize', applySlideSizes) removeResizeEventListener(applySlideSizes)
teardownAutoplay() teardownAutoplay()
} }
}) })
@@ -141,6 +149,21 @@
store.next({ infinite, pagesCount }) store.next({ infinite, pagesCount })
applyOffset() 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)
}
</script> </script>
<div class="main-container"> <div class="main-container">
@@ -154,17 +177,22 @@
{/if} {/if}
<div <div
class="content-container" class="content-container"
bind:this={pageWindow} bind:this={pageWindowElement}
> >
<div <div
use:swipeable="{{ thresholdProvider: () => pageWidth/3 }}"
on:start={handleSwipeStart}
on:move={handleSwipeMove}
on:end={handleSwipeEnd}
on:threshold={handleThreshold}
style=" style="
transform: translateX({offset}px); transform: translateX({offset}px);
transition-duration: {speed}ms; transition-duration: {_speed}ms;
" "
bind:this={pagesElement} bind:this={pagesElement}
> >
<slot></slot> <slot></slot>
</div> </div>
</div> </div>
{#if arrows} {#if arrows}
<slot name="next" {showNextPage}> <slot name="next" {showNextPage}>

View File

@@ -94,6 +94,7 @@
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
user-select: none;
} }
.color-container > p { .color-container > p {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;

77
src/swipeable.js Normal file
View File

@@ -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)
}
}
}

46
src/utils/event.js Normal file
View File

@@ -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
}