Skip to content

Commit a8af127

Browse files
committed
feat: rehype-image-size, prevent image jump
1 parent 1c7c618 commit a8af127

File tree

6 files changed

+95
-9
lines changed

6 files changed

+95
-9
lines changed

lib/rehype-image-size.js

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
const memoize = require('micro-memoize');
2+
const visit = require('unist-util-visit');
3+
const pAll = require('p-all');
4+
const sizeOf = require('image-size');
5+
6+
/**
7+
* Simple plugin to get the size of local images so we can use it in react
8+
*/
9+
const rehypeImageSize = () => {
10+
async function transformer(tree) {
11+
const nodes = [];
12+
visit(tree, 'element', node => {
13+
if (node.tagName !== 'img') {
14+
return;
15+
} else {
16+
nodes.push(node);
17+
}
18+
});
19+
await pAll(
20+
nodes.map(node => () => visitor(node)),
21+
{ concurrency: 25 }
22+
);
23+
return tree;
24+
}
25+
async function visitor(node) {
26+
const isRelative =
27+
node && node.properties && node.properties.src && node.properties.src.startsWith('/');
28+
if (isRelative) {
29+
const dimensions = sizeOf(`public/${node.properties.src}`);
30+
node.properties['dimensions'] = dimensions;
31+
}
32+
}
33+
return transformer;
34+
};
35+
36+
module.exports = memoize(rehypeImageSize);

lib/rehype-plugins.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
const memoize = require('micro-memoize');
22
const { rehypeVscode } = require('unified-vscode');
3+
const rehypeImgs = require('./rehype-image-size');
34

4-
const rehypePlugins = [memoize(rehypeVscode)];
5+
const rehypePlugins = [memoize(rehypeVscode), rehypeImgs];
56

67
module.exports = { rehypePlugins };

package.json

+1
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
"gray-matter": "^4.0.2",
3333
"hast-util-to-string": "^1.0.4",
3434
"html-react-parser": "^0.13.0",
35+
"image-size": "^0.8.3",
3536
"lodash.debounce": "^4.0.8",
3637
"mdi-react": "7.3.0",
3738
"micro-memoize": "^4.0.9",

src/components/mdx/image.tsx

+40-6
Original file line numberDiff line numberDiff line change
@@ -40,15 +40,49 @@ const useImgix = (src: string) => {
4040
};
4141
};
4242

43-
export const Img: React.FC<BoxProps & { loading?: string; src?: string; alt?: string }> = ({
44-
src: _src,
45-
...rest
46-
}) => {
43+
const getAspectRatio = dimensions => {
44+
if (!dimensions) return;
45+
46+
const { width, height } = dimensions;
47+
48+
return (height / width) * 100;
49+
};
50+
51+
const BaseImg: React.FC<any> = props => (
52+
<Box
53+
loading="lazy"
54+
maxWidth="100%"
55+
width={['100%', '100%', 'inherit', 'inherit']}
56+
display="block"
57+
as="img"
58+
{...props}
59+
/>
60+
);
61+
62+
export const Img: React.FC<
63+
BoxProps & { loading?: string; src?: string; alt?: string; dimensions?: any }
64+
> = React.memo(({ src: _src, dimensions, ...rest }) => {
4765
const { src, srcset } = useImgix(_src);
4866
const props = {
4967
src,
5068
srcSet: srcset,
5169
...rest,
5270
};
53-
return <Box loading="lazy" maxWidth="100%" display="block" as="img" {...props} />;
54-
};
71+
72+
if (dimensions) {
73+
// means the image is local and we can generate the aspect ratio
74+
// and prevent the page from jumping due to lack of an image being loaded
75+
// (because of the built in lazy-loading)
76+
77+
const aspectRatio = getAspectRatio(dimensions);
78+
79+
const width = dimensions.width <= 720 ? dimensions.width : '100%';
80+
return (
81+
<Box width={width} maxWidth="100%" padding="0 !important" position="relative" className="img">
82+
<Box height="0" paddingBottom={`${aspectRatio}%`} width="100%" />
83+
<BaseImg position="absolute" top={0} left={0} {...props} />
84+
</Box>
85+
);
86+
}
87+
return <BaseImg className="img" {...props} />;
88+
});

src/components/mdx/md-contents.tsx

+2-2
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ export const styleOverwrites = {
3838
pre: {
3939
// px: space(['none', 'none', 'extra-loose', 'extra-loose']),
4040
},
41-
img: {
41+
'.img': {
4242
mx: 'auto',
4343
},
4444
},
@@ -210,7 +210,7 @@ export const styleOverwrites = {
210210
mt: space('extra-loose'),
211211
},
212212
},
213-
img: {
213+
'.img': {
214214
my: space('extra-loose'),
215215
},
216216
table: {

yarn.lock

+14
Original file line numberDiff line numberDiff line change
@@ -4960,6 +4960,13 @@ ignore@^4.0.6:
49604960
resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc"
49614961
integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==
49624962

4963+
image-size@^0.8.3:
4964+
version "0.8.3"
4965+
resolved "https://registry.yarnpkg.com/image-size/-/image-size-0.8.3.tgz#f0b568857e034f29baffd37013587f2c0cad8b46"
4966+
integrity sha512-SMtq1AJ+aqHB45c3FsB4ERK0UCiA2d3H1uq8s+8T0Pf8A3W4teyBQyaFaktH6xvZqh+npwlKU7i4fJo0r7TYTg==
4967+
dependencies:
4968+
queue "6.0.1"
4969+
49634970
immediate@^3.2.3:
49644971
version "3.3.0"
49654972
resolved "https://registry.yarnpkg.com/immediate/-/immediate-3.3.0.tgz#1aef225517836bcdf7f2a2de2600c79ff0269266"
@@ -6987,6 +6994,13 @@ [email protected], querystring@^0.2.0:
69876994
resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.0.tgz#b209849203bb25df820da756e747005878521620"
69886995
integrity sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=
69896996

6997+
6998+
version "6.0.1"
6999+
resolved "https://registry.yarnpkg.com/queue/-/queue-6.0.1.tgz#abd5a5b0376912f070a25729e0b6a7d565683791"
7000+
integrity sha512-AJBQabRCCNr9ANq8v77RJEv73DPbn55cdTb+Giq4X0AVnNVZvMHlYp7XlQiN+1npCZj1DuSmaA2hYVUUDgxFDg==
7001+
dependencies:
7002+
inherits "~2.0.3"
7003+
69907004
randombytes@^2.0.0, randombytes@^2.0.1, randombytes@^2.0.5, randombytes@^2.1.0:
69917005
version "2.1.0"
69927006
resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a"

0 commit comments

Comments
 (0)