Skip to content

Commit

Permalink
perf(react): ⚡️ render only visible canvas area (#259)
Browse files Browse the repository at this point in the history
* perf(react): ⚡️ render only visible canvas area

* chore: 🤖 update changelog
  • Loading branch information
theashraf authored Jun 24, 2024
1 parent d7c2c20 commit a564ff0
Show file tree
Hide file tree
Showing 3 changed files with 51 additions and 1 deletion.
9 changes: 9 additions & 0 deletions .changeset/thin-humans-run.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
'@lottiefiles/dotlottie-react': minor
---

perf(react): ⚡️ render only visible canvas area

This update optimizes the rendering performance by ensuring that only the visible portion of the canvas is rendered, utilizing the dotlottie-web `setViewport` API.

> Note: No changes are required for existing usage. The optimization is applied internally within the `DotLottieReact` component.
4 changes: 4 additions & 0 deletions apps/dotlottie-react-example/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,10 @@ function App() {
playOnHover={playOnHover}
autoResizeCanvas={autoResizeCanvas}
marker={marker}
style={{
margin: '2px',
border: '1px solid white',
}}
animationId={currentAnimationId}
/>
<input type="range" min="0" max="100" defaultValue="0" value={progress} />
Expand Down
39 changes: 38 additions & 1 deletion packages/react/src/use-dotlottie.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,27 @@ export interface UseDotLottieResult {

const isServerSide = (): boolean => typeof window === 'undefined';

const getCanvasViewport = (
canvas: HTMLCanvasElement,
dpr: number,
): { height: number; width: number; x: number; y: number } => {
const rect = canvas.getBoundingClientRect();
const windowWidth = window.innerWidth;
const windowHeight = window.innerHeight;

const visibleLeft = Math.max(0, -rect.left);
const visibleTop = Math.max(0, -rect.top);
const visibleRight = Math.min(rect.width, windowWidth - rect.left);
const visibleBottom = Math.min(rect.height, windowHeight - rect.top);

const x = visibleLeft * dpr;
const y = visibleTop * dpr;
const width = (visibleRight - visibleLeft) * dpr;
const height = (visibleBottom - visibleTop) * dpr;

return { x, y, width, height };
};

export const useDotLottie = (config?: DotLottieConfig): UseDotLottieResult => {
const [dotLottie, setDotLottie] = useState<DotLottie | null>(null);

Expand All @@ -80,6 +101,20 @@ export const useDotLottie = (config?: DotLottieConfig): UseDotLottieResult => {
}
}, []);

const updateViewport = useCallback(() => {
if (!dotLottieRef.current) return;

const canvas = canvasRef.current;

if (!canvas) return;

const dpr = configRef.current?.renderConfig?.devicePixelRatio || window.devicePixelRatio || 1;

const { height, width, x, y } = getCanvasViewport(canvas, dpr);

dotLottieRef.current.setViewport(x, y, width, height);
}, []);

const intersectionObserver = useMemo(() => {
if (isServerSide()) return null;

Expand Down Expand Up @@ -157,7 +192,9 @@ export const useDotLottie = (config?: DotLottieConfig): UseDotLottieResult => {
}
canvas.addEventListener('mouseenter', hoverHandler);
canvas.addEventListener('mouseleave', hoverHandler);
dotLottieInstance.addEventListener('frame', updateViewport);

updateViewport();
setDotLottie(dotLottieInstance);
}

Expand All @@ -169,7 +206,7 @@ export const useDotLottie = (config?: DotLottieConfig): UseDotLottieResult => {
canvas?.removeEventListener('mouseenter', hoverHandler);
canvas?.removeEventListener('mouseleave', hoverHandler);
};
}, [intersectionObserver, resizeObserver, hoverHandler]);
}, [intersectionObserver, resizeObserver, hoverHandler, updateViewport]);

// speed reactivity
useEffect(() => {
Expand Down

0 comments on commit a564ff0

Please sign in to comment.