Create a functioning carousel
Thanks a million to Grafikart!
This commit is contained in:
@@ -2,15 +2,75 @@
|
|||||||
import { onMount } from 'svelte'
|
import { onMount } from 'svelte'
|
||||||
import * as fn from '../functions'
|
import * as fn from '../functions'
|
||||||
|
|
||||||
|
// Dependencies
|
||||||
|
import dayjs from 'dayjs'
|
||||||
|
import advancedFormat from 'dayjs/plugin/advancedFormat'
|
||||||
|
dayjs.extend(advancedFormat)
|
||||||
|
|
||||||
// Components
|
// Components
|
||||||
import IconArrow from '../atoms/IconArrow'
|
import IconArrow from '../atoms/IconArrow'
|
||||||
|
|
||||||
// Props and variables
|
// Props
|
||||||
export let viewer = false
|
export let viewer = false
|
||||||
export let photos = []
|
export let photos = []
|
||||||
|
|
||||||
|
// Variables
|
||||||
let hover
|
let hover
|
||||||
let photoPrev
|
let currentIndex = 1
|
||||||
let photoNext
|
$: currentPhoto = photos[currentIndex] || null
|
||||||
|
$: prevPhoto = photos[currentIndex - 1] || photos[photos.length - 1]
|
||||||
|
$: nextPhoto = photos[currentIndex + 1] || photos[0]
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Go to a photo
|
||||||
|
*/
|
||||||
|
const goToPrev = () => {
|
||||||
|
currentIndex--
|
||||||
|
currentIndex = (currentIndex < 0) ? photos.length - 1 : currentIndex
|
||||||
|
}
|
||||||
|
const goToNext = () => {
|
||||||
|
currentIndex++
|
||||||
|
currentIndex = (currentIndex >= photos.length) ? 0 : currentIndex
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Drag and touch navigation
|
||||||
|
*/
|
||||||
|
let touchStartX = 0
|
||||||
|
let touchStartY = 0
|
||||||
|
let touchEndX = 0
|
||||||
|
let touchEndY = 0
|
||||||
|
|
||||||
|
const swipeStart = event => {
|
||||||
|
touchStartX = event.changedTouches[0].screenX
|
||||||
|
touchStartY = event.changedTouches[0].screenY
|
||||||
|
}
|
||||||
|
const swipeEnd = event => {
|
||||||
|
touchEndX = event.changedTouches[0].screenX
|
||||||
|
touchEndY = event.changedTouches[0].screenY
|
||||||
|
if (touchEndX <= touchStartX) goToNext()
|
||||||
|
if (touchEndX >= touchStartX) goToPrev()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -19,10 +79,15 @@
|
|||||||
onMount(() => {
|
onMount(() => {
|
||||||
// Hover function
|
// Hover function
|
||||||
hover = event => {
|
hover = event => {
|
||||||
const to = event.currentTarget.dataset.to
|
|
||||||
const button = event.currentTarget.querySelector('button')
|
const button = event.currentTarget.querySelector('button')
|
||||||
const photoActive = document.querySelector('.gallery .active')
|
const photoActive = document.querySelector('.gallery__images .active')
|
||||||
const photoToHover = (to === 'prev') ? photoActive.previousSibling : photoActive.nextSibling
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
switch (event.type) {
|
switch (event.type) {
|
||||||
case 'mouseenter':
|
case 'mouseenter':
|
||||||
@@ -36,29 +101,30 @@
|
|||||||
default: break
|
default: break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Go to previous photo
|
|
||||||
photoPrev = () => {
|
|
||||||
console.log('previous photo')
|
|
||||||
}
|
|
||||||
|
|
||||||
// Go to next photo
|
|
||||||
photoNext = () => {
|
|
||||||
console.log('next photo')
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
/*
|
||||||
|
REVEAL ANIMATION ON CAROUSEL:
|
||||||
|
Fix the CSS priority? Cancel the transition delay and duration?
|
||||||
|
data-aos="{
|
||||||
|
(photo === prevPhoto) ? 'carousel-prev' :
|
||||||
|
(photo === currentPhoto) ? 'carousel-active' :
|
||||||
|
(photo === nextPhoto) ? 'carousel-next' : ''
|
||||||
|
}
|
||||||
|
*/
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="carousel">
|
<svelte:window on:keydown={keyboardNav} />
|
||||||
|
|
||||||
|
<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">
|
||||||
{#each photos as photo, index}
|
{#each photos as photo, index}
|
||||||
<picture class="gallery__images--photo"
|
<picture class="gallery__images--photo"
|
||||||
class:prev={index === 0}
|
class:prev={photo === prevPhoto}
|
||||||
class:active={index === 1}
|
class:active={photo === currentPhoto}
|
||||||
class:next={index === 2}
|
class:next={photo === nextPhoto}
|
||||||
>
|
>
|
||||||
<source media="(min-width: 968px)" srcset={fn.getThumbnail(photo.image.private_hash, 1400)}>
|
<source media="(min-width: 968px)" srcset={fn.getThumbnail(photo.image.private_hash, 1400)}>
|
||||||
<source media="(min-width: 800px)" srcset={fn.getThumbnail(photo.image.private_hash, 900)}>
|
<source media="(min-width: 800px)" srcset={fn.getThumbnail(photo.image.private_hash, 900)}>
|
||||||
@@ -70,14 +136,14 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="carousel__controls">
|
<div class="carousel__controls">
|
||||||
<div class="carousel__controls--area prev" data-to="prev" on:mouseenter={hover} on:mouseleave={hover} on:click={photoPrev}>
|
<div class="carousel__controls--area prev" data-to="prev" on:mouseenter={hover} on:mouseleave={hover} on:click={goToPrev}>
|
||||||
<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" className="icon" />
|
<IconArrow direction="left" color="#ff6c89" className="icon" />
|
||||||
<IconArrow direction="left" color="#fff" className="icon" hidden="true" />
|
<IconArrow direction="left" color="#fff" className="icon" hidden="true" />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="carousel__controls--area next" data-to="next" on:mouseenter={hover} on:mouseleave={hover} on:click={photoNext}>
|
<div class="carousel__controls--area next" data-to="next" on:mouseenter={hover} on:mouseleave={hover} on:click={goToNext}>
|
||||||
<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" className="icon" />
|
<IconArrow direction="right" color="#ff6c89" className="icon" />
|
||||||
<IconArrow direction="right" color="#fff" className="icon" hidden="true" />
|
<IconArrow direction="right" color="#fff" className="icon" hidden="true" />
|
||||||
@@ -86,18 +152,18 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{#if viewer}
|
{#if viewer}
|
||||||
<div class="carousel__number">05</div>
|
<div class="carousel__number">{currentIndex < 10 ? 0 : ''}{currentIndex + 1}</div>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="carousel__information">
|
<div class="carousel__information">
|
||||||
<div class="carousel__information--location style-location">
|
<div class="carousel__information--location style-location">
|
||||||
<p class="street">Street Name, Suburb</p>
|
<p class="street">{currentPhoto.name}</p>
|
||||||
<p class="style-caps state">Region, Country</p>
|
<p class="style-caps state">{currentPhoto.location.region}, {currentPhoto.location.country.name}</p>
|
||||||
</div>
|
</div>
|
||||||
<!-- Duplicate for animation? -->
|
<!-- Duplicate for animation? -->
|
||||||
{#if viewer}
|
{#if viewer}
|
||||||
<p class="carousel__information--date">Apr 6th, 2019</p>
|
<p class="carousel__information--date">{dayjs(currentPhoto.created_on).format('MMM Do, YYYY')}</p>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -122,4 +188,3 @@
|
|||||||
</ol>
|
</ol>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
// Carousel
|
// Carousel
|
||||||
.carousel {
|
.carousel {
|
||||||
background-color: $color-primary;
|
background-color: $color-primary;
|
||||||
transform: translateZ(0);
|
|
||||||
|
|
||||||
.wrap {
|
.wrap {
|
||||||
max-width: 1280px;
|
max-width: 1280px;
|
||||||
@@ -12,7 +11,10 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Gallery
|
|
||||||
|
/*
|
||||||
|
** Gallery
|
||||||
|
*/
|
||||||
.gallery {
|
.gallery {
|
||||||
position: relative;
|
position: relative;
|
||||||
z-index: 2;
|
z-index: 2;
|
||||||
@@ -25,96 +27,122 @@
|
|||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Variables
|
||||||
|
$scale: 0.9;
|
||||||
|
$angle: 1deg;
|
||||||
|
$angleHover: 1.25deg;
|
||||||
|
$distance: 9%;
|
||||||
|
$distanceHover: 10%;
|
||||||
|
$radius: 8px;
|
||||||
|
|
||||||
// Images
|
// Images
|
||||||
&__images {
|
&__images {
|
||||||
position: relative;
|
position: relative;
|
||||||
height: 0;
|
height: 0;
|
||||||
padding-bottom: calc(100% / 1.5); // Ratio 3.2
|
padding-bottom: calc(100% / 1.5); // Ratio 3.2
|
||||||
|
user-select: none;
|
||||||
|
|
||||||
// Photo
|
// Photo
|
||||||
&--photo {
|
&--photo {
|
||||||
|
opacity: 0;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
display: block;
|
z-index: -1;
|
||||||
background-color: $color-primary;
|
top: 0;
|
||||||
box-shadow: 0 15px 60px rgba(#000, 0.3);
|
left: 0;
|
||||||
transition: transform 600ms $ease-quart;
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
transform: scale($scale);
|
||||||
|
box-shadow: 0 pxVW(15) pxVW(60) rgba(#000, 0.3);
|
||||||
|
transition: transform 600ms $ease-quart, opacity 600ms $ease-quart;
|
||||||
|
will-change: transform, opacity;
|
||||||
|
|
||||||
@include breakpoint (sm) {
|
@include breakpoint (sm) {
|
||||||
border-radius: 8px;
|
border-radius: $radius;
|
||||||
}
|
}
|
||||||
|
|
||||||
img {
|
img {
|
||||||
|
opacity: 0;
|
||||||
|
position: relative;
|
||||||
|
z-index: 1;
|
||||||
display: block;
|
display: block;
|
||||||
height: auto;
|
|
||||||
width: 100%;
|
width: 100%;
|
||||||
opacity: 0.6;
|
height: auto;
|
||||||
transition: opacity 600ms $ease-quart;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Temp hiding
|
// Overlay
|
||||||
&:nth-of-type(4), &:nth-of-type(5) {
|
&:before {
|
||||||
display: none;
|
opacity: 0;
|
||||||
|
content: "";
|
||||||
|
display: block;
|
||||||
|
position: absolute;
|
||||||
|
z-index: 2;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
background-color: $color-primary;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Active photo
|
// Active photo
|
||||||
.active {
|
.active {
|
||||||
|
opacity: 1;
|
||||||
z-index: 10;
|
z-index: 10;
|
||||||
top: 50%;
|
transform: scale(1);
|
||||||
left: 50%;
|
|
||||||
width: 100%;
|
|
||||||
transform: translate3d(-50%, -50%, 0);
|
|
||||||
|
|
||||||
img {
|
img {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Variables
|
// Previous & next photo
|
||||||
$hoverAngle: 1.25deg;
|
.prev, .next {
|
||||||
$hoverDistance: 7%;
|
opacity: 1;
|
||||||
|
|
||||||
|
img {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:before {
|
||||||
|
opacity: 0.4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Previous photo
|
// Previous photo
|
||||||
.prev {
|
.prev {
|
||||||
|
z-index: 8;
|
||||||
|
|
||||||
@include breakpoint (sm) {
|
@include breakpoint (sm) {
|
||||||
top: 50%;
|
transform: translate(-$distance, -1%) rotate(-$angle) scale($scale);
|
||||||
transform: translate3d(-6%, -50%, 0) rotate(-1deg);
|
|
||||||
width: 85%;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hover
|
// Hover
|
||||||
&.hover {
|
&.hover {
|
||||||
transform: translate3d(-$hoverDistance, -50%, 0) rotate(-$hoverAngle);
|
transform: translate(-$distanceHover, -1%) rotate(-$angleHover) scale($scale);
|
||||||
|
|
||||||
img {
|
|
||||||
opacity: 0.8;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Next photo
|
// Next photo
|
||||||
.next {
|
.next {
|
||||||
|
z-index: 9;
|
||||||
|
|
||||||
@include breakpoint (sm) {
|
@include breakpoint (sm) {
|
||||||
top: 50%;
|
transform: translate($distance, -1%) rotate($angle) scale($scale);
|
||||||
right: 0;
|
|
||||||
transform: translate3d(6%, -50%, 0) rotate(1deg);
|
|
||||||
width: 85%;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hover
|
// Hover
|
||||||
&.hover {
|
&.hover {
|
||||||
transform: translate3d($hoverDistance, -50%, 0) rotate($hoverAngle);
|
transform: translate($distanceHover, -1%) rotate($angleHover) scale($scale);
|
||||||
|
|
||||||
img {
|
|
||||||
opacity: 0.8;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Controls (arrows)
|
|
||||||
|
/*
|
||||||
|
** Controls (arrows)
|
||||||
|
*/
|
||||||
&__controls {
|
&__controls {
|
||||||
display: none;
|
display: none;
|
||||||
|
|
||||||
@@ -237,8 +265,6 @@
|
|||||||
&__image {
|
&__image {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
// display: flex;
|
|
||||||
// justify-content: center;
|
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
|
|
||||||
img {
|
img {
|
||||||
|
|||||||
@@ -48,15 +48,13 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Gallery
|
// Photos
|
||||||
.gallery {
|
.gallery__images--photo {
|
||||||
&__images {
|
&:before {
|
||||||
&--photo {
|
|
||||||
background-color: $color-secondary;
|
background-color: $color-secondary;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -13,7 +13,10 @@
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Top part
|
|
||||||
|
/*
|
||||||
|
** Top part
|
||||||
|
*/
|
||||||
&__top {
|
&__top {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
z-index: 10;
|
z-index: 10;
|
||||||
@@ -57,13 +60,16 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Carousel
|
|
||||||
|
/*
|
||||||
|
** Carousel
|
||||||
|
*/
|
||||||
.carousel {
|
.carousel {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 50%;
|
top: 50%;
|
||||||
left: 0;
|
left: 0;
|
||||||
right: 0;
|
right: 0;
|
||||||
transform: translate3d(0, -37%, 0);
|
transform: translateY(-37%);
|
||||||
margin: 0;
|
margin: 0;
|
||||||
|
|
||||||
@include breakpoint (sm) {
|
@include breakpoint (sm) {
|
||||||
@@ -83,7 +89,7 @@
|
|||||||
// Specific box shadow for images
|
// Specific box shadow for images
|
||||||
&__images {
|
&__images {
|
||||||
&--photo {
|
&--photo {
|
||||||
box-shadow: 0 16px pxVW(40) rgba(#2E025C, 0.4);
|
box-shadow: 0 pxVW(16px) pxVW(40) rgba(#2E025C, 0.4);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user