Skip to content

Commit

Permalink
Add a feature to fade in image on fully loaded
Browse files Browse the repository at this point in the history
- Also change module css to normal css for eaiser consume
  • Loading branch information
Legend-Master committed Aug 12, 2024
1 parent b6c0a9c commit 0566379
Show file tree
Hide file tree
Showing 9 changed files with 69 additions and 46 deletions.
14 changes: 7 additions & 7 deletions example/src/pages/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,19 +44,19 @@ export default function Home(): JSX.Element {
<main>
<div className={styles.bigMargin}></div>
<div className={styles.cardsContainer}>
<Card name={'something 1'} image={stoneRoadImage} />
<Card name={'something 2'} image={stoneRoadImage} />
<Card name={'something 3'} image={stoneRoadImage} />
<Card name={'something 4'} image={stoneRoadImage} />
<Card name={'something 5'} image={stoneRoadImage} />
<Card name={'something 6'} image={stoneRoadImage} />
<Card name="normal 1" image={stoneRoadImage} />
<Card name="normal 2" image={stoneRoadImage} />
<Card name="normal 3" image={stoneRoadImage} />
<Card name="swap on load 4" image={stoneRoadImage} swapOnLoad />
<Card name="swap on load 5" image={stoneRoadImage} swapOnLoad />
<Card name="swap on load 6" image={stoneRoadImage} swapOnLoad />
</div>
</main>
</Layout>
)
}

function Card(props: { name: string; image: LoaderOutput }) {
function Card(props: { name: string; image: LoaderOutput; swapOnLoad?: boolean }) {
return (
<div className={clsx(styles.card, 'pagination-nav__link')}>
<NativeIdealImage img={props.image} className={styles.cardImage} />
Expand Down
1 change: 1 addition & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
2 changes: 1 addition & 1 deletion scripts/copy-css.js
Original file line number Diff line number Diff line change
@@ -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')
5 changes: 5 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
5 changes: 1 addition & 4 deletions src/missing-types.d.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1 @@
declare module '*.module.css' {
const classes: { readonly [key: string]: string }
export default classes
}
declare module '*.css' {}
33 changes: 33 additions & 0 deletions src/theme/NativeIdealImage.css
Original file line number Diff line number Diff line change
@@ -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);
}
23 changes: 0 additions & 23 deletions src/theme/NativeIdealImage.module.css

This file was deleted.

31 changes: 20 additions & 11 deletions src/theme/NativeIdealImage.tsx
Original file line number Diff line number Diff line change
@@ -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 (
<picture
className={styles.picture}
style={
lqip
? ({
'--lqip': `url(${lqip})`,
} as React.CSSProperties)
: undefined
}
className={clsx('native-ideal-img', {
'swap-on-load': placeHolderOnTop && swapOnLoad,
loaded,
})}
style={lqip ? ({ '--lqip': `url(${lqip})` } as React.CSSProperties) : undefined}
onLoad={() => setLoaded(true)}
>
{formats.map((format) => (
<source
Expand Down

0 comments on commit 0566379

Please sign in to comment.