44 lines
1.3 KiB
TypeScript
44 lines
1.3 KiB
TypeScript
|
|
import { useEffect } from "react";
|
||
|
|
|
||
|
|
export function useHiDPICanvas(
|
||
|
|
imgRef: React.RefObject<HTMLImageElement | null>,
|
||
|
|
canvasRef: React.RefObject<HTMLCanvasElement | null>
|
||
|
|
) {
|
||
|
|
const sync = () => {
|
||
|
|
const img = imgRef.current,
|
||
|
|
cvs = canvasRef.current;
|
||
|
|
if (!img || !cvs) return;
|
||
|
|
|
||
|
|
const dpr = window.devicePixelRatio || 1;
|
||
|
|
const w = img.clientWidth || img.naturalWidth || 0;
|
||
|
|
const h = img.clientHeight || img.naturalHeight || 0;
|
||
|
|
|
||
|
|
// CSS size
|
||
|
|
cvs.style.width = `${w}px`;
|
||
|
|
cvs.style.height = `${h}px`;
|
||
|
|
|
||
|
|
// backing store size (scaled for HiDPI)
|
||
|
|
const W = Math.max(1, Math.round(w * dpr));
|
||
|
|
const H = Math.max(1, Math.round(h * dpr));
|
||
|
|
if (cvs.width !== W || cvs.height !== H) {
|
||
|
|
cvs.width = W;
|
||
|
|
cvs.height = H;
|
||
|
|
const ctx = cvs.getContext("2d");
|
||
|
|
if (ctx) ctx.setTransform(dpr, 0, 0, dpr, 0, 0); // draw in CSS px
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
useEffect(() => {
|
||
|
|
const ro = new ResizeObserver(sync); // reacts to image size changes
|
||
|
|
if (imgRef.current) ro.observe(imgRef.current);
|
||
|
|
const onResize = () => sync();
|
||
|
|
window.addEventListener("resize", onResize);
|
||
|
|
return () => {
|
||
|
|
ro.disconnect();
|
||
|
|
window.removeEventListener("resize", onResize);
|
||
|
|
};
|
||
|
|
}, []); // eslint-disable-line react-hooks/exhaustive-deps
|
||
|
|
|
||
|
|
return { sync };
|
||
|
|
}
|