Skip to content

Commit bd92ec8

Browse files
fix(ts-tailwind): Beams, Dither, GridDistortion & GridScan
1 parent 2d9ad63 commit bd92ec8

9 files changed

Lines changed: 99 additions & 66 deletions

File tree

public/r/Beams-TS-TW.json

Lines changed: 3 additions & 3 deletions
Large diffs are not rendered by default.

public/r/Dither-TS-TW.json

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.

public/r/GridDistortion-TS-TW.json

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.

public/r/GridScan-TS-TW.json

Lines changed: 3 additions & 3 deletions
Large diffs are not rendered by default.

public/r/registry.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6226,8 +6226,8 @@
62266226
"type": "registry:component",
62276227
"dependencies": [
62286228
"three@^0.167.1",
6229-
"@react-three/fiber@^9.3.0",
6230-
"@react-three/drei@^10.7.4"
6229+
"@react-three/drei@^10.7.4",
6230+
"@react-three/fiber@^9.3.0"
62316231
],
62326232
"registryDependencies": [],
62336233
"files": [
@@ -6893,9 +6893,9 @@
68936893
"description": "Animated grid room 3D scan effect and cool interactions.",
68946894
"type": "registry:component",
68956895
"dependencies": [
6896+
"face-api.js@^0.22.2",
68966897
"postprocessing@^6.36.0",
6897-
"three@^0.167.1",
6898-
"face-api.js@^0.22.2"
6898+
"three@^0.167.1"
68996899
],
69006900
"registryDependencies": [],
69016901
"files": [

src/ts-tailwind/Backgrounds/Beams/Beams.tsx

Lines changed: 33 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
1-
import { forwardRef, useImperativeHandle, useEffect, useRef, useMemo, FC, ReactNode } from 'react';
2-
1+
import { FC, forwardRef, ReactNode, useEffect, useImperativeHandle, useMemo, useRef } from 'react';
32
import * as THREE from 'three';
43

5-
import { Canvas, useFrame } from '@react-three/fiber';
64
import { PerspectiveCamera } from '@react-three/drei';
5+
import { Canvas, useFrame } from '@react-three/fiber';
76
import { degToRad } from 'three/src/math/MathUtils.js';
87

98
type UniformValue = THREE.IUniform<unknown> | unknown;
@@ -46,6 +45,8 @@ function extendMaterial<T extends THREE.Material = THREE.Material>(
4645
if ('envMap' in defaults) uniforms.envMap.value = defaults.envMap;
4746
if ('envMapIntensity' in defaults) uniforms.envMapIntensity.value = defaults.envMapIntensity;
4847

48+
defaults.dispose();
49+
4950
Object.entries(cfg.uniforms ?? {}).forEach(([key, u]) => {
5051
uniforms[key] =
5152
u !== null && typeof u === 'object' && 'value' in u
@@ -76,7 +77,7 @@ function extendMaterial<T extends THREE.Material = THREE.Material>(
7677
}
7778

7879
const CanvasWrapper: FC<{ children: ReactNode }> = ({ children }) => (
79-
<Canvas dpr={[1, 2]} frameloop="always" className="w-full h-full relative">
80+
<Canvas dpr={[1, 2]} frameloop="always" className="relative w-full h-full">
8081
{children}
8182
</Canvas>
8283
);
@@ -243,9 +244,27 @@ const Beams: FC<BeamsProps> = ({
243244
uScale: scale
244245
}
245246
}),
246-
[speed, noiseIntensity, scale]
247+
[]
247248
);
248249

250+
useEffect(() => {
251+
return () => {
252+
beamMaterial.dispose();
253+
};
254+
}, [beamMaterial]);
255+
256+
useEffect(() => {
257+
if (beamMaterial.uniforms.uSpeed) beamMaterial.uniforms.uSpeed.value = speed;
258+
}, [beamMaterial, speed]);
259+
260+
useEffect(() => {
261+
if (beamMaterial.uniforms.uNoiseIntensity) beamMaterial.uniforms.uNoiseIntensity.value = noiseIntensity;
262+
}, [beamMaterial, noiseIntensity]);
263+
264+
useEffect(() => {
265+
if (beamMaterial.uniforms.uScale) beamMaterial.uniforms.uScale.value = scale;
266+
}, [beamMaterial, scale]);
267+
249268
return (
250269
<CanvasWrapper>
251270
<group rotation={[0, 0, degToRad(rotation)]}>
@@ -324,13 +343,22 @@ const MergedPlanes = forwardRef<
324343
>(({ material, width, count, height }, ref) => {
325344
const mesh = useRef<THREE.Mesh<THREE.BufferGeometry, THREE.ShaderMaterial>>(null!);
326345
useImperativeHandle(ref, () => mesh.current);
346+
327347
const geometry = useMemo(
328348
() => createStackedPlanesBufferGeometry(count, width, height, 0, 100),
329349
[count, width, height]
330350
);
351+
352+
useEffect(() => {
353+
return () => {
354+
geometry.dispose();
355+
};
356+
}, [geometry]);
357+
331358
useFrame((_, delta) => {
332359
mesh.current.material.uniforms.time.value += 0.1 * delta;
333360
});
361+
334362
return <mesh ref={mesh} geometry={geometry} material={material} />;
335363
});
336364
MergedPlanes.displayName = 'MergedPlanes';

src/ts-tailwind/Backgrounds/Dither/Dither.tsx

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
1-
/* eslint-disable react/no-unknown-property */
2-
import { useRef, useState, useEffect, forwardRef } from 'react';
3-
import { Canvas, useFrame, useThree, ThreeEvent } from '@react-three/fiber';
1+
import { Canvas, ThreeEvent, useFrame, useThree } from '@react-three/fiber';
42
import { EffectComposer, wrapEffect } from '@react-three/postprocessing';
53
import { Effect } from 'postprocessing';
4+
import { forwardRef, useEffect, useRef } from 'react';
65
import * as THREE from 'three';
76

87
const waveVertexShader = `
@@ -156,9 +155,10 @@ class RetroEffectImpl extends Effect {
156155
}
157156
}
158157

158+
const WrappedRetroEffect = wrapEffect(RetroEffectImpl);
159+
159160
const RetroEffect = forwardRef<RetroEffectImpl, { colorNum: number; pixelSize: number }>((props, ref) => {
160161
const { colorNum, pixelSize } = props;
161-
const WrappedRetroEffect = wrapEffect(RetroEffectImpl);
162162
return <WrappedRetroEffect ref={ref} colorNum={colorNum} pixelSize={pixelSize} />;
163163
});
164164

@@ -311,7 +311,7 @@ export default function Dither({
311311
}: DitherProps) {
312312
return (
313313
<Canvas
314-
className="w-full h-full relative"
314+
className="relative w-full h-full"
315315
camera={{ position: [0, 0, 6] }}
316316
dpr={1}
317317
gl={{ antialias: true, preserveDrawingBuffer: true }}

src/ts-tailwind/Backgrounds/GridDistortion/GridDistortion.tsx

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React, { useRef, useEffect } from 'react';
1+
import React, { useEffect, useRef } from 'react';
22
import * as THREE from 'three';
33

44
interface GridDistortionProps {
@@ -69,7 +69,9 @@ const GridDistortion: React.FC<GridDistortionProps> = ({
6969
renderer.setClearColor(0x000000, 0);
7070
rendererRef.current = renderer;
7171

72-
container.innerHTML = '';
72+
if (container.contains(renderer.domElement)) {
73+
container.removeChild(renderer.domElement);
74+
}
7375
container.appendChild(renderer.domElement);
7476

7577
const camera = new THREE.OrthographicCamera(0, 0, 0, 0, -1000, 1000);
@@ -119,8 +121,6 @@ const GridDistortion: React.FC<GridDistortionProps> = ({
119121
scene.add(plane);
120122

121123
const handleResize = () => {
122-
if (!container || !renderer || !camera) return;
123-
124124
const rect = container.getBoundingClientRect();
125125
const width = rect.width;
126126
const height = rect.height;
@@ -204,10 +204,10 @@ const GridDistortion: React.FC<GridDistortionProps> = ({
204204
console.error('dataTexture.image.data is not a Float32Array');
205205
return;
206206
}
207-
const data: Float32Array = dataTexture.image.data;
207+
const texData: Float32Array = dataTexture.image.data;
208208
for (let i = 0; i < size * size; i++) {
209-
data[i * 4] *= relaxation;
210-
data[i * 4 + 1] *= relaxation;
209+
texData[i * 4] *= relaxation;
210+
texData[i * 4 + 1] *= relaxation;
211211
}
212212

213213
const gridMouseX = size * mouseState.x;
@@ -220,8 +220,8 @@ const GridDistortion: React.FC<GridDistortionProps> = ({
220220
if (distSq < maxDist * maxDist) {
221221
const index = 4 * (i + size * j);
222222
const power = Math.min(maxDist / Math.sqrt(distSq), 10);
223-
data[index] += strength * 100 * mouseState.vX * power;
224-
data[index + 1] -= strength * 100 * mouseState.vY * power;
223+
texData[index] += strength * 100 * mouseState.vX * power;
224+
texData[index + 1] -= strength * 100 * mouseState.vY * power;
225225
}
226226
}
227227
}
@@ -246,18 +246,20 @@ const GridDistortion: React.FC<GridDistortionProps> = ({
246246
container.removeEventListener('mousemove', handleMouseMove);
247247
container.removeEventListener('mouseleave', handleMouseLeave);
248248

249+
scene.clear();
250+
251+
if (geometry) geometry.dispose();
252+
if (material) material.dispose();
253+
if (dataTexture) dataTexture.dispose();
254+
if (uniforms.uTexture.value) uniforms.uTexture.value.dispose();
255+
249256
if (renderer) {
250257
renderer.dispose();
251258
if (container.contains(renderer.domElement)) {
252259
container.removeChild(renderer.domElement);
253260
}
254261
}
255262

256-
if (geometry) geometry.dispose();
257-
if (material) material.dispose();
258-
if (dataTexture) dataTexture.dispose();
259-
if (uniforms.uTexture.value) uniforms.uTexture.value.dispose();
260-
261263
sceneRef.current = null;
262264
rendererRef.current = null;
263265
cameraRef.current = null;

src/ts-tailwind/Backgrounds/GridScan/GridScan.tsx

Lines changed: 33 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1+
import * as faceapi from 'face-api.js';
2+
import { BloomEffect, ChromaticAberrationEffect, EffectComposer, EffectPass, RenderPass } from 'postprocessing';
13
import React, { useEffect, useRef, useState } from 'react';
2-
import { EffectComposer, RenderPass, EffectPass, BloomEffect, ChromaticAberrationEffect } from 'postprocessing';
34
import * as THREE from 'three';
4-
import * as faceapi from 'face-api.js';
55

66
type GridScanProps = {
77
enableWebcam?: boolean;
@@ -360,6 +360,23 @@ export const GridScan: React.FC<GridScanProps> = ({
360360
const MAX_SCANS = 8;
361361
const scanStartsRef = useRef<number[]>([]);
362362

363+
const skewScaleRef = useRef(0);
364+
const tiltScaleRef = useRef(0);
365+
const yawScaleRef = useRef(0);
366+
const depthResponseRef = useRef(0);
367+
const smoothTimeRef = useRef(0);
368+
const yBoostRef = useRef(0);
369+
370+
useEffect(() => {
371+
const s = THREE.MathUtils.clamp(sensitivity, 0, 1);
372+
skewScaleRef.current = THREE.MathUtils.lerp(0.06, 0.2, s);
373+
tiltScaleRef.current = THREE.MathUtils.lerp(0.12, 0.3, s);
374+
yawScaleRef.current = THREE.MathUtils.lerp(0.1, 0.28, s);
375+
depthResponseRef.current = THREE.MathUtils.lerp(0.25, 0.45, s);
376+
smoothTimeRef.current = THREE.MathUtils.lerp(0.45, 0.12, s);
377+
yBoostRef.current = THREE.MathUtils.lerp(1.2, 1.6, s);
378+
}, [sensitivity]);
379+
363380
const pushScan = (t: number) => {
364381
const arr = scanStartsRef.current.slice();
365382
if (arr.length >= MAX_SCANS) arr.shift();
@@ -379,16 +396,8 @@ export const GridScan: React.FC<GridScanProps> = ({
379396
const bufT = useRef<number[]>([]);
380397
const bufYaw = useRef<number[]>([]);
381398

382-
const s = THREE.MathUtils.clamp(sensitivity, 0, 1);
383-
const skewScale = THREE.MathUtils.lerp(0.06, 0.2, s);
384-
const tiltScale = THREE.MathUtils.lerp(0.12, 0.3, s);
385-
const yawScale = THREE.MathUtils.lerp(0.1, 0.28, s);
386-
const depthResponse = THREE.MathUtils.lerp(0.25, 0.45, s);
387-
const smoothTime = THREE.MathUtils.lerp(0.45, 0.12, s);
388399
const maxSpeed = Infinity;
389400

390-
const yBoost = THREE.MathUtils.lerp(1.2, 1.6, s);
391-
392401
useEffect(() => {
393402
const el = containerRef.current;
394403
if (!el) return;
@@ -545,6 +554,8 @@ export const GridScan: React.FC<GridScanProps> = ({
545554
const dt = Math.max(0, Math.min(0.1, (now - last) / 1000));
546555
last = now;
547556

557+
const smoothTime = smoothTimeRef.current;
558+
548559
lookCurrent.current.copy(
549560
smoothDampVec2(lookCurrent.current, lookTarget.current, lookVel.current, smoothTime, maxSpeed, dt)
550561
);
@@ -571,10 +582,12 @@ export const GridScan: React.FC<GridScanProps> = ({
571582
yawCurrent.current = yawSm.value;
572583
yawVel.current = yawSm.v;
573584

574-
const skew = new THREE.Vector2(lookCurrent.current.x * skewScale, -lookCurrent.current.y * yBoost * skewScale);
575-
material.uniforms.uSkew.value.set(skew.x, skew.y);
576-
material.uniforms.uTilt.value = tiltCurrent.current * tiltScale;
577-
material.uniforms.uYaw.value = THREE.MathUtils.clamp(yawCurrent.current * yawScale, -0.6, 0.6);
585+
material.uniforms.uSkew.value.set(
586+
lookCurrent.current.x * skewScaleRef.current,
587+
-lookCurrent.current.y * yBoostRef.current * skewScaleRef.current
588+
);
589+
material.uniforms.uTilt.value = tiltCurrent.current * tiltScaleRef.current;
590+
material.uniforms.uYaw.value = THREE.MathUtils.clamp(yawCurrent.current * yawScaleRef.current, -0.6, 0.6);
578591

579592
material.uniforms.iTime.value = now / 1000;
580593
renderer.clear(true, true, true);
@@ -590,6 +603,7 @@ export const GridScan: React.FC<GridScanProps> = ({
590603
return () => {
591604
if (rafRef.current) cancelAnimationFrame(rafRef.current);
592605
window.removeEventListener('resize', onResize);
606+
scene.clear();
593607
material.dispose();
594608
(quad.geometry as THREE.BufferGeometry).dispose();
595609
if (composerRef.current) {
@@ -599,18 +613,7 @@ export const GridScan: React.FC<GridScanProps> = ({
599613
renderer.dispose();
600614
container.removeChild(renderer.domElement);
601615
};
602-
}, [
603-
sensitivity,
604-
lineThickness,
605-
linesColor,
606-
scanColor,
607-
scanOpacity,
608-
gridScale,
609-
lineStyle,
610-
lineJitter,
611-
scanDirection,
612-
enablePost
613-
]);
616+
}, [lineThickness, linesColor, scanColor, scanOpacity, gridScale, lineStyle, lineJitter, scanDirection, enablePost]);
614617

615618
useEffect(() => {
616619
const m = materialRef.current;
@@ -744,7 +747,7 @@ export const GridScan: React.FC<GridScanProps> = ({
744747
const look = new THREE.Vector2(Math.tanh(nxm), Math.tanh(nym));
745748

746749
const faceSize = Math.min(1, Math.hypot(box.width / vw, box.height / vh));
747-
const depthScale = 1 + depthResponse * (faceSize - 0.25);
750+
const depthScale = 1 + depthResponseRef.current * (faceSize - 0.25);
748751
lookTarget.current.copy(look.multiplyScalar(depthScale));
749752

750753
const leftEye = res.landmarks.getLeftEye();
@@ -799,14 +802,14 @@ export const GridScan: React.FC<GridScanProps> = ({
799802
video.srcObject = null;
800803
}
801804
};
802-
}, [enableWebcam, modelsReady, depthResponse]);
805+
}, [enableWebcam, modelsReady]);
803806

804807
return (
805808
<div ref={containerRef} className={`relative w-full h-full overflow-hidden ${className ?? ''}`} style={style}>
806809
{showPreview && (
807-
<div className="absolute right-3 bottom-3 w-[220px] h-[132px] rounded-lg overflow-hidden border border-white/25 shadow-[0_4px_16px_rgba(0,0,0,0.4)] bg-black text-white text-[12px] leading-[1.2] font-sans pointer-events-none">
810+
<div className="right-3 bottom-3 absolute bg-black shadow-[0_4px_16px_rgba(0,0,0,0.4)] border border-white/25 rounded-lg w-[220px] h-[132px] overflow-hidden font-sans text-[12px] text-white leading-[1.2] pointer-events-none">
808811
<video ref={videoRef} muted playsInline autoPlay className="w-full h-full object-cover -scale-x-100" />
809-
<div className="absolute left-2 top-2 px-[6px] py-[2px] bg-black/50 rounded-[6px] backdrop-blur-[4px]">
812+
<div className="top-2 left-2 absolute bg-black/50 backdrop-blur-[4px] px-[6px] py-[2px] rounded-[6px]">
810813
{enableWebcam
811814
? modelsReady
812815
? uiFaceActive

0 commit comments

Comments
 (0)