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>
import { onMount } from 'svelte'
import { onMount, createEventDispatcher } from 'svelte'
import { stores } from '@sapper/app'
import { currentLocation } from '../store'
import * as fn from '../functions'
const dispatch = createEventDispatcher()
const { page } = stores()
// Dependencies
import dayjs from 'dayjs'
@@ -12,29 +15,49 @@
import IconArrow from '../atoms/IconArrow'
// Props
export let photos
export let viewer = false
export let photos = []
export let init = undefined
// Variables
let hover
let currentIndex = 1
let currentIndex = 0
$: currentPhoto = photos[currentIndex] || null
$: prevPhoto = photos[currentIndex - 1] || photos[photos.length - 1]
$: nextPhoto = photos[currentIndex + 1] || photos[0]
/*
** Go to a photo
** Navigate to a photo
*/
const goToPrev = () => {
// Go to specific photo on Init
if (init) {
currentIndex = photos.findIndex(photo => photo.slug === init.params.photo)
}
// Send current photo to event
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
}
const goToNext = () => {
} else if (direction === 'next') {
currentIndex++
currentIndex = (currentIndex >= photos.length) ? 0 : currentIndex
}
// Send current photo to event
sendCurrentPhoto({ init: false })
}
/*
** Drag and touch navigation
@@ -51,25 +74,19 @@
const swipeEnd = event => {
touchEndX = event.changedTouches[0].screenX
touchEndY = event.changedTouches[0].screenY
if (touchEndX <= touchStartX) goToNext()
if (touchEndX >= touchStartX) goToPrev()
if (touchEndX <= touchStartX) goToPhoto('prev')
if (touchEndX >= touchStartX) goToPhoto('next')
}
// Keyboard navigation
const keyboardNav = event => {
const keyCode = event.keyCode
switch (event.keyCode) {
case 37: case 80: case 74:
goToPrev()
break
case 39: case 78: case 75:
goToNext()
break
// case 27: case 67:
// document.getElementById('photo_close').click()
// break
default: break
/*
** Keyboard navigation
*/
const keyboardNav = event => {
if ([37,80,74].includes(event.keyCode)) {
goToPhoto('prev')
} else if ([39,78,75].includes(event.keyCode)) {
goToPhoto('next')
}
}
@@ -90,22 +107,25 @@
photoToHover = (photoActive.nextSibling != null) ? photoActive.nextSibling : photoActive.parentNode.firstChild
}
switch (event.type) {
case 'mouseenter':
// Toggle class and focus of the button
if (event.type === 'mouseenter') {
photoToHover.classList.add('hover')
button.focus()
break
case 'mouseleave':
} else if (event.type === 'mouseleave') {
photoToHover.classList.remove('hover')
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?
data-aos="{
(photo === prevPhoto) ? 'carousel-prev' :
@@ -117,7 +137,10 @@
<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="gallery">
<div class="gallery__images">
@@ -137,14 +160,20 @@
</div>
<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">
<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" 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">
<IconArrow direction="right" color="#ff6c89" class="icon" />
<IconArrow direction="right" color="#fff" class="icon" hidden="true" />

View File

@@ -9,13 +9,14 @@
// Define either to preload data or use the store
let preloaded
currentLocation.subscribe(store => preloaded = store ? store : undefined)
currentPhotos.subscribe(store => preloaded = store ? store : undefined)
// Preload data
export async function preload (page, session) {
// Load the photos if not loaded
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()
return {
photos: photos.data
@@ -23,14 +24,17 @@
}
// Use the store otherwise
else return {
photos: preloaded.photos
photos: preloaded
}
}
</script>
<script>
import { onMount } from 'svelte'
import { onMount, createEventDispatcher } from 'svelte'
import { goto } from '@sapper/app'
import * as fn from '../../../../functions'
const { page } = stores()
const dispatch = createEventDispatcher()
// Dependencies
import dayjs from 'dayjs'
@@ -41,38 +45,80 @@
import IconGlobe from '../../../../atoms/IconGlobe'
import IconCross from '../../../../atoms/IconCross'
import Carousel from '../../../../organisms/Carousel'
import SocialMetas from '../../../../utils/SocialMetas'
// Props and variables
const { page } = stores()
// Props
export let photos
let currentIndex
let indexFormated
let viewerPhotos
// Variables
let windowWidth
let changeWindowWidth
let currentPhoto
// Define current location
const location = (!$currentLocation) ? $locations.find(loc => loc.slug === $page.params.location) : $currentLocation
const locationFull = `${location.name}, ${location.country.name}`
// Update store current location informations
if (!$currentLocation) currentLocation.set(location)
if (!$currentPhotos) currentPhotos.set(photos)
// Define path
const path = `/viewer/${location.country.slug}/${location.slug}/`
// The photo has changed from the carousel
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
*/
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>
<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:window bind:innerWidth={windowWidth} />
@@ -95,5 +141,10 @@
</div>
</div>
<Carousel {photos} viewer={true} />
<Carousel
{photos}
viewer={true}
init={$page}
on:photoChange={photoChanged}
/>
</section>

View File

@@ -45,7 +45,7 @@
// Photo
&__photo {
$duration: 0.9s;
$duration: 1s;
opacity: 0;
overflow: hidden;
@@ -57,7 +57,7 @@
height: 100%;
transform: scale($scale);
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;
@include breakpoint (sm) {

View File

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