Temporary browsable Carousel on photo page

Stuff to be fixed/work on:
- Why is currentPhoto only triggered at the second photo and not the first?
- popState events (prev/next on browser) not working - detect URL changes and change the currentIndex
This commit is contained in:
2020-03-02 22:30:27 +01:00
parent 1db9217cd0
commit 804880a908
4 changed files with 156 additions and 77 deletions

View File

@@ -1,7 +1,10 @@
<script> <script>
import { onMount } from 'svelte' import { onMount, createEventDispatcher } from 'svelte'
import { stores } from '@sapper/app'
import { currentLocation } from '../store' import { currentLocation } from '../store'
import * as fn from '../functions' import * as fn from '../functions'
const dispatch = createEventDispatcher()
const { page } = stores()
// Dependencies // Dependencies
import dayjs from 'dayjs' import dayjs from 'dayjs'
@@ -12,27 +15,47 @@
import IconArrow from '../atoms/IconArrow' import IconArrow from '../atoms/IconArrow'
// Props // Props
export let photos
export let viewer = false export let viewer = false
export let photos = [] export let init = undefined
// Variables // Variables
let hover let hover
let currentIndex = 1 let currentIndex = 0
$: currentPhoto = photos[currentIndex] || null $: currentPhoto = photos[currentIndex] || null
$: prevPhoto = photos[currentIndex - 1] || photos[photos.length - 1] $: prevPhoto = photos[currentIndex - 1] || photos[photos.length - 1]
$: nextPhoto = photos[currentIndex + 1] || photos[0] $: nextPhoto = photos[currentIndex + 1] || photos[0]
/* /*
** Go to a photo ** Navigate to a photo
*/ */
const goToPrev = () => { // Go to specific photo on Init
currentIndex-- if (init) {
currentIndex = (currentIndex < 0) ? photos.length - 1 : currentIndex currentIndex = photos.findIndex(photo => photo.slug === init.params.photo)
} }
const goToNext = () => {
currentIndex++ // Send current photo to event
currentIndex = (currentIndex >= photos.length) ? 0 : currentIndex const sendCurrentPhoto = ({ init = false }) => {
console.log('sendCurrentPhoto event: ', currentPhoto.slug)
dispatch('photoChange', {
currentPhoto: currentPhoto,
init
})
}
// Change the current index depending on direction
const goToPhoto = direction => {
if (direction === 'prev') {
currentIndex--
currentIndex = (currentIndex < 0) ? photos.length - 1 : currentIndex
} else if (direction === 'next') {
currentIndex++
currentIndex = (currentIndex >= photos.length) ? 0 : currentIndex
}
// Send current photo to event
sendCurrentPhoto({ init: false })
} }
@@ -51,25 +74,19 @@
const swipeEnd = event => { const swipeEnd = event => {
touchEndX = event.changedTouches[0].screenX touchEndX = event.changedTouches[0].screenX
touchEndY = event.changedTouches[0].screenY touchEndY = event.changedTouches[0].screenY
if (touchEndX <= touchStartX) goToNext() if (touchEndX <= touchStartX) goToPhoto('prev')
if (touchEndX >= touchStartX) goToPrev() if (touchEndX >= touchStartX) goToPhoto('next')
} }
// Keyboard navigation
const keyboardNav = event => {
const keyCode = event.keyCode
switch (event.keyCode) { /*
case 37: case 80: case 74: ** Keyboard navigation
goToPrev() */
break const keyboardNav = event => {
case 39: case 78: case 75: if ([37,80,74].includes(event.keyCode)) {
goToNext() goToPhoto('prev')
break } else if ([39,78,75].includes(event.keyCode)) {
// case 27: case 67: goToPhoto('next')
// document.getElementById('photo_close').click()
// break
default: break
} }
} }
@@ -90,22 +107,25 @@
photoToHover = (photoActive.nextSibling != null) ? photoActive.nextSibling : photoActive.parentNode.firstChild photoToHover = (photoActive.nextSibling != null) ? photoActive.nextSibling : photoActive.parentNode.firstChild
} }
switch (event.type) { // Toggle class and focus of the button
case 'mouseenter': if (event.type === 'mouseenter') {
photoToHover.classList.add('hover') photoToHover.classList.add('hover')
button.focus() button.focus()
break } else if (event.type === 'mouseleave') {
case 'mouseleave': photoToHover.classList.remove('hover')
photoToHover.classList.remove('hover') button.blur()
button.blur()
break
default: break
} }
} }
// Send current photo to event for init
sendCurrentPhoto({ init: true })
}) })
/* /*
REVEAL ANIMATION ON CAROUSEL: TODO: REVEAL ANIMATION ON CAROUSEL
Fix the CSS priority? Cancel the transition delay and duration? Fix the CSS priority? Cancel the transition delay and duration?
data-aos="{ data-aos="{
(photo === prevPhoto) ? 'carousel-prev' : (photo === prevPhoto) ? 'carousel-prev' :
@@ -117,7 +137,10 @@
<svelte:window on:keydown={keyboardNav} /> <svelte:window on:keydown={keyboardNav} />
<div class="carousel" on:touchstart={swipeStart} on:touchend={swipeEnd}> <div class="carousel"
on:touchstart={swipeStart}
on:touchend={swipeEnd}
>
<div class="wrap"> <div class="wrap">
<div class="gallery"> <div class="gallery">
<div class="gallery__images"> <div class="gallery__images">
@@ -137,14 +160,20 @@
</div> </div>
<div class="carousel__controls"> <div class="carousel__controls">
<div class="carousel__area carousel__area--prev" data-to="prev" on:mouseenter={hover} on:mouseleave={hover} on:click={goToPrev}> <div class="carousel__area carousel__area--prev" data-to="prev"
on:mouseenter={hover} on:mouseleave={hover}
on:click={() => goToPhoto('prev')}
>
<button class="button-control button-control--white dir-left" aria-label="Previous"> <button class="button-control button-control--white dir-left" aria-label="Previous">
<IconArrow direction="left" color="#ff6c89" class="icon" /> <IconArrow direction="left" color="#ff6c89" class="icon" />
<IconArrow direction="left" color="#fff" class="icon" hidden="true" /> <IconArrow direction="left" color="#fff" class="icon" hidden="true" />
</button> </button>
</div> </div>
<div class="carousel__area carousel__area--next" data-to="next" on:mouseenter={hover} on:mouseleave={hover} on:click={goToNext}> <div class="carousel__area carousel__area--next" data-to="next"
on:mouseenter={hover} on:mouseleave={hover}
on:click={() => goToPhoto('next')}
>
<button class="button-control button-control--white dir-right" aria-label="Next"> <button class="button-control button-control--white dir-right" aria-label="Next">
<IconArrow direction="right" color="#ff6c89" class="icon" /> <IconArrow direction="right" color="#ff6c89" class="icon" />
<IconArrow direction="right" color="#fff" class="icon" hidden="true" /> <IconArrow direction="right" color="#fff" class="icon" hidden="true" />

View File

@@ -9,13 +9,14 @@
// Define either to preload data or use the store // Define either to preload data or use the store
let preloaded let preloaded
currentLocation.subscribe(store => preloaded = store ? store : undefined) currentPhotos.subscribe(store => preloaded = store ? store : undefined)
// Preload data // Preload data
export async function preload (page, session) { export async function preload (page, session) {
// Load the photos if not loaded // Load the photos if not loaded
if (!preloaded) { if (!preloaded) {
const req = await this.fetch(`${apiEndpoints.rest}/items/photos?fields=id,name,slug,image.*,image.data,location.*,location.country.*,created_on&filter[location.slug][rlike]=%${page.params.location}%`) const req = await this.fetch(`${apiEndpoints.rest}/items/photos?fields=id,name,slug,image.*,location.*,location.country.*,created_on,modified_on&filter[location.slug][rlike]=%${page.params.location}%`)
const photos = await req.json() const photos = await req.json()
return { return {
photos: photos.data photos: photos.data
@@ -23,14 +24,17 @@
} }
// Use the store otherwise // Use the store otherwise
else return { else return {
photos: preloaded.photos photos: preloaded
} }
} }
</script> </script>
<script> <script>
import { onMount } from 'svelte' import { onMount, createEventDispatcher } from 'svelte'
import { goto } from '@sapper/app'
import * as fn from '../../../../functions' import * as fn from '../../../../functions'
const { page } = stores()
const dispatch = createEventDispatcher()
// Dependencies // Dependencies
import dayjs from 'dayjs' import dayjs from 'dayjs'
@@ -41,38 +45,80 @@
import IconGlobe from '../../../../atoms/IconGlobe' import IconGlobe from '../../../../atoms/IconGlobe'
import IconCross from '../../../../atoms/IconCross' import IconCross from '../../../../atoms/IconCross'
import Carousel from '../../../../organisms/Carousel' import Carousel from '../../../../organisms/Carousel'
import SocialMetas from '../../../../utils/SocialMetas'
// Props and variables // Props
const { page } = stores()
export let photos export let photos
let currentIndex
let indexFormated // Variables
let viewerPhotos
let windowWidth let windowWidth
let changeWindowWidth let currentPhoto
// Define current location // Define current location
const location = (!$currentLocation) ? $locations.find(loc => loc.slug === $page.params.location) : $currentLocation const location = (!$currentLocation) ? $locations.find(loc => loc.slug === $page.params.location) : $currentLocation
const locationFull = `${location.name}, ${location.country.name}`
// Update store current location informations // Update store current location informations
if (!$currentLocation) currentLocation.set(location) if (!$currentLocation) currentLocation.set(location)
if (!$currentPhotos) currentPhotos.set(photos) if (!$currentPhotos) currentPhotos.set(photos)
// Define path // The photo has changed from the carousel
const path = `/viewer/${location.country.slug}/${location.slug}/` const photoChanged = event => {
currentPhoto = event.detail.currentPhoto
console.log('photoChanged [photo] event: ', currentPhoto.slug)
// Change the URL to the current photo
if (!event.detail.init) {
const windowPathname = window.location.pathname
const newUrl = windowPathname.substring(0, windowPathname.lastIndexOf('/') + 1) + currentPhoto.slug
history.pushState('', document.title, newUrl)
}
}
// Access a direct photo or navigate in history
const photoSlug = $page.params.photo
// On init, send an event to the Carousel component with the photoSlug to set currentIndex and then change the photo
// Pop event from browser (prev/next)
// const changedUrl = event => {
// dispatch('changedUrl', {
// location: event.target.location
// })
// }
/*
!!! TODO:
- URL navigation on photo change (from Carousel)
- If coming to direct URL, show the proper photo (i.e currentIndex from photo.slug in URL)
- Get currentPhoto from Carousel to update title and metas
*/
/* /*
** Run code on browser only ** Run code on browser only
*/ */
onMount(() => { onMount(() => {
/*
!!! TODO:
- Change the title with the current photo name and update Metas (with window location url)
*/
// console.log(currentPhoto)
dispatch('changeUrl', {
page: $page
})
}) })
</script> </script>
<svelte:head> <svelte:head>
<title>Houses Of - photoName photoCountryName</title> <title>Houses Of Photos of {location.name}, {location.country.name}</title>
<meta name="description" content={location.description}>
<SocialMetas
title="Houses Of - Beautiful houses of {location.name}, {location.country.name}"
description="Houses Of {location.name} {location.description}"
/>
<!-- url="//{$page.host.split(':3000')[0]}/viewer/{currentPhoto.location.country.slug}/{currentPhoto.location.slug}/{currentPhoto.slug}" -->
<!-- image={currentPhoto ? fn.getThumbnail(currentPhoto.image.private_hash, 1200, 630) : null} -->
</svelte:head> </svelte:head>
<svelte:window bind:innerWidth={windowWidth} /> <svelte:window bind:innerWidth={windowWidth} />
@@ -95,5 +141,10 @@
</div> </div>
</div> </div>
<Carousel {photos} viewer={true} /> <Carousel
{photos}
viewer={true}
init={$page}
on:photoChange={photoChanged}
/>
</section> </section>

View File

@@ -45,7 +45,7 @@
// Photo // Photo
&__photo { &__photo {
$duration: 0.9s; $duration: 1s;
opacity: 0; opacity: 0;
overflow: hidden; overflow: hidden;
@@ -57,7 +57,7 @@
height: 100%; height: 100%;
transform: scale($scale); transform: scale($scale);
box-shadow: 0 pxVW(15) pxVW(60) rgba(#000, 0.3); box-shadow: 0 pxVW(15) pxVW(60) rgba(#000, 0.3);
transition: transform $duration $ease-quart, opacity $duration $ease-quart; transition: transform $duration $ease-quart, opacity $duration / 2 $ease-quart;
will-change: transform, opacity; will-change: transform, opacity;
@include breakpoint (sm) { @include breakpoint (sm) {

View File

@@ -94,8 +94,8 @@
} }
} }
// Information // Informations
&__information { &__infos {
margin-top: 32px; margin-top: 32px;
padding: 0 24px; padding: 0 24px;
@@ -104,27 +104,26 @@
justify-content: space-between; justify-content: space-between;
padding: 0; padding: 0;
} }
}
// Location // Location
&--location { &__locations {
width: 75%; width: 75%;
text-align: left; }
&__location {
text-align: left;
margin-top: 0;
}
.street { // Date
margin: 0; &__date {
} color: rgba($color-tertiary, 0.5);
} font-size: rem(12px);
margin-top: 16px;
// Date @include breakpoint (sm) {
&--date { font-size: rem(14px);
color: rgba($color-tertiary, 0.5); margin: 0;
font-size: rem(12px);
margin-top: 16px;
@include breakpoint (sm) {
font-size: rem(14px);
margin: 0;
}
} }
} }