@playcanvas/blocks
Version:
High level abstract 3D primitives for React
64 lines • 2.6 kB
JavaScript
import { useApp, useAppEvent } from "@playcanvas/react/hooks";
import { useEffect, useRef } from "react";
const nearlyEquals = (a, b, epsilon = 1e-4) => {
for (let i = 0; i < a.length; i++) {
if (Math.abs(a[i] - b[i]) >= epsilon)
return false;
}
return true;
};
/**
* A custom React hook that triggers rendering when the camera's transform or projection matrix changes.
* Only renders when the camera is visible in the viewport.
*
* @param {Entity | null} entity - The PlayCanvas entity representing the camera. If null, the hook does nothing.
* @returns {void} This hook does not return a value but updates the rendering state of the application.
*/
export const useRenderOnCameraChange = (entity) => {
const app = useApp();
const prevWorld = useRef(new Float32Array(16));
const prevProj = useRef(new Float32Array(16));
const isVisible = useRef(true);
/**
* This hook ensures that rendering only happens when the canvas is visible in the viewport.
* This takes precedence over the camera change detection. Eg if the camera is animating, but
* the canvas is not visible, dont render.
*/
useEffect(() => {
const canvas = app.graphicsDevice.canvas;
if (!canvas)
return;
const observer = new IntersectionObserver(([entry]) => {
isVisible.current = entry.isIntersecting;
if (!isVisible.current) {
app.renderNextFrame = false;
}
}, { threshold: 0.1 });
observer.observe(canvas);
return () => observer.disconnect();
}, [app]);
/**
* This hook ensures that rendering only happens when the camera has moved or its projection has changed.
* However if the canvas is not visible on the page it will take precedence.
* Don't render if the canvas is not visible regardless of any animations.
*/
useAppEvent('update', () => {
if (!entity || !isVisible.current)
return;
const world = entity.getWorldTransform().data;
const proj = entity.camera?.projectionMatrix?.data;
if (!proj) {
app.renderNextFrame = true;
return;
}
const changed = !nearlyEquals(world, prevWorld.current) || !nearlyEquals(proj, prevProj.current);
if (!app.renderNextFrame) {
app.renderNextFrame = changed;
}
if (app.renderNextFrame) {
prevWorld.current.set(world);
prevProj.current.set(proj);
}
});
};
//# sourceMappingURL=use-render-on-camera-change.js.map