UNPKG

@threlte/extras

Version:

Utilities, abstractions and plugins for your Threlte apps

60 lines (59 loc) 2.31 kB
import { Camera, Vector3 } from 'three'; import { currentWritable, isInstanceOf, useTask, useThrelte } from '@threlte/core'; import { fromStore } from 'svelte/store'; const origin = new Vector3(); const position = new Vector3(); const lastPosition = new Vector3(); export const useViewport = (target) => { const viewport = currentWritable({ width: 0, height: 0, factor: 0, distance: 0 }); const { camera: cameraStore, size: sizeStore, renderStage, scheduler } = useThrelte(); const updateViewport = (size, camera, distance) => { viewport.update(($viewport) => { const { width, height } = size; if (Array.isArray(target)) { origin.fromArray(target); } else if (target !== undefined) { origin.copy(target); } $viewport.distance = distance; if (isInstanceOf(camera, 'OrthographicCamera')) { $viewport.width = width / camera.zoom; $viewport.height = height / camera.zoom; $viewport.factor = 1; } else if (isInstanceOf(camera, 'PerspectiveCamera')) { const fov = (camera.fov * Math.PI) / 180; // convert vertical fov to radians const h = 2 * Math.tan(fov / 2) * distance; // visible height const w = h * (width / height); $viewport.width = w; $viewport.height = h; $viewport.factor = width / w; } return $viewport; }); }; const camera = fromStore(cameraStore); const size = fromStore(sizeStore); useTask(() => { camera.current.getWorldPosition(position); if (!position.equals(lastPosition)) { const distance = position.distanceTo(origin); updateViewport(size.current, camera.current, distance); lastPosition.copy(position); } }, { autoInvalidate: false, stage: scheduler.createStage(Symbol('viewport-stage'), { before: renderStage }) }); $effect.pre(() => { const distance = camera.current.getWorldPosition(position).distanceTo(origin); updateViewport(size.current, camera.current, distance); }); return viewport; };