How to Implement Product Image Slider?
PreviousHow to Implement the Quickly Add to Cart Feature?NextHow to Create and Integrate a Custom Payment Option View in Checkout?
Last updated
Was this helpful?
Was this helpful?
import { Image } from '@akinon/next/components/image';
import { Link, SwiperSlide, SwiperReact } from '@theme/components';
import clsx from 'clsx';
import { useEffect, useRef, useState } from 'react';
import { useInView } from 'react-intersection-observer';
import { Lazy, Virtual } from 'swiper';
const loadImage = (slide) => {
const image = slide.querySelector('img');
if (image) {
const src = image.getAttribute('data-src');
if (src) {
image.setAttribute('src', src);
image.removeAttribute('data-src');
}
}
};
const handleSlideChange = (swiper, paginationRef) => {
const activeIndex = swiper?.activeIndex;
const bullets = paginationRef?.current?.querySelectorAll(
'.swiper-pagination-rectangle'
);
// Active Slide Show
if (swiper?.slides[activeIndex]) {
loadImage(swiper.slides[activeIndex]);
}
// Pagination Marks
bullets?.forEach((bullet, index) => {
bullet.classList.toggle('!bg-secondary', index === swiper.realIndex);
});
};
const Slide = ({
imagesArray,
swiper,
swiperRef,
setSwiper,
productImage,
paginationRef,
SlideItem
}) => {
useEffect(() => {
const swiperInstance = swiperRef.current?.swiper;
if (swiperInstance) {
// First image is render
loadImage(swiperInstance.slides[0]);
swiperInstance.on('slideChange', () =>
handleSlideChange(swiperInstance, paginationRef)
);
return () => {
swiperInstance.off('slideChange', () =>
handleSlideChange(swiperInstance, paginationRef)
);
};
}
}, [swiperRef, paginationRef]);
return (
<SwiperReact
ref={swiperRef}
slidesPerView={1}
pagination={{ el: '.swiper-pagination', clickable: true }}
preloadImages={false}
lazy={{
enabled: true,
checkInView: true,
loadPrevNext: true,
loadPrevNextAmount: 0,
loadOnTransitionStart: true
}}
onSwiper={setSwiper}
modules={[Virtual, Lazy]}
>
{imagesArray?.map((item, productImageIndex) =>
SlideItem(
productImageIndex === 0 ? productImage : item.image,
productImageIndex
)
)}
<div
className="swiper-pagination flex-row items-center justify-center gap-x-1 z-20 absolute bottom-0 w-full"
ref={paginationRef}
>
{imagesArray?.map((item, productImageIndex) => (
<span
key={productImageIndex}
className={clsx(
productImageIndex === 0 && '!bg-secondary',
'swiper-pagination-rectangle h-1 w-full bg-gray-650'
)}
></span>
))}
</div>
<div className="hidden md:flex absolute inset-0 z-10 justify-between">
{imagesArray.map((image, productImageIndex) => (
<div
key={productImageIndex}
className="w-full"
onMouseMove={() => swiper?.slideTo(productImageIndex)}
onMouseEnter={() => {
paginationRef?.current?.classList.add('flex');
}}
onMouseLeave={() => {
paginationRef?.current?.classList.remove('flex');
}}
></div>
))}
</div>
</SwiperReact>
);
};
const SlideImage = ({ image_url, product_name }) => {
const { ref, inView } = useInView({
triggerOnce: true
});
const default_image = '/default-no-image.png';
image_url = image_url || default_image;
return (
<div ref={ref}>
<Image
fill
loading="lazy"
priority={false}
src={inView ? image_url : '/white-bg.png'}
alt={product_name}
aspectRatio={1}
quality={100}
sizes="
(max-width: 768px) 175px,
(max-width: 1170px) 350px,
500px"
crop="center"
className="flex"
style={{
imageRendering: '-webkit-optimize-contrast'
}}
/>
</div>
);
};
const ProductImageSlider = ({
absolute_url,
multiImages,
productImages,
setSwiper,
productImage,
swiper,
image_url_one,
product_name,
setViewProducts,
product,
index
}) => {
const swiperRef = useRef(null);
const paginationRef = useRef(null);
const [viewed, setViewed] = useState(false);
const { ref, inView } = useInView();
const SlideItem = (image_url: string, productImageIndex: number) => (
<SwiperSlide key={productImageIndex}>
<SlideImage image_url={image_url} product_name={product_name} />
</SwiperSlide>
);
useEffect(() => {
const swiper = swiperRef?.current?.swiper;
// Load Image
swiper?.slides?.forEach(loadImage);
// Change Slide Image
swiper?.on('slideChange', () => handleSlideChange(swiper, paginationRef));
// Pagination referance
if (swiper && swiper?.pagination && swiper?.pagination?.el) {
paginationRef.current = swiper.pagination.el;
}
if (!viewed && inView) {
setViewed(true);
setViewProducts &&
setViewProducts((prev) => [...prev, { ...product, index }]);
}
// Cleanup function
return () => {
if (swiper) {
swiper?.off('slideChange', () =>
handleSlideChange(swiper, paginationRef)
);
}
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [inView, swiper]);
return (
<div ref={ref}>
<Link href={absolute_url}>
{!multiImages ? (
productImages?.length > 1 ? (
<Slide
imagesArray={productImages}
swiper={swiper}
swiperRef={swiperRef}
setSwiper={setSwiper}
productImage={productImage}
paginationRef={paginationRef}
SlideItem={SlideItem}
/>
) : (
<SlideImage image_url={image_url_one} product_name={product_name} />
)
) : multiImages?.length > 1 ? (
<Slide
imagesArray={multiImages}
swiper={swiper}
swiperRef={swiperRef}
setSwiper={setSwiper}
productImage={productImage}
paginationRef={paginationRef}
SlideItem={SlideItem}
/>
) : (
<SlideImage image_url={image_url_one} product_name={product_name} />
)}
</Link>
</div>
);
};
export default ProductImageSlider;'use client';
import 'swiper/swiper-bundle.min.css';
export { Swiper as SwiperReact } from 'swiper/react';'use client';
export { SwiperSlide } from 'swiper/react';// Swiper Components
export * from './swiper-react';
export * from './swiper-slide';