Skip to content

Commit c460601

Browse files
committed
improvement to iamge quality
1 parent d1f139d commit c460601

File tree

1 file changed

+119
-0
lines changed

1 file changed

+119
-0
lines changed

src/theme/IdealImage/index.js

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
/**
2+
* Custom IdealImage component with improved quality for sized images
3+
*/
4+
5+
import React from 'react';
6+
import ReactIdealImage from '@slorber/react-ideal-image';
7+
import {translate} from '@docusaurus/Translate';
8+
9+
// Helper function to convert bytes to human readable size
10+
function bytesToSize(bytes) {
11+
const sizes = ['B', 'KB', 'MB', 'GB', 'TB'];
12+
if (bytes === 0) {
13+
return 'n/a';
14+
}
15+
const scale = Math.floor(Math.log(bytes) / Math.log(1024));
16+
if (scale === 0) {
17+
return `${bytes} ${sizes[scale]}`;
18+
}
19+
return `${(bytes / 1024 ** scale).toFixed(1)} ${sizes[scale]}`;
20+
}
21+
22+
// Custom message handler
23+
function getMessage(icon, state) {
24+
switch (icon) {
25+
case 'noicon':
26+
case 'loaded':
27+
return null;
28+
case 'loading':
29+
return translate({
30+
id: 'theme.IdealImageMessage.loading',
31+
message: 'Loading...',
32+
description: 'When the full-scale image is loading',
33+
});
34+
case 'load': {
35+
const {pickedSrc} = state;
36+
const {size} = pickedSrc;
37+
const sizeMessage = size ? ` (${bytesToSize(size)})` : '';
38+
return translate(
39+
{
40+
id: 'theme.IdealImageMessage.load',
41+
message: 'Click to load{sizeMessage}',
42+
description:
43+
'To prompt users to load the full image. sizeMessage is a parenthesized size figure.',
44+
},
45+
{sizeMessage},
46+
);
47+
}
48+
case 'offline':
49+
return translate({
50+
id: 'theme.IdealImageMessage.offline',
51+
message: 'Your browser is offline. Image not loaded',
52+
description: 'When the user is viewing an offline document',
53+
});
54+
case 'error': {
55+
const {loadInfo} = state;
56+
if (loadInfo === 404) {
57+
return translate({
58+
id: 'theme.IdealImageMessage.404error',
59+
message: '404. Image not found',
60+
description: 'When the image is not found',
61+
});
62+
}
63+
return translate({
64+
id: 'theme.IdealImageMessage.error',
65+
message: 'Error. Click to reload',
66+
description: 'When the image fails to load for unknown error',
67+
});
68+
}
69+
default:
70+
throw new Error(`Wrong icon: ${icon}`);
71+
}
72+
}
73+
74+
export default function IdealImage(props) {
75+
const {img, size, ...propsRest} = props;
76+
77+
// In dev env just use regular img with original file
78+
if (typeof img === 'string' || 'default' in img) {
79+
return (
80+
<img src={typeof img === 'string' ? img : img.default} {...propsRest} />
81+
);
82+
}
83+
84+
// For sized images (sm, md), use a higher quality placeholder
85+
// to avoid the blurry initial display
86+
let betterPlaceholder = img.preSrc;
87+
88+
if (size === 'sm' || size === 'md' || size === 'lg') {
89+
const availableImages = img.src.images || [];
90+
if (availableImages.length > 0) {
91+
// For smaller sizes, use 600px image as placeholder instead of LQIP
92+
// This provides much better initial quality
93+
const higherQualityImage = availableImages.find(image => image.width === 600) ||
94+
availableImages.find(image => image.width === 300) ||
95+
availableImages[availableImages.length - 1];
96+
97+
if (higherQualityImage) {
98+
betterPlaceholder = higherQualityImage.path;
99+
}
100+
}
101+
}
102+
103+
return (
104+
<ReactIdealImage
105+
{...propsRest}
106+
height={img.src.height ?? 100}
107+
width={img.src.width ?? 100}
108+
placeholder={{lqip: betterPlaceholder}}
109+
src={img.src.src}
110+
srcSet={img.src.images.map((image) => ({
111+
...image,
112+
src: image.path,
113+
}))}
114+
getMessage={getMessage}
115+
// For sized images, load eagerly to show quality immediately
116+
threshold={size ? 0 : undefined}
117+
/>
118+
);
119+
}

0 commit comments

Comments
 (0)