Merged in fix/sw-1127-image-fixes (pull request #1123)
Fix/sw-1127 image fixes for lightbox * fix(SW-1127): move back to top button behind lightbox * fix(SW-1127): don't loop lightbox thumbnails * fix(SW-1127): nicer animation in the gallery This both fixes a bug in the gallery where the animation in the carousel didn't work so good and also animates the images different directions depending on if the user go left or right. Approved-by: Matilda Landström
This commit is contained in:
@@ -123,6 +123,7 @@
|
|||||||
--booking-widget-z-index: 10;
|
--booking-widget-z-index: 10;
|
||||||
--booking-widget-open-z-index: 100;
|
--booking-widget-open-z-index: 100;
|
||||||
--dialog-z-index: 9;
|
--dialog-z-index: 9;
|
||||||
|
--back-to-top-button: 80;
|
||||||
--sidepeek-z-index: 100;
|
--sidepeek-z-index: 100;
|
||||||
--lightbox-z-index: 150;
|
--lightbox-z-index: 150;
|
||||||
--default-modal-overlay-z-index: 100;
|
--default-modal-overlay-z-index: 100;
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
"use client"
|
"use client"
|
||||||
import { AnimatePresence, motion } from "framer-motion"
|
import { AnimatePresence, motion } from "framer-motion"
|
||||||
|
import { useState } from "react"
|
||||||
|
|
||||||
import ArrowRightIcon from "@/components/Icons/ArrowRight"
|
import ArrowRightIcon from "@/components/Icons/ArrowRight"
|
||||||
import CloseIcon from "@/components/Icons/Close"
|
import CloseIcon from "@/components/Icons/Close"
|
||||||
@@ -20,10 +21,35 @@ export default function FullView({
|
|||||||
currentIndex,
|
currentIndex,
|
||||||
totalImages,
|
totalImages,
|
||||||
}: FullViewProps) {
|
}: FullViewProps) {
|
||||||
|
const [animateLeft, setAnimateLeft] = useState(true)
|
||||||
|
|
||||||
function handleSwipe(offset: number) {
|
function handleSwipe(offset: number) {
|
||||||
if (offset > 30) onPrev()
|
if (offset > 30) onPrev()
|
||||||
if (offset < -30) onNext()
|
if (offset < -30) onNext()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function handleNext() {
|
||||||
|
setAnimateLeft(true)
|
||||||
|
onNext()
|
||||||
|
}
|
||||||
|
|
||||||
|
function handlePrev() {
|
||||||
|
setAnimateLeft(false)
|
||||||
|
onPrev()
|
||||||
|
}
|
||||||
|
|
||||||
|
const variants = {
|
||||||
|
initial: (animateLeft: boolean) => ({
|
||||||
|
opacity: 0,
|
||||||
|
x: animateLeft ? 300 : -300,
|
||||||
|
}),
|
||||||
|
animate: { opacity: 1, x: 0 },
|
||||||
|
exit: (animateLeft: boolean) => ({
|
||||||
|
opacity: 0,
|
||||||
|
x: animateLeft ? -300 : 300,
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.fullViewContainer}>
|
<div className={styles.fullViewContainer}>
|
||||||
<Button
|
<Button
|
||||||
@@ -48,13 +74,14 @@ export default function FullView({
|
|||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div className={styles.fullViewImageContainer}>
|
<div className={styles.fullViewImageContainer}>
|
||||||
<AnimatePresence initial={false} custom={currentIndex}>
|
<AnimatePresence initial={false} custom={animateLeft}>
|
||||||
<motion.div
|
<motion.div
|
||||||
key={image.imageSizes.medium}
|
key={image.imageSizes.medium}
|
||||||
custom={currentIndex}
|
custom={animateLeft}
|
||||||
initial={{ opacity: 0, x: 300 }}
|
variants={variants}
|
||||||
animate={{ opacity: 1, x: 0 }}
|
initial="initial"
|
||||||
exit={{ opacity: 0, x: -300 }}
|
animate="animate"
|
||||||
|
exit="exit"
|
||||||
transition={{ duration: 0.3 }}
|
transition={{ duration: 0.3 }}
|
||||||
className={styles.fullViewImage}
|
className={styles.fullViewImage}
|
||||||
drag="x"
|
drag="x"
|
||||||
@@ -78,13 +105,13 @@ export default function FullView({
|
|||||||
|
|
||||||
<motion.button
|
<motion.button
|
||||||
className={`${styles.navigationButton} ${styles.fullViewPrevButton}`}
|
className={`${styles.navigationButton} ${styles.fullViewPrevButton}`}
|
||||||
onClick={onPrev}
|
onClick={handlePrev}
|
||||||
>
|
>
|
||||||
<ArrowRightIcon color="burgundy" className={styles.leftTransformIcon} />
|
<ArrowRightIcon color="burgundy" className={styles.leftTransformIcon} />
|
||||||
</motion.button>
|
</motion.button>
|
||||||
<motion.button
|
<motion.button
|
||||||
className={`${styles.navigationButton} ${styles.fullViewNextButton}`}
|
className={`${styles.navigationButton} ${styles.fullViewNextButton}`}
|
||||||
onClick={onNext}
|
onClick={handleNext}
|
||||||
>
|
>
|
||||||
<ArrowRightIcon color="burgundy" />
|
<ArrowRightIcon color="burgundy" />
|
||||||
</motion.button>
|
</motion.button>
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
"use client"
|
"use client"
|
||||||
import { AnimatePresence, motion } from "framer-motion"
|
import { AnimatePresence, motion } from "framer-motion"
|
||||||
|
import { useState } from "react"
|
||||||
import { useIntl } from "react-intl"
|
import { useIntl } from "react-intl"
|
||||||
|
|
||||||
import { ChevronLeftIcon } from "@/components/Icons"
|
import { ChevronLeftIcon } from "@/components/Icons"
|
||||||
@@ -21,12 +22,13 @@ export default function Gallery({
|
|||||||
selectedImage,
|
selectedImage,
|
||||||
}: GalleryProps) {
|
}: GalleryProps) {
|
||||||
const intl = useIntl()
|
const intl = useIntl()
|
||||||
|
const [animateLeft, setAnimateLeft] = useState(true)
|
||||||
const mainImage = selectedImage || images[0]
|
const mainImage = selectedImage || images[0]
|
||||||
const mainImageIndex = images.findIndex((img) => img === mainImage)
|
const mainImageIndex = images.findIndex((img) => img === mainImage)
|
||||||
|
|
||||||
function getThumbImages() {
|
function getThumbImages() {
|
||||||
const thumbs = []
|
const thumbs = []
|
||||||
for (let i = 1; i <= 5; i++) {
|
for (let i = 1; i <= Math.min(5, images.length); i++) {
|
||||||
const index = (mainImageIndex + i) % images.length
|
const index = (mainImageIndex + i) % images.length
|
||||||
thumbs.push(images[index])
|
thumbs.push(images[index])
|
||||||
}
|
}
|
||||||
@@ -34,15 +36,29 @@ export default function Gallery({
|
|||||||
}
|
}
|
||||||
|
|
||||||
function handleNext() {
|
function handleNext() {
|
||||||
|
setAnimateLeft(true)
|
||||||
const nextIndex = (mainImageIndex + 1) % images.length
|
const nextIndex = (mainImageIndex + 1) % images.length
|
||||||
onSelectImage(images[nextIndex])
|
onSelectImage(images[nextIndex])
|
||||||
}
|
}
|
||||||
|
|
||||||
function handlePrev() {
|
function handlePrev() {
|
||||||
|
setAnimateLeft(false)
|
||||||
const prevIndex = (mainImageIndex - 1 + images.length) % images.length
|
const prevIndex = (mainImageIndex - 1 + images.length) % images.length
|
||||||
onSelectImage(images[prevIndex])
|
onSelectImage(images[prevIndex])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const variants = {
|
||||||
|
initial: (animateLeft: boolean) => ({
|
||||||
|
opacity: 0,
|
||||||
|
x: animateLeft ? 300 : -300,
|
||||||
|
}),
|
||||||
|
animate: { opacity: 1, x: 0 },
|
||||||
|
exit: (animateLeft: boolean) => ({
|
||||||
|
opacity: 0,
|
||||||
|
x: animateLeft ? -300 : 300,
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.galleryContainer}>
|
<div className={styles.galleryContainer}>
|
||||||
<Button
|
<Button
|
||||||
@@ -72,13 +88,15 @@ export default function Gallery({
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className={styles.mainImageWrapper}>
|
<div className={styles.mainImageWrapper}>
|
||||||
<AnimatePresence initial={false} mode="wait">
|
<AnimatePresence initial={false} custom={animateLeft}>
|
||||||
<motion.div
|
<motion.div
|
||||||
key={mainImage.imageSizes.medium}
|
key={mainImage.imageSizes.medium}
|
||||||
className={styles.mainImageContainer}
|
className={styles.mainImageContainer}
|
||||||
initial={{ opacity: 0, x: 300 }}
|
custom={animateLeft}
|
||||||
animate={{ opacity: 1, x: 0 }}
|
variants={variants}
|
||||||
exit={{ opacity: 0, x: -300 }}
|
initial="initial"
|
||||||
|
animate="animate"
|
||||||
|
exit="exit"
|
||||||
transition={{ duration: 0.3 }}
|
transition={{ duration: 0.3 }}
|
||||||
>
|
>
|
||||||
<Image
|
<Image
|
||||||
|
|||||||
@@ -108,6 +108,7 @@
|
|||||||
height: 100%;
|
height: 100%;
|
||||||
will-change: transform;
|
will-change: transform;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
position: absolute;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mainImageContainer img,
|
.mainImageContainer img,
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
align-items: flex-end;
|
align-items: flex-end;
|
||||||
position: fixed;
|
position: fixed;
|
||||||
bottom: 20px;
|
bottom: 20px;
|
||||||
z-index: 1000;
|
z-index: var(--back-to-top-button);
|
||||||
background-color: var(--Base-Surface-Primary-light-Normal);
|
background-color: var(--Base-Surface-Primary-light-Normal);
|
||||||
color: var(--Base-Button-Secondary-On-Fill-Normal);
|
color: var(--Base-Button-Secondary-On-Fill-Normal);
|
||||||
border: 2px solid var(--Base-Button-Secondary-On-Fill-Normal);
|
border: 2px solid var(--Base-Button-Secondary-On-Fill-Normal);
|
||||||
|
|||||||
Reference in New Issue
Block a user