Skip to content

Commit

Permalink
Merge pull request #56 from thomasKn/thomas/fv-261-image-component-to…
Browse files Browse the repository at this point in the history
…-render-a-blur-image-while-image-is

Add sanity blur image preview
  • Loading branch information
thomasKn authored Feb 10, 2024
2 parents bf9c585 + 4a750cf commit dcfcf93
Show file tree
Hide file tree
Showing 3 changed files with 75 additions and 34 deletions.
4 changes: 2 additions & 2 deletions app/components/Banner.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ const BannerMediaOverlay = forwardRef<
return (
<div
aria-hidden
className={cn('absolute inset-0 bg-black', className)}
className={cn('absolute inset-0 z-[2] bg-black', className)}
ref={ref}
style={style}
{...props}
Expand All @@ -79,7 +79,7 @@ const BannerContent = forwardRef<
const cleanContentAlignment = vercelStegaCleanAll(contentAlignment);
return (
<div
className={cn('container relative h-full py-4', className)}
className={cn('container relative z-[3] h-full py-4', className)}
ref={ref}
{...props}
>
Expand Down
91 changes: 63 additions & 28 deletions app/components/sanity/SanityImage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,26 @@ export function SanityImage(props: {
sizes?: null | string;
style?: React.CSSProperties;
}) {
const {className, data, loading, sanityEncodeData, sizes, style} = props;
const env = useEnvironmentVariables();
const {
aspectRatio,
className,
data,
loading,
sanityEncodeData,
sizes,
style,
} = props;

const aspectRatioValues = props.aspectRatio?.split('/');
if (!data) {
return null;
}

if (props.aspectRatio && aspectRatioValues?.length !== 2) {
const aspectRatioValues = aspectRatio?.split('/');

if (aspectRatio && aspectRatioValues?.length !== 2) {
console.warn(
`Invalid aspect ratio: ${props.aspectRatio}. Using the original aspect ratio. The aspect ratio should be in the format "width/height".`,
`Invalid aspect ratio: ${aspectRatio}. Using the original aspect ratio. The aspect ratio should be in the format "width/height".`,
);
}

Expand All @@ -32,10 +44,6 @@ export function SanityImage(props: {
? parseFloat(aspectRatioValues[1])
: undefined;

if (!data) {
return null;
}

const urlBuilder = imageUrlBuilder({
dataset: env?.SANITY_STUDIO_DATASET!,
projectId: env?.SANITY_STUDIO_PROJECT_ID!,
Expand All @@ -47,6 +55,7 @@ export function SanityImage(props: {
})
.auto('format');

const urlPreview = urlBuilder.width(30).url();
const urlDefault = urlBuilder.url();
// Values used for srcset attribute of image tag (in pixels)
const srcSetValues = [
Expand Down Expand Up @@ -79,25 +88,51 @@ export function SanityImage(props: {
.concat(`, ${urlDefault} ${data.width}w`);

return (
<img
alt={data.altText || ''}
className={cn(['object-[var(--focalX)_var(--focalY)]', className])}
// Adding this attribute makes sure the image is always clickable in the Presentation tool
data-sanity={sanityEncodeData}
height={aspectRatioHeight || data.height}
loading={loading}
sizes={sizes || undefined}
src={urlDefault}
srcSet={srcSet}
style={
{
'--focalX': focalCoords.x + '%',
'--focalY': focalCoords.y + '%',
aspectRatio: `${aspectRatioWidth || data.width}/${aspectRatioHeight || data.height}`,
...style,
} as React.CSSProperties
}
width={aspectRatioWidth || data.width}
/>
<span className="relative block overflow-hidden">
<img
alt={data.altText || ''}
className={cn([
'relative z-[1] object-[var(--focalX)_var(--focalY)]',
className,
])}
// Adding this attribute makes sure the image is always clickable in the Presentation tool
data-sanity={sanityEncodeData}
height={aspectRatioHeight || data.height}
loading={loading}
sizes={sizes || undefined}
src={urlDefault}
srcSet={srcSet}
style={
{
'--focalX': focalCoords.x + '%',
'--focalY': focalCoords.y + '%',
aspectRatio: `${aspectRatioWidth || data.width}/${aspectRatioHeight || data.height}`,
...style,
} as React.CSSProperties
}
width={aspectRatioWidth || data.width}
/>
{/* Preview blurry image (30px) that will load before the highres image */}
<img
alt={data.altText || ''}
className={cn([
'absolute inset-0 object-[var(--focalX)_var(--focalY)] blur-2xl transition-opacity',
className,
])}
height={aspectRatioHeight || data.height}
loading="eager"
sizes={sizes || undefined}
src={urlPreview}
srcSet={urlPreview}
style={
{
'--focalX': focalCoords.x + '%',
'--focalY': focalCoords.y + '%',
aspectRatio: `${aspectRatioWidth || data.width}/${aspectRatioHeight || data.height}`,
} as React.CSSProperties
}
width={aspectRatioWidth || data.width}
/>
</span>
);
}
14 changes: 10 additions & 4 deletions app/components/sections/CarouselSection.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import type {TypeFromSelection} from 'groqd';

import Autoplay from 'embla-carousel-autoplay';
import {useMemo} from 'react';
import {useInView} from 'framer-motion';
import {useMemo, useRef} from 'react';

import type {SectionDefaultProps} from '~/lib/type';
import type {CAROUSEL_SECTION_FRAGMENT} from '~/qroq/sections';
Expand Down Expand Up @@ -31,7 +32,8 @@ export function CarouselSection(
slidesPerViewDesktop,
title,
} = data;

const ref = useRef<HTMLDivElement>(null);
const inView = useInView(ref);
const slidesPerView = slidesPerViewDesktop ? 100 / slidesPerViewDesktop : 100;
const plugins = useMemo(() => (autoplay ? [Autoplay()] : []), [autoplay]);
const imageSizes = slidesPerViewDesktop
Expand All @@ -42,7 +44,7 @@ export function CarouselSection(
const isActive = slides?.length! > 1;

return (
<div className="container">
<div className="container" ref={ref}>
<h2>{title}</h2>
<div
style={
Expand All @@ -66,7 +68,11 @@ export function CarouselSection(
className="p-0 md:basis-1/2 md:pl-4 lg:basis-[var(--slidesPerView)]"
key={slide._key}
>
<SanityImage data={slide.image} sizes={imageSizes} />
<SanityImage
data={slide.image}
loading={inView ? 'eager' : 'lazy'}
sizes={imageSizes}
/>
</CarouselItem>
))}
</CarouselContent>
Expand Down

0 comments on commit dcfcf93

Please sign in to comment.