Add swipe gesture on photo Viewer

This commit is contained in:
2021-11-22 23:21:51 +01:00
parent ac8c49d954
commit ded276b8a0
5 changed files with 131 additions and 5 deletions

10
src/global.d.ts vendored
View File

@@ -1 +1,11 @@
/// <reference types="@sveltejs/kit" /> /// <reference types="@sveltejs/kit" />
/**
* Custom Events
*/
// Swipe
declare namespace svelte.JSX {
interface HTMLAttributes<T> {
onswipe?: (event: CustomEvent<string> & { target: EventTarget & T }) => any
}
}

View File

@@ -1,10 +1,11 @@
<script lang="ts"> <script lang="ts">
import { browser } from '$app/env' import { browser } from '$app/env'
import { page } from '$app/stores' import { page } from '$app/stores'
import dayjs from 'dayjs'
import advancedFormat from 'dayjs/plugin/advancedFormat.js'
import { getAssetUrlKey } from '$utils/helpers' import { getAssetUrlKey } from '$utils/helpers'
import { throttle } from '$utils/functions' import { throttle } from '$utils/functions'
import { swipe } from '$utils/interactions/swipe'
import dayjs from 'dayjs'
import advancedFormat from 'dayjs/plugin/advancedFormat.js'
// Components // Components
import Metas from '$components/Metas.svelte' import Metas from '$components/Metas.svelte'
import Image from '$components/atoms/Image.svelte' import Image from '$components/atoms/Image.svelte'
@@ -23,6 +24,7 @@
enum directions { PREV, NEXT } enum directions { PREV, NEXT }
let innerWidth: number
let globalOffset = offset let globalOffset = offset
let isLoading = false let isLoading = false
let hasNext = offset + limit < countPhotos let hasNext = offset + limit < countPhotos
@@ -74,6 +76,24 @@
} }
} }
// Enable swipe gestures
const handleSwipe = ({ detail }: CustomEvent<string>) => {
// Swipe up and down on mobile/small screens
if (innerWidth < 992) {
switch (detail) {
case '-y': goToNext(); break;
case 'y': goToPrevious(); break;
}
}
// Swipe left and right on larger screens
else {
switch (detail) {
case '-x': goToNext(); break;
case 'x': goToPrevious(); break;
}
}
}
/** /**
* Load photos * Load photos
@@ -152,7 +172,10 @@
} }
</script> </script>
<svelte:window on:keydown={handleKeydown} /> <svelte:window
bind:innerWidth
on:keydown={handleKeydown}
/>
{#if currentPhoto} {#if currentPhoto}
<Metas <Metas
@@ -166,7 +189,7 @@
<main class="viewer-photo"> <main class="viewer-photo">
<div class="container grid"> <div class="container grid">
<div class="viewer-photo__carousel"> <div class="viewer-photo__carousel">
<div class="viewer-photo__images"> <div class="viewer-photo__images" use:swipe on:swipe={handleSwipe}>
{#each visiblePhotos as photo, index (photo.id)} {#each visiblePhotos as photo, index (photo.id)}
<Image <Image
class="photo photo--{currentIndex === 0 ? index + 1 : index}" class="photo photo--{currentIndex === 0 ? index + 1 : index}"

View File

@@ -11,7 +11,7 @@ body {
color: #fff; color: #fff;
cursor: default; cursor: default;
overflow-x: hidden; overflow-x: hidden;
overscroll-behavior-y: none; overscroll-behavior: none;
} }
*, *:before, *:after { *, *:before, *:after {
text-rendering: optimizeLegibility; text-rendering: optimizeLegibility;

View File

@@ -2,6 +2,7 @@
height: 100vh; height: 100vh;
display: flex; display: flex;
align-items: center; align-items: center;
overflow: hidden;
.container { .container {
height: 100%; height: 100%;
@@ -37,6 +38,7 @@
width: 100%; width: 100%;
margin: auto auto 0; margin: auto auto 0;
padding-top: 66.66%; padding-top: 66.66%;
touch-action: none;
.photo { .photo {
--opacity: 1; --opacity: 1;

View File

@@ -0,0 +1,91 @@
/**
* Swipe action
* @description Detects horizontal or vertical swipe on an element
* @link Inspired by https://github.com/Rezi/svelte-gestures/blob/main/src/swipe.ts
*/
export const swipe = (
node: HTMLElement,
options: SwipeOptions = {
travelX: 60,
travelY: 30,
timeframe: 1000,
}
) => {
let startTime: number
let startX: number
let startY: number
let endX: number
let endY: number
if (!node) {
throw new Error('No specified node')
}
// Detect swipe direction from given values
const detectDirection = (startX: number, startY: number, endX: number, endY: number) => {
// If action has been made within the timeframe (after would return)
if (Date.now() - startTime < options.timeframe) {
const deltaX = endX - startX
const deltaY = endY - startY
const absX = Math.abs(deltaX)
const absY = Math.abs(deltaY)
// Horizontal
if (absX >= 2 * absY && absX > options.travelX) {
return deltaX > 0 ? 'x' : '-x'
}
// Vertical
else if (absY >= 2 * absX && absY > options.travelY) {
return deltaY > 0 ? 'y' : '-y'
}
// Tap
else return null
}
}
// Down event
const onDown = ({ x, y }: PointerEvent) => {
startX = x
startY = y
startTime = Date.now()
}
node.addEventListener('pointerdown', onDown, false)
// Up event
const onUp = ({ x, y }: PointerEvent) => {
endX = x
endY = y
// Get direction from values
const direction = detectDirection(startX, startY, endX, endY)
if (direction) {
// Dispatch event if direction has been detected
node.dispatchEvent(
new CustomEvent('swipe', { detail: direction })
)
}
}
node.addEventListener('pointerup', onUp, false)
return {
/**
* Destroy
*/
destroy () {
node.removeEventListener('pointerdown', onDown)
node.removeEventListener('pointerup', onUp)
}
}
}
/**
* Types
*/
interface SwipeOptions {
travelX?: number
travelY?: number
timeframe?: number
}