phanpy/src/components/avatar.jsx

100 lines
2.7 KiB
React
Raw Normal View History

2022-12-10 12:14:48 +03:00
import './avatar.css';
2023-03-13 05:10:21 +03:00
import { useRef } from 'preact/hooks';
2023-10-21 07:21:05 +03:00
import mem from '../utils/mem';
2022-12-10 12:14:48 +03:00
const SIZES = {
s: 16,
m: 20,
l: 24,
xl: 32,
xxl: 50,
xxxl: 64,
2022-12-10 12:14:48 +03:00
};
const alphaCache = {};
2023-06-14 06:15:05 +03:00
const canvas = window.OffscreenCanvas
? new OffscreenCanvas(1, 1)
: document.createElement('canvas');
2023-09-09 09:26:08 +03:00
const ctx = canvas.getContext('2d', {
willReadFrequently: true,
});
2024-03-10 18:25:07 +03:00
ctx.imageSmoothingEnabled = false;
2023-06-14 06:15:05 +03:00
2023-04-10 19:26:43 +03:00
function Avatar({ url, size, alt = '', squircle, ...props }) {
2022-12-10 12:14:48 +03:00
size = SIZES[size] || size || SIZES.m;
2023-03-13 05:10:21 +03:00
const avatarRef = useRef();
2023-03-21 10:52:26 +03:00
const isMissing = /missing\.png$/.test(url);
2022-12-10 12:14:48 +03:00
return (
<span
2023-03-13 05:10:21 +03:00
ref={avatarRef}
2023-04-10 19:26:43 +03:00
class={`avatar ${squircle ? 'squircle' : ''} ${
alphaCache[url] ? 'has-alpha' : ''
}`}
2022-12-10 12:14:48 +03:00
style={{
width: size,
height: size,
}}
title={alt}
{...props}
2022-12-10 12:14:48 +03:00
>
{!!url && (
2023-03-13 05:10:21 +03:00
<img
src={url}
width={size}
height={size}
alt={alt}
loading="lazy"
2023-06-14 15:31:02 +03:00
decoding="async"
2023-03-21 10:52:26 +03:00
crossOrigin={
alphaCache[url] === undefined && !isMissing
? 'anonymous'
: undefined
}
onError={(e) => {
if (e.target.crossOrigin) {
e.target.crossOrigin = null;
e.target.src = url;
}
}}
2023-03-13 05:10:21 +03:00
onLoad={(e) => {
2023-03-13 11:22:41 +03:00
if (avatarRef.current) avatarRef.current.dataset.loaded = true;
if (alphaCache[url] !== undefined) return;
2023-03-21 10:52:26 +03:00
if (isMissing) return;
2024-05-28 17:22:14 +03:00
setTimeout(() => {
2023-12-28 05:50:54 +03:00
try {
// Check if image has alpha channel
const { width, height } = e.target;
if (canvas.width !== width) canvas.width = width;
if (canvas.height !== height) canvas.height = height;
ctx.drawImage(e.target, 0, 0);
const allPixels = ctx.getImageData(0, 0, width, height);
// At least 10% of pixels have alpha <= 128
const hasAlpha =
allPixels.data.filter(
(pixel, i) => i % 4 === 3 && pixel <= 128,
).length /
(allPixels.data.length / 4) >
0.1;
if (hasAlpha) {
// console.log('hasAlpha', hasAlpha, allPixels.data);
avatarRef.current.classList.add('has-alpha');
}
alphaCache[url] = hasAlpha;
ctx.clearRect(0, 0, width, height);
} catch (e) {
// Silent fail
alphaCache[url] = false;
}
2024-05-28 17:22:14 +03:00
}, 1);
2023-03-13 05:10:21 +03:00
}}
/>
2022-12-10 12:14:48 +03:00
)}
</span>
);
2022-12-16 08:27:04 +03:00
}
2023-10-21 07:21:05 +03:00
export default mem(Avatar);