diff --git a/src/web/components/BackgroundImage.tsx b/src/web/components/BackgroundImage.tsx index ce951aa..a6f3cdf 100644 --- a/src/web/components/BackgroundImage.tsx +++ b/src/web/components/BackgroundImage.tsx @@ -4,11 +4,13 @@ import useAsyncEffect from 'use-async-effect'; import InspiratContext from 'web/contexts/Inspirat'; import { + BACKGROUND_ANIMATION_INITIAL_SCALE, BACKGROUND_TRANSITION_DURATION, BACKGROUND_TRANSITION_FUNCTION, BACKGROUND_RULE_OVERRIDES } from 'web/etc/constants'; -import { preloadImage } from 'web/lib/utils'; +import log from 'web/lib/log'; +// import { preloadImage } from 'web/lib/utils'; import classes, { keyframes } from './BackgroundImage.css'; @@ -61,28 +63,35 @@ export default function BackgroundImage(props: BackgroundImageProps) { const photoUrls = buildPhotoUrls(photo); - void Promise.race([ - preloadImage(photoUrls.lowQuality), - preloadImage(photoUrls.highQuality) - ]).then(() => { - if (!isMounted()) return; - setAnyImageReady(true); - setAnimationName(keyframes.zoomOut); - setStyleOverrides(BACKGROUND_RULE_OVERRIDES[photo.id] ?? {}); - }); + // void Promise.race([ + // preloadImage(photoUrls.lowQuality), + // preloadImage(photoUrls.highQuality) + // ]).then(() => { + // if (!isMounted()) return; + // setAnyImageReady(true); + // setAnimationName(keyframes.zoomOut); + // setStyleOverrides(BACKGROUND_RULE_OVERRIDES[photo.id] ?? {}); + // }); + + // TODO: Testing out not waiting to preload images. + setAnyImageReady(true); + setAnimationName(keyframes.zoomOut); + setStyleOverrides(BACKGROUND_RULE_OVERRIDES[photo.id] ?? {}); setLowQualityUrl(photoUrls.lowQuality); setFullQualityUrl(photoUrls.highQuality); }, () => { // DO NOT CLEAR THIS, IT RESETS PHOTO ZOOM AT THE START OF A TRANSITION. // setAnimationName('none'); - }, [photo?.id, isActive]); + }, [photo?.id]); const srcSet = lowQualityUrl && fullQualityUrl ? [ `${lowQualityUrl} ${Math.round(window.screen.width / 2)}w`, - fullQualityUrl + `${fullQualityUrl} ${Math.round(window.screen.width * 2 * BACKGROUND_ANIMATION_INITIAL_SCALE)}w` ].join(', ') : undefined; + // if (srcSet) log.debug('srcSet', srcSet); + return (
background diff --git a/src/web/components/Greeting.css.ts b/src/web/components/Greeting.css.ts index 4d994f5..e608d99 100644 --- a/src/web/components/Greeting.css.ts +++ b/src/web/components/Greeting.css.ts @@ -34,7 +34,8 @@ const classes = { borderRadius: '24px', backgroundColor: 'rgba(80, 80, 80, 0.12)', backdropFilter: 'blur(12px)', - boxShadow: '0px 0px 24px rgba(0, 0, 0, 0.12)' + boxShadow: '0px 0px 24px rgba(0, 0, 0, 0.12)', + userSelect: 'none' }) }; diff --git a/src/web/components/dev-tools/DevTools.tsx b/src/web/components/dev-tools/DevTools.tsx index 23ebb65..3ad7c2a 100644 --- a/src/web/components/dev-tools/DevTools.tsx +++ b/src/web/components/dev-tools/DevTools.tsx @@ -94,18 +94,12 @@ export const DevTools = () => { getCurrentPhotoFromCollection({ offset: dayOffset + 1 }).then(photo => { if (!photo) return; const { lowQuality, highQuality } = buildPhotoUrls(photo); - return Promise.all([ - preloadImage(lowQuality), - preloadImage(highQuality) - ]); + return Promise.all([preloadImage(lowQuality), preloadImage(highQuality)]); }), getCurrentPhotoFromCollection({ offset: dayOffset - 1 }).then(photo => { if (!photo) return; const { lowQuality, highQuality } = buildPhotoUrls(photo); - return Promise.all([ - preloadImage(lowQuality), - preloadImage(highQuality) - ]); + return Promise.all([preloadImage(lowQuality), preloadImage(highQuality)]); }) ]); }, [showDevTools, dayOffset]); diff --git a/src/web/contexts/Inspirat.tsx b/src/web/contexts/Inspirat.tsx index 848da35..521d7bd 100644 --- a/src/web/contexts/Inspirat.tsx +++ b/src/web/contexts/Inspirat.tsx @@ -188,7 +188,11 @@ export function InspiratProvider(props: React.PropsWithChildren) { // This is where IMGIX configuration for low and high quality versions of // photos is defined. return buildPhotoUrlSrcSet(photo.urls.full, { - // blend: 'FA653D80', + q: 100, + w: Math.round(window.screen.width / 2), + h: Math.round(window.screen.height / 2) + // Adds a color overlay. Can be useful for debugging. + // blend: 'FA653D', // blendMode: 'overlay' }, { w: Math.round(window.screen.width * 2 * BACKGROUND_ANIMATION_INITIAL_SCALE), diff --git a/src/web/lib/utils.ts b/src/web/lib/utils.ts index 6c24ea4..cf10a3b 100644 --- a/src/web/lib/utils.ts +++ b/src/web/lib/utils.ts @@ -167,8 +167,12 @@ export function onClickAndHold(interval: number, cb: (e: React.MouseEvent | Reac return handler; } +type PreloadImageCacheValue = { + promise: Promise; + state: 'LOADING' | 'SUCCESS' | 'ERROR'; +}; -const preloadImageCache = new Map(); +const preloadImageCache = new Map(); /** @@ -177,26 +181,28 @@ const preloadImageCache = new Map(); */ export async function preloadImage(imgUrl: string) { if (!preloadImageCache.has(imgUrl)) { - preloadImageCache.set(imgUrl, 'LOADING'); - } - - return new Promise((resolve, reject) => { - const img = new Image(); - - img.addEventListener('load', () => { - preloadImageCache.set(imgUrl, 'SUCCESS'); - resolve(imgUrl); + const imagePromise = new Promise((resolve, reject) => { + const img = new Image(); + + img.addEventListener('load', () => { + preloadImageCache.set(imgUrl, { promise: imagePromise, state: 'SUCCESS' }); + resolve(imgUrl); + }); + + img.addEventListener('error', event => { + preloadImageCache.set(imgUrl, { promise: imagePromise, state: 'ERROR' }); + const message = event.error?.message ?? 'Unknown Error'; + reject(new Error(`[preloadImage] Failed to load image: ${message}`, { cause: event.error })); + }); + + // N.B. Setting this property will cause the browser to fetch the image. + img.src = imgUrl; }); - img.addEventListener('error', event => { - preloadImageCache.set(imgUrl, 'ERROR'); - const message = event.error?.message ?? 'Unknown Error'; - reject(new Error(`[preloadImage] Failed to load image: ${message}`, { cause: event.error })); - }); + preloadImageCache.set(imgUrl, { promise: imagePromise, state: 'LOADING' }); + } - // N.B. Setting this property will cause the browser to fetch the image. - img.src = imgUrl; - }); + return preloadImageCache.get(imgUrl)?.promise; } /** @@ -204,8 +210,8 @@ export async function preloadImage(imgUrl: string) { * currently preloading. */ preloadImage.isLoadingImages = () => { - const states = new Set(preloadImageCache.values()); - return states.has('LOADING'); + const states = [...preloadImageCache.values()].map(cacheValue => cacheValue.state); + return states.includes('LOADING'); }; /** @@ -293,11 +299,6 @@ export function buildPhotoUrlSrcSet(url: string, lqOptions ={}, fullOptions = {} return { lowQuality: updateImgixQueryParams(url, { q: QUALITY_LQIP, - w: Math.round(window.screen.width / 2), - h: Math.round(window.screen.height / 2), - // Adds a color overlay. Can be useful for debugging. - // blend: 'FA653D', - // blendMode: 'overlay', ...lqOptions }), highQuality: updateImgixQueryParams(url, {