From 05663791c71a8ff0ab676dc3a94b23892705a748 Mon Sep 17 00:00:00 2001 From: Tony Date: Mon, 12 Aug 2024 22:19:54 +0800 Subject: [PATCH] Add a feature to fade in image on fully loaded - Also change module css to normal css for eaiser consume --- example/src/pages/index.tsx | 14 ++++++------ package-lock.json | 1 + package.json | 1 + scripts/copy-css.js | 2 +- src/index.ts | 5 ++++ src/missing-types.d.ts | 5 +--- src/theme/NativeIdealImage.css | 33 +++++++++++++++++++++++++++ src/theme/NativeIdealImage.module.css | 23 ------------------- src/theme/NativeIdealImage.tsx | 31 ++++++++++++++++--------- 9 files changed, 69 insertions(+), 46 deletions(-) create mode 100644 src/theme/NativeIdealImage.css delete mode 100644 src/theme/NativeIdealImage.module.css diff --git a/example/src/pages/index.tsx b/example/src/pages/index.tsx index a584adc..a38e293 100644 --- a/example/src/pages/index.tsx +++ b/example/src/pages/index.tsx @@ -44,19 +44,19 @@ export default function Home(): JSX.Element {
- - - - - - + + + + + +
) } -function Card(props: { name: string; image: LoaderOutput }) { +function Card(props: { name: string; image: LoaderOutput; swapOnLoad?: boolean }) { return (
diff --git a/package-lock.json b/package-lock.json index f5692aa..214f811 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,6 +14,7 @@ "dependencies": { "@docusaurus/core": "^3.5.1", "@docusaurus/types": "^3.5.1", + "clsx": "^2.1.1", "loader-utils": "^3.3.1", "react": "^18.3.1", "react-dom": "^18.3.1", diff --git a/package.json b/package.json index 6c1084d..14fe765 100644 --- a/package.json +++ b/package.json @@ -28,6 +28,7 @@ "dependencies": { "@docusaurus/core": "^3.5.1", "@docusaurus/types": "^3.5.1", + "clsx": "^2.1.1", "loader-utils": "^3.3.1", "react": "^18.3.1", "react-dom": "^18.3.1", diff --git a/scripts/copy-css.js b/scripts/copy-css.js index 290b9bb..8615b8a 100644 --- a/scripts/copy-css.js +++ b/scripts/copy-css.js @@ -1,3 +1,3 @@ import { copyFile } from 'fs/promises' -await copyFile('src/theme/NativeIdealImage.module.css', 'lib/theme/NativeIdealImage.module.css') +await copyFile('src/theme/NativeIdealImage.css', 'lib/theme/NativeIdealImage.css') diff --git a/src/index.ts b/src/index.ts index 05d999c..8270615 100644 --- a/src/index.ts +++ b/src/index.ts @@ -48,6 +48,11 @@ export type LoaderOutput = { export type NativeIdealImageProps = ComponentProps<'img'> & { readonly img: { default: string } | string | LoaderOutput + /** + * Swap (fade in) the actual image after it's fully loaded, + * requires JavaScript to work, so this might cause the image to load a bit slower + * */ + swapOnLoad?: boolean } export default function pluginNativeIdealImage( diff --git a/src/missing-types.d.ts b/src/missing-types.d.ts index 423c636..a839446 100644 --- a/src/missing-types.d.ts +++ b/src/missing-types.d.ts @@ -1,4 +1 @@ -declare module '*.module.css' { - const classes: { readonly [key: string]: string } - export default classes -} +declare module '*.css' {} diff --git a/src/theme/NativeIdealImage.css b/src/theme/NativeIdealImage.css new file mode 100644 index 0000000..0d8d904 --- /dev/null +++ b/src/theme/NativeIdealImage.css @@ -0,0 +1,33 @@ +.native-ideal-img { + display: block; + position: relative; + overflow: hidden; +} + +.native-ideal-img > img { + display: block; + width: 100%; + height: auto; +} + +.native-ideal-img::after { + content: ''; + position: absolute; + inset: 0; + background-image: var(--lqip); + background-repeat: no-repeat; + background-size: cover; + filter: blur(10px); + transform: scale(1.1); + z-index: -1; + transition: opacity 300ms, transform 300ms; +} + +.native-ideal-img.swap-on-load::after { + z-index: 1; +} + +.native-ideal-img.swap-on-load.loaded::after { + opacity: 0; + transform: scale(1); +} diff --git a/src/theme/NativeIdealImage.module.css b/src/theme/NativeIdealImage.module.css deleted file mode 100644 index ebcfdf7..0000000 --- a/src/theme/NativeIdealImage.module.css +++ /dev/null @@ -1,23 +0,0 @@ -.picture { - display: block; - position: relative; - overflow: hidden; -} - -.picture > img { - display: block; - width: 100%; - height: auto; -} - -.picture::after { - content: ''; - position: absolute; - inset: 0; - background-image: var(--lqip); - background-repeat: no-repeat; - background-size: cover; - filter: blur(10px); - transform: scale(1.1); - z-index: -1; -} diff --git a/src/theme/NativeIdealImage.tsx b/src/theme/NativeIdealImage.tsx index 35f0ef6..39fda33 100644 --- a/src/theme/NativeIdealImage.tsx +++ b/src/theme/NativeIdealImage.tsx @@ -1,26 +1,35 @@ -import React from 'react' +import React, { useEffect, useState } from 'react' +import clsx from 'clsx' import type { NativeIdealImageProps } from '../index.js' -import styles from './NativeIdealImage.module.css' +import './NativeIdealImage.css' export default function NativeIdealImage(props: NativeIdealImageProps): JSX.Element { - const { img, width, height, sizes, loading, ...propsRest } = props + const { img, width, height, sizes, loading, swapOnLoad, ...propsRest } = props const formats = typeof img === 'object' && 'formats' in img ? img.formats : [] const lqip = typeof img === 'object' && 'lqip' in img ? img.lqip : '' const singleImage = formats[0]?.srcSet.length === 1 ? formats[0] : undefined + const [placeHolderOnTop, setPlaceHolderOnTop] = useState(false) + const [loaded, setLoaded] = useState(false) + + useEffect(() => { + if (!loaded) { + const id = setTimeout(() => setPlaceHolderOnTop(true), 50) + return () => clearTimeout(id) + } + }, [loaded]) + return ( setLoaded(true)} > {formats.map((format) => (