Files
housesof/src/organisms/Carousel.svelte
Félix Péault 6ff05fea48 Fix Fullscreen double click bug, Default size for photos
- Reset the value of swiped when changing photo
2020-04-08 14:33:06 +02:00

240 lines
8.8 KiB
Svelte

<script>
import { onMount, createEventDispatcher } from 'svelte'
import { stores } from '@sapper/app'
import { currentLocation, fullscreen } from 'utils/store'
import { getThumbnail, formatDate } from 'utils/functions'
const dispatch = createEventDispatcher()
const { page } = stores()
// Dependencies
import SwipeListener from 'swipe-listener'
import lazySizes from 'lazysizes'
// Animations
import { animateIn } from 'animations/Carousel'
// Components
import IconArrow from 'atoms/IconArrow'
import Counter from 'atoms/Counter'
import PaginationDots from 'molecules/PaginationDots'
// Props and Variables
export let photos
export let viewer = false
export let locationUrl = false
let scope
let swiped
let currentIndex = 0
// Reactive variables from currentIndex
$: currentPhoto = photos[currentIndex] || null
$: prevPhoto = photos[currentIndex - 1] || photos[photos.length - 1]
$: nextPhoto = photos[currentIndex + 1] || photos[0]
// Default size for the image
const defaultWidth = 900
const defaultHeight = Math.ceil(defaultWidth / 1.5)
/*
** Navigate to a photo
*/
// Change current index from param
const goToPhoto = to => {
if (to === 'prev') {
currentIndex--
currentIndex = (currentIndex < 0) ? photos.length - 1 : currentIndex
} else if (to === 'next') {
currentIndex++
currentIndex = (currentIndex >= photos.length) ? 0 : currentIndex
} else {
currentIndex = to
}
// Dispatch current photo
dispatch('photoChange', photos[currentIndex])
// Reset fullscreen value if open
if ($fullscreen) fullscreen.set()
// Reset swiped event if fired
swiped = false
}
// Hover on controls
const hoverPhoto = event => {
const button = event.currentTarget.querySelector('button')
const photoActive = scope.querySelector('.is-active')
let photoToHover
if (event.currentTarget.dataset.to === 'prev') {
photoToHover = (photoActive.previousSibling != null) ? photoActive.previousSibling : photoActive.parentNode.lastChild
} else {
photoToHover = (photoActive.nextSibling != null) ? photoActive.nextSibling : photoActive.parentNode.firstChild
}
// Toggle class and focus of the button
if (event.type === 'mouseenter') {
photoToHover.classList.add('hover')
button.classList.add('hover')
} else if (event.type === 'mouseleave') {
photoToHover.classList.remove('hover')
button.classList.remove('hover')
}
}
// Open fullscreen
const openFullscreen = event => {
if (!swiped) {
fullscreen.set(currentPhoto)
}
// Reset swiped event if fired
swiped = false
}
/*
** Navigation
*/
// Drag and swipe
const swipe = directions => {
swiped = true
// Detect swipe direction
if (directions.right) {
goToPhoto('prev')
} else if (directions.left) {
goToPhoto('next')
}
}
// Keyboard navigation
const keyboardNav = event => {
if ([37,80,74].includes(event.keyCode)) {
goToPhoto('prev')
} else if ([39,78,75].includes(event.keyCode)) {
goToPhoto('next')
}
}
/*
** Run code when mounted
*/
onMount(() => {
// Entering transition
animateIn(scope)
// Enable gestures
const touch = SwipeListener(scope)
// Viewer: Navigate to photo on init and URL change
if (viewer) {
page.subscribe(page => {
if (page.path.includes('/viewer/')) {
goToPhoto(photos.findIndex(photo => photo.slug === page.params.photo))
}
})
}
})
</script>
<svelte:window on:keydown={keyboardNav} />
<div class="carousel" role="region" aria-label="Photos"
bind:this={scope}
on:swipe={event => swipe(event.detail.directions)}
>
<div class="wrap">
<div class="gallery">
<div class="gallery__images">
{#each photos as { id, name, location, image }, index}
<div class="gallery__photo"
class:is-prev={id === prevPhoto.id}
class:is-active={id === currentPhoto.id}
class:is-next={id === nextPhoto.id}
role="tabpanel"
aria-label="Photo {index + 1} of {photos.length}"
aria-hidden={index === currentIndex ? undefined : true}
>
<picture class="gallery__picture"
on:click={openFullscreen}
>
<source media="(min-width: 968px)" data-srcset={getThumbnail(image.private_hash, 1200)}>
<source media="(min-width: 800px)" data-srcset={getThumbnail(image.private_hash, 900)}>
<source media="(min-width: 500px)" data-srcset={getThumbnail(image.private_hash, 600)}>
<source media="(min-width: 300px)" data-srcset={getThumbnail(image.private_hash, 400)}>
<img src={getThumbnail(image.private_hash, 900)}
alt="{name}, {location.name}, {location.country.name}"
width={defaultWidth} height={defaultHeight}
class:lazyload={
// Load the current index and the two previous/next ones
index === currentIndex || index >= currentIndex - 2 && index <= currentIndex + 2
// If last photo, load the two first ones (index = 0)
|| currentIndex === photos.length - 1 && (index === 0 || index === 1)
// If first photo, load the two last ones (index = photos length)
|| currentIndex === 0 && (index === photos.length - 1 || index === photos.length - 2)
}
>
</picture>
</div>
{/each}
</div>
<div class="carousel__controls">
<div class="carousel__area carousel__area--prev" data-to="prev" rel="prev"
on:mouseenter={hoverPhoto} on:mouseleave={hoverPhoto}
on:click={() => goToPhoto('prev')}
>
<button class="button-control button-control--white dir-left" aria-label="Previous">
<IconArrow direction="left" color="#ff6c89" class="icon" />
<IconArrow direction="left" color="#fff" class="icon" hidden="true" />
</button>
</div>
<div class="carousel__area carousel__area--next" data-to="next" rel="next"
on:mouseenter={hoverPhoto} on:mouseleave={hoverPhoto}
on:click={() => goToPhoto('next')}
>
<button class="button-control button-control--white dir-right" aria-label="Next">
<IconArrow direction="right" color="#ff6c89" class="icon" />
<IconArrow direction="right" color="#fff" class="icon" hidden="true" />
</button>
</div>
</div>
{#if viewer}
<Counter {currentIndex} className="carousel__number" />
{/if}
</div>
<div class="carousel__infos">
<div class="carousel__locations">
{#each photos as { id, name, location }, index}
<div class="carousel__location style-location"
class:is-prev={id === prevPhoto.id}
class:is-active={id === currentPhoto.id}
class:is-next={id === nextPhoto.id}
>
<p class="street">{name}</p>
<p class="state style-caps style-caps--transparent">
{#if locationUrl}
<a href="/location/{location.country.slug}/{location.slug}" sapper-noscroll>
{location.name}, {location.country.name}
</a>
{:else}
{location.name}, {location.country.name}
{/if}
</p>
</div>
{/each}
</div>
{#if viewer}
<p class="carousel__date">{formatDate(currentPhoto.date, 'FULL')}</p>
{/if}
</div>
</div>
<PaginationDots className="carousel__dots" {photos} {currentIndex}
on:goToIndex={event => currentIndex = event.detail}
/>
</div>