From 445dba26c3636f1a0695549a446e1d5a27eb87db Mon Sep 17 00:00:00 2001 From: memdmp Date: Thu, 14 Aug 2025 13:26:07 +0200 Subject: feat: a proxy-based OnceCell --- src/user/ThreeVideo.ts | 60 +++++++++++++++++++++++++++++++++++++++++++++----- src/user/index.ts | 44 ++++++++++++++++++------------------ 2 files changed, 76 insertions(+), 28 deletions(-) diff --git a/src/user/ThreeVideo.ts b/src/user/ThreeVideo.ts index 92615f1..828c6c9 100644 --- a/src/user/ThreeVideo.ts +++ b/src/user/ThreeVideo.ts @@ -2,13 +2,61 @@ import { Video as BaseVideo, type FrameTime, type InitConfig } from '$/lib/Playe import * as THREE from 'three'; import type { OrbitControls } from 'three/examples/jsm/Addons.js'; -export const OnceCell = (create: () => T) => { - let called = false, - cached = null as unknown as T - return () => { - if (called) return cached - else { cached = create(); called = true; return cached } +export type Proxy = T +export const OnceCell = (create: () => T): Proxy => { + let value = null as unknown as T; + let created = false; + let createOnce = (): T extends object ? T : never => { + // @ts-ignore + if (created) return value; else return (created = true, value = create()) } + return new Proxy({ + get value() { + return createOnce() + } + }, { + get(_, ...args) { + return Reflect.get(createOnce(), ...args) + }, + set(_, ...args) { + return Reflect.set(createOnce(), ...args) + }, + has(_, ...args) { + return Reflect.has(createOnce(), ...args) + }, + deleteProperty(_, ...args) { + return Reflect.deleteProperty(createOnce(), ...args) + }, + isExtensible(_, ...args) { + return Reflect.isExtensible(createOnce(), ...args) + }, + ownKeys(_, ...args) { + return Reflect.ownKeys(createOnce(), ...args) + }, + defineProperty(_, ...args) { + return Reflect.defineProperty(createOnce(), ...args) + }, + getOwnPropertyDescriptor(_, ...args) { + return Reflect.getOwnPropertyDescriptor(createOnce(), ...args) + }, + preventExtensions(_, ...args) { + return Reflect.preventExtensions(createOnce(), ...args) + }, + getPrototypeOf(_, ...args) { + return Reflect.getPrototypeOf(createOnce(), ...args) + }, + setPrototypeOf(_, ...args) { + return Reflect.setPrototypeOf(createOnce(), ...args) + }, + apply(_, ...args) { + type F = (this: any, ...args: any[]) => any + return Reflect.apply(createOnce() as unknown as T extends F ? F : never, ...args) + }, + construct(_, ...args) { + type F = (this: any, ...args: any[]) => any + return Reflect.construct(createOnce() as unknown as T extends F ? F : never, ...args) + }, + }) as Proxy } export default abstract class ThreeVideo extends BaseVideo { diff --git a/src/user/index.ts b/src/user/index.ts index 541cd94..b4f0501 100644 --- a/src/user/index.ts +++ b/src/user/index.ts @@ -71,9 +71,9 @@ export default class Video extends ThreeVideo { metalness: 0.2, bumpScale: 1 })); - protected uiDark = OnceCell(() => new THREE.Mesh(this.uiGeometry(), this.uiDarkMaterial())) + protected uiDark = OnceCell(() => new THREE.Mesh(this.uiGeometry, this.uiDarkMaterial)) protected uiLightMaterial = OnceCell(() => new THREE.MeshBasicMaterial({ color: 0x000000 })) - protected uiLight = OnceCell(() => new THREE.Mesh(this.uiGeometry(), this.uiLightMaterial())) + protected uiLight = OnceCell(() => new THREE.Mesh(this.uiGeometry, this.uiLightMaterial)) protected lighting = OnceCell(() => { const dirLight = new THREE.DirectionalLight(0xffffff, 3); dirLight.castShadow = true; @@ -99,7 +99,7 @@ export default class Video extends ThreeVideo { c.height = c.width / 5 * 7; return c; }); - protected uiCanvasCtx = OnceCell(() => this.uiCanvas().getContext('2d', { + protected uiCanvasCtx = OnceCell(() => this.uiCanvas.getContext('2d', { alpha: true, })) protected drawUiCanvas() { } @@ -138,44 +138,44 @@ export default class Video extends ThreeVideo { case beat >= 13 && beat < 15: { this.scene.background = new THREE.Color(0x000000); - this.scene.add(this.lighting()) + this.scene.add(this.lighting) - this.scene.add(this.uiDark()); + this.scene.add(this.uiDark); const progress = (beat - 13) / 3.5 - this.uiDark().rotation.x = bezier(progress, 0.4, 0.6, 0.6, 1.1); - this.uiDark().rotation.y = bezier(progress, 2, 1.7, 1.6, bezier(progress, 1, 0.8, 0.3, -0.5)); + this.uiDark.rotation.x = bezier(progress, 0.4, 0.6, 0.6, 1.1); + this.uiDark.rotation.y = bezier(progress, 2, 1.7, 1.6, bezier(progress, 1, 0.8, 0.3, -0.5)); this.camera.position.z = bezier(progress, 6, 4, 4, bezier(progress, 7, 9, 15, 25)); - this.lighting().position.set(0, 0, this.camera.position.z); + this.lighting.position.set(0, 0, this.camera.position.z); this.renderScene(this.ctx) - this.scene.remove(this.uiDark()); - this.scene.remove(this.lighting()) + this.scene.remove(this.uiDark); + this.scene.remove(this.lighting) break; } case beat >= 15 && beat < 16: { this.scene.background = new THREE.Color(0x000000); - this.scene.add(this.lighting()) + this.scene.add(this.lighting) - this.scene.add(this.uiDark()); + this.scene.add(this.uiDark); const progress = (beat - 15) - this.uiDark().rotation.x = bezier(progress, -0.5, -0.5, -0.5, -0.5); - this.uiDark().rotation.y = bezier(progress, 0.7, 0.6, 0.4, 0.4); + this.uiDark.rotation.x = bezier(progress, -0.5, -0.5, -0.5, -0.5); + this.uiDark.rotation.y = bezier(progress, 0.7, 0.6, 0.4, 0.4); this.camera.position.z = bezier(progress, 4, 5, 5, 8); - this.lighting().position.set(-0.5, 0, this.camera.position.z); + this.lighting.position.set(-0.5, 0, this.camera.position.z); this.renderScene(this.ctx) - this.scene.remove(this.uiDark()); - this.scene.remove(this.lighting()) + this.scene.remove(this.uiDark); + this.scene.remove(this.lighting) break; } case beat >= 16 && beat < 18.4: { - this.scene.add(this.uiLight()); + this.scene.add(this.uiLight); const progress = (beat - 16) / 3.5 - this.uiLight().rotation.x = bezier(progress, -0.5, 0.6, 0.6, 1.1) * (beat < 17 ? 1 : -1); - this.uiLight().rotation.y = bezier(progress, 0.4, 1.7, 1.8, 2) * (beat < 17 ? 1 : -1); + this.uiLight.rotation.x = bezier(progress, -0.5, 0.6, 0.6, 1.1) * (beat < 17 ? 1 : -1); + this.uiLight.rotation.y = bezier(progress, 0.4, 1.7, 1.8, 2) * (beat < 17 ? 1 : -1); this.camera.position.z = bezier(progress, 8, 4, 4, 12); this.renderScene(this.ctx) - this.scene.remove(this.uiLight()); + this.scene.remove(this.uiLight); break; } case beat >= 18.4 && beat < 22.8: { @@ -247,7 +247,7 @@ export default class Video extends ThreeVideo { renderText(this.ctx, `${(Math.floor(beat * 10) / 10).toFixed(1)}`, '#646663', { ...AdDefault, size: this.px(12), weight: 400 }, { x: this.w - this.px(4), y: this.h - this.px(4) }, 'end') } public cleanup(): void { - this.uiCanvas().remove() + this.uiCanvas.remove() super.cleanup() } public fps = 59.94; -- cgit v1.2.3