Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Files] Delay <Image/> blurhash reveal and handle blurhash errors #146159

Merged
merged 3 commits into from
Nov 24, 2022
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 43 additions & 5 deletions src/plugins/files/public/components/image/image.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
import React from 'react';
import { useState } from 'react';
import { EuiImage, EuiImageProps } from '@elastic/eui';
import classNames from 'classnames';
import { css } from '@emotion/react';
import type { FileImageMetadata } from '../../../common';
import { getBlurhashSrc } from '../util';

Expand All @@ -28,10 +30,19 @@ export const Image = ({ src, url, alt, onLoad, onError, meta, ...rest }: Props)

const [isBlurHashLoaded, setIsBlurHashLoaded] = useState<boolean>(false);
const { blurhash, width, height } = meta ?? {};
const blurhashSrc = React.useMemo(
() => (blurhash && width && height ? getBlurhashSrc({ hash: blurhash, width, height }) : null),
[blurhash, width, height]
);
const blurhashSrc = React.useMemo(() => {
if (blurhash && width && height) {
try {
return getBlurhashSrc({ hash: blurhash, width, height });
} catch (e) {
// eslint-disable-next-line no-console
console.warn(`Failed to generate image src from blurhash`, e);
return null;
}
} else {
return null;
}
}, [blurhash, width, height]);

// prettier-ignore
const currentSrc = (isBlurHashLoaded || !blurhashSrc) ? imageSrc : blurhashSrc
Expand All @@ -41,6 +52,26 @@ export const Image = ({ src, url, alt, onLoad, onError, meta, ...rest }: Props)
alt=""
loading={'lazy'}
{...rest}
className={classNames(rest.className, { blurhash: currentSrc === blurhashSrc })}
css={css`
&.blurhash {
// Makes blurhash image visually appear after the .9s delay with a .1s transition duration.
// This is needed for a nicer UX when the original image loads fast.
animation-name: imageBlurhashFadeIn;
animation-duration: 1s;
@keyframes imageBlurhashFadeIn {
0% {
opacity: 0;
}
90% {
opacity: 0;
}
100% {
opacity: 1;
}
}
}
`}
src={currentSrc}
onLoad={(ev) => {
// if the `meta.blurhash` is passed, then the component first renders the blurhash and the `onLoad` event fires for the first time,
Expand All @@ -62,7 +93,14 @@ export const Image = ({ src, url, alt, onLoad, onError, meta, ...rest }: Props)
}
}}
onError={(ev) => {
onError?.(ev);
if (currentSrc === imageSrc) {
onError?.(ev);
} else {
// blurhash failed to load, consider it is loaded to start loading the full image
// eslint-disable-next-line no-console
console.warn(`Failed to load blurhash src`);
setIsBlurHashLoaded(true);
}
}}
/>
);
Expand Down