more gallery navigation options

This commit is contained in:
saiminh 2023-10-19 17:04:25 +02:00
parent 10560eb69f
commit 4beb82368e
3 changed files with 401 additions and 51 deletions

View file

@ -1,40 +1,46 @@
<script lang="ts"> <script lang="ts">
import { onMount } from 'svelte'; import { onMount } from 'svelte';
import { gsap } from 'gsap'; import { gsap } from 'gsap';
import { ScrollTrigger } from 'gsap/dist/ScrollTrigger'; import ScrollToPlugin from 'gsap/dist/ScrollToPlugin';
import { CldImage } from 'svelte-cloudinary'; import { CldImage } from 'svelte-cloudinary';
export let data; export let data;
gsap.registerPlugin(ScrollTrigger);
let visible = false; let visible = false;
onMount(() => { onMount(() => {
gsap.registerPlugin(ScrollToPlugin);
visible = true; visible = true;
let is_landscape = window.matchMedia('(orientation:landscape)').matches
window.addEventListener('resize', () => {
is_landscape = window.matchMedia('(orientation:landscape)').matches
})
if (document.querySelector('.workclone')) { if (document.querySelector('.workclone')) {
document.querySelector('.workclone')?.remove(); document.querySelector('.workclone')?.remove();
} }
setTimeout( () => { setTimeout( () => {
if (document.querySelector('.gallery img')) { document.querySelectorAll('.gallery img')?.forEach( image => {
document.querySelectorAll('.gallery img').forEach(image => { const img = image as HTMLImageElement;
let img = image as HTMLImageElement; if (!img.complete) {
if (!img.complete) { img.classList.add('imageIsLoading');
img.classList.add('imageIsLoading'); img.onload = () => {
img.onload = () => { gsap.fromTo(img, {
gsap.fromTo(img, { autoAlpha: 0,
autoAlpha: 0, scale: 1.2,
scale: 1.2, }, {
}, { autoAlpha: 1,
autoAlpha: 1, scale: 1,
scale: 1, duration: 1,
duration: 1, ease: 'power4.out',
ease: 'power4.out', })
})
}
} }
}) }
} })
}, 10) }, 10)
gsap.to('.logo-wrapper', { gsap.to('.logo-wrapper', {
@ -55,36 +61,60 @@
}) })
const gallery = document.querySelector('.gallery') as HTMLElement; const gallery = document.querySelector('.gallery') as HTMLElement;
let startX: number; let isAnimating = false;
let scrollLeft: number; const galleryClickHandler = (e: MouseEvent) => {
let isDown = false; if (isAnimating) return;
const allImgs = gallery.querySelectorAll('figure');
const scrollX = gallery.scrollLeft;
const imgWidth = allImgs[0].offsetWidth || 0;
const imgCount = allImgs.length;
const galleryOuterWidth = (document.querySelector('.gallery-wrapper') as HTMLElement)?.offsetWidth || 0;
const galleryOverlap = imgWidth - galleryOuterWidth % imgWidth;
const maxScrollX = imgWidth * (imgCount - 1);
const imgIndex = scrollX < maxScrollX ? Math.round(scrollX / imgWidth) : imgCount - 1;
let newScrollPos;
if (imgIndex < imgCount - 2) {
newScrollPos = scrollX + imgWidth;
}
else if ( imgIndex === imgCount - 2){
newScrollPos = is_landscape ? scrollX + galleryOverlap * .8 : scrollX + galleryOverlap * .85 ;
}
else {
newScrollPos = 0;
}
gsap.to(gallery, {
scrollTo: { x: newScrollPos , autoKill: false },
duration: .75,
ease: 'power4.out',
onStart: () => { isAnimating = true },
onComplete: () => { isAnimating = false },
})
}
const hoverElement = document.createElement('div');
hoverElement.classList.add('hoverElement');
document.querySelector('.gallery-wrapper')?.appendChild(hoverElement);
const galleryHoverHandler = (e: MouseEvent) => {
gsap.to(hoverElement, { x: e.clientX, y: e.clientY + 25, rotateX: 10, rotateZ: -5, autoAlpha: 1, duration: 0.3, overwrite: true })
}
const galleryLeaveHandler = (e: MouseEvent) => {
gsap.to(hoverElement, { autoAlpha: 0, duration: 0.3, overwrite: true })
}
if (gallery) { if (gallery) {
gallery.addEventListener('mousedown', (e) => { gallery.addEventListener('click', galleryClickHandler )
e.preventDefault(); gallery.addEventListener('mousemove', galleryHoverHandler )
gallery.style.scrollSnapType = 'none'; gallery.addEventListener('mouseleave', galleryLeaveHandler )
gallery.style.scrollBehavior = 'auto';
isDown = true;
startX = e.pageX;
scrollLeft = gallery.scrollLeft;
});
gallery.addEventListener('mouseleave', () => {
isDown = false;
});
gallery.addEventListener('mouseup', () => {
isDown = false;
});
gallery.addEventListener('mousemove', (e) => {
if (!isDown) return;
e.preventDefault();
const x = e.pageX;
const walk = (x - startX) * 4; //scroll-fast
gallery.scrollLeft = scrollLeft - walk;
});
} }
return () => { return () => {
gsap.killTweensOf('.logo-wrapper, .work, .gallery-wrapper'); gsap.killTweensOf('.logo-wrapper, .work, .gallery-wrapper')
gallery.removeEventListener('click', galleryClickHandler )
gallery.removeEventListener('mousemove', galleryHoverHandler )
gallery.removeEventListener('mouseleave', galleryLeaveHandler )
} }
}) })
@ -286,6 +316,7 @@
// opacity: 0.75; // opacity: 0.75;
perspective: 250px; perspective: 250px;
perspective-origin: center bottom; perspective-origin: center bottom;
@media screen and (min-width: 768px) { @media screen and (min-width: 768px) {
margin-bottom: -80px; margin-bottom: -80px;
top: -80px; top: -80px;
@ -299,12 +330,11 @@
position: relative; position: relative;
display: flex; display: flex;
scroll-behavior: smooth; scroll-behavior: smooth;
gap: 1px; gap: 0;
overflow-x: scroll; overflow-x: scroll;
scroll-snap-type: x mandatory; scroll-snap-type: x mandatory;
width: 115vw; width: 115vw;
left: -4vw; left: -4vw;
cursor: grab;
transform: rotate3d(-2, 0, 1, -10deg); transform: rotate3d(-2, 0, 1, -10deg);
-ms-overflow-style: none; /* IE and Edge */ -ms-overflow-style: none; /* IE and Edge */
scrollbar-width: none; scrollbar-width: none;
@ -314,7 +344,6 @@
@media screen and (min-width: 768px) { @media screen and (min-width: 768px) {
width: 110vw; width: 110vw;
left: -3vw; left: -3vw;
transform: rotate3d(-2, 0, 1, -10deg);
} }
} }
figure { figure {
@ -323,13 +352,13 @@
overflow: hidden; overflow: hidden;
justify-content: center; justify-content: center;
scroll-snap-align: start; scroll-snap-align: start;
padding: 0 1px;
flex: 1 0 85%; flex: 1 0 85%;
background-color: var(--color-highlight);
height: 100%; height: 100%;
aspect-ratio: 140/84; aspect-ratio: 140/84;
background-color: var(--color-text);
width: auto; width: auto;
margin: 0; margin: 0;
@media screen and (min-width: 768px) { @media screen and (min-width: 768px) {
flex: 1 0 66.666%; flex: 1 0 66.666%;
} }
@ -348,5 +377,30 @@
visibility: hidden; visibility: hidden;
opacity: 0; opacity: 0;
} }
:global(.hoverElement) {
position: fixed;
top: 0;
left: 0;
width: 50px;
height: 50px;
background-color: var(--color-bg);
z-index: 100;
opacity: 0;
visibility: hidden;
box-shadow: -4px 4px 8px rgba(0,0,0,.2);
pointer-events: none;
@media screen and (pointer: coarse) {
display: none;
}
&:before {
content: url('/moveright.svg');
font-size: 33px;
line-height: 50px;
text-align: center;
display: block;
height: 50px;
width: 50px;
}
}
</style> </style>

292
static/moveright.ai Normal file

File diff suppressed because one or more lines are too long

4
static/moveright.svg Normal file
View file

@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 40 50" width="40" height="50" style="enable-background:new 0 0 40 50" xml:space="preserve"><style type="text/css">
.st0{fill:rgba(255,255,255,0.8)}
.st1{fill:none;stroke:rgba(255,255,255,0.8);stroke-width:3;stroke-miterlimit:10;}
</style><line class="st1" x1="5.3" y1="25" x2="31.4" y2="25"/><polygon class="st0" points="21.4,37.3 19.4,35.1 30.2,25 19.4,14.9 21.4,12.7 34.6,25"/></svg>

After

Width:  |  Height:  |  Size: 442 B