Skip to content

Commit 7919007

Browse files
author
takuma-hmng8
committed
12.11
1 parent f99827d commit 7919007

File tree

11 files changed

+193
-112
lines changed

11 files changed

+193
-112
lines changed

app/domSyncer/DomSyncer.tsx

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import * as THREE from "three";
2-
import { useEffect, useRef } from "react";
2+
import { useLayoutEffect, useRef } from "react";
33
import { useFrame, extend, useThree, useLoader } from "@react-three/fiber";
44
import { FxMaterial, FxMaterialProps } from "@/utils/fxMaterial";
55
import {
@@ -10,11 +10,6 @@ import {
1010

1111
extend({ FxMaterial });
1212

13-
/*===============================================
14-
TODO*
15-
- 角丸
16-
===============================================*/
17-
1813
const CONSTANT = {
1914
textureResolution: new THREE.Vector2(1440, 1029),
2015
};
@@ -29,35 +24,45 @@ export const DomSyncer = ({ state }: { state: number }) => {
2924

3025
const [updateNoise] = useNoise({ size, dpr });
3126
const [updateTransitionBg] = useTransitionBg({ size, dpr });
32-
const [updateDomSyncer] = useDomSyncer({ size, dpr }, [state]);
3327

3428
const domArr = useRef<(HTMLElement | Element)[]>([]);
35-
useEffect(() => {
29+
30+
const [updateDomSyncer, setDomSyncer, domSyncerObj] = useDomSyncer(
31+
{ size, dpr },
32+
[state]
33+
);
34+
useLayoutEffect(() => {
3635
if (state === 0) {
3736
domArr.current = [...document.querySelectorAll(".item")!];
3837
} else {
3938
domArr.current = [...document.querySelectorAll(".item2")!];
4039
}
41-
}, [state]);
40+
setDomSyncer({
41+
dom: domArr.current,
42+
boderRadius: [...Array(domArr.current.length)].map((_, i) => i * 50.0),
43+
});
44+
}, [state, setDomSyncer, momo]);
4245

46+
const resolutionRef = useRef(new THREE.Vector2(0, 0));
4347
useFrame((props) => {
4448
const noise = updateNoise(props);
4549
const fx = updateTransitionBg(props, {
4650
noiseMap: noise,
4751
textureResolution: CONSTANT.textureResolution,
4852
texture0: momo,
4953
texture1: momo,
50-
noiseStrength: 0.0,
54+
noiseStrength: 0.05,
5155
});
5256

5357
const syncedTexture = updateDomSyncer(props, {
54-
dom: domArr.current,
5558
texture: [...Array(domArr.current.length)].map(() => fx),
56-
resolution: [...Array(domArr.current.length)].map(
57-
() => CONSTANT.textureResolution
59+
resolution: [...Array(domArr.current.length)].map(() =>
60+
resolutionRef.current.set(props.size.width, props.size.height)
5861
),
5962
});
6063

64+
// console.log(domSyncerObj.isIntersecting(-1, false));
65+
6166
const main = mainShaderRef.current;
6267
if (main) {
6368
main.u_fx = syncedTexture;

app/domSyncer/page.tsx

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ export default function Page() {
3030
position: "fixed",
3131
bottom: 0,
3232
right: 0,
33+
zIndex: 1000,
34+
cursor: "pointer",
3335
}}>
3436
stateの切り替え
3537
</button>
@@ -49,7 +51,10 @@ export default function Page() {
4951
key={i}
5052
style={{
5153
width: "calc(50% - 40px)",
52-
height: "120vh",
54+
height: "800px",
55+
border: "8px solid #000",
56+
zIndex: 100,
57+
borderRadius: `${i * 50}px`,
5358
}}></div>
5459
))}
5560
</>
@@ -61,7 +66,8 @@ export default function Page() {
6166
key={i}
6267
style={{
6368
width: "100%",
64-
height: "120vh",
69+
height: "80vh",
70+
zIndex: 100,
6571
}}></div>
6672
))}
6773
</>

packages/use-shader-fx/src/hooks/useDomSyncer/index.ts

Lines changed: 33 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -5,27 +5,36 @@ import { RootState, Size } from "@react-three/fiber";
55
import { useSingleFBO } from "../../utils/useSingleFBO";
66
import { HooksReturn } from "../types";
77
import { useParams } from "../../utils/useParams";
8-
import { errorHandling } from "./utils/errorHandling";
8+
import { errorHandler } from "./utils/errorHandler";
99
import { createMesh } from "./utils/createMesh";
10-
import { intersectionHandler } from "./utils/intersectionHandler";
10+
import { useIntersectionHandler } from "./utils/useIntersectionHandler";
1111
import { updateRect } from "./utils/updateRect";
12+
import { useIsIntersecting, IsIntersecting } from "./utils/useIsIntersecting";
1213

1314
export type DomSyncerParams = {
14-
texture: THREE.Texture[];
15-
dom: (HTMLElement | Element | null)[];
15+
texture?: THREE.Texture[];
16+
dom?: (HTMLElement | Element | null)[];
1617
resolution?: THREE.Vector2[];
18+
boderRadius?: number[];
1719
};
1820

1921
export type DomSyncerObject = {
2022
scene: THREE.Scene;
2123
camera: THREE.Camera;
2224
renderTarget: THREE.WebGLRenderTarget;
25+
/**
26+
* The syncing DOM also returns a crossing decision.
27+
* @param index - Index of the dom for which you want to return an intersection decision. -1 will return the entire array.
28+
* @param once - If set to true, it will continue to return true once crossed.
29+
*/
30+
isIntersecting: IsIntersecting;
2331
};
2432

2533
export const DOMSYNCER_PARAMS: DomSyncerParams = {
2634
texture: [],
2735
dom: [],
2836
resolution: [],
37+
boderRadius: [],
2938
};
3039

3140
/**
@@ -51,53 +60,44 @@ export const useDomSyncer = (
5160
});
5261
const [params, setParams] = useParams<DomSyncerParams>(DOMSYNCER_PARAMS);
5362

54-
// dependenciesをtriggerして、meshと
63+
// Avoid instancing vec2 every frame
64+
const resolutionRef = useRef<THREE.Vector2>(new THREE.Vector2(0, 0));
65+
66+
// Update monitored doms according to the dependency array
5567
const [refreshTrigger, setRefreshTrigger] = useState(true);
5668
useEffect(() => {
5769
setRefreshTrigger(true);
5870
// eslint-disable-next-line react-hooks/exhaustive-deps
5971
}, dependencies);
6072

61-
const resolutionRef = useRef<THREE.Vector2>(new THREE.Vector2(0, 0));
62-
const intersectionObserverRef = useRef<IntersectionObserver[]>([]);
63-
const intersectionDomRef = useRef<(HTMLElement | Element | null)[]>([]);
64-
const isIntersectingRef = useRef<boolean[]>([]);
73+
const intersectionHandler = useIntersectionHandler();
74+
const { isIntersectingOnceRef, isIntersectingRef, isIntersecting } =
75+
useIsIntersecting();
6576

6677
const updateFx = useCallback(
6778
(props: RootState, updateParams?: DomSyncerParams) => {
6879
const { gl, size } = props;
6980

7081
updateParams && setParams(updateParams);
7182

72-
/*===============================================
73-
エラーハンドリング
74-
===============================================*/
75-
errorHandling(params);
83+
errorHandler(params);
7684

77-
/*===============================================
78-
最初の1回だけ、materialを生成して、sceneに渡す
79-
===============================================*/
8085
if (refreshTrigger) {
8186
createMesh({
8287
params,
8388
size,
84-
resolutionRef,
8589
scene,
8690
});
8791

8892
intersectionHandler({
89-
intersectionObserverRef,
90-
intersectionDomRef,
9193
isIntersectingRef,
94+
isIntersectingOnceRef,
9295
params,
9396
});
9497

9598
setRefreshTrigger(false);
9699
}
97100

98-
/*===============================================
99-
rectを更新する
100-
===============================================*/
101101
updateRect({
102102
params,
103103
size,
@@ -108,7 +108,16 @@ export const useDomSyncer = (
108108

109109
return updateRenderTarget(gl);
110110
},
111-
[updateRenderTarget, setParams, refreshTrigger, scene, params]
111+
[
112+
updateRenderTarget,
113+
setParams,
114+
intersectionHandler,
115+
refreshTrigger,
116+
scene,
117+
params,
118+
isIntersectingOnceRef,
119+
isIntersectingRef,
120+
]
112121
);
113122

114123
return [
@@ -118,6 +127,7 @@ export const useDomSyncer = (
118127
scene: scene,
119128
camera: camera,
120129
renderTarget: renderTarget,
130+
isIntersecting: isIntersecting,
121131
},
122132
];
123133
};

packages/use-shader-fx/src/hooks/useDomSyncer/shader/main.frag

Lines changed: 28 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,34 @@ varying vec2 vUv;
44
uniform sampler2D u_texture;
55
uniform vec2 u_textureResolution;
66
uniform vec2 u_resolution;
7+
uniform float u_borderRadius;
78

89
void main() {
9-
vec2 ratio=vec2(
10-
min((u_resolution.x/u_resolution.y)/(u_textureResolution.x/u_textureResolution.y),1.),
11-
min((u_resolution.y/u_resolution.x)/(u_textureResolution.y/u_textureResolution.x),1.)
10+
// texuture color
11+
vec2 ratio = vec2(
12+
min((u_resolution.x / u_resolution.y) / (u_textureResolution.x / u_textureResolution.y), 1.0),
13+
min((u_resolution.y / u_resolution.x) / (u_textureResolution.y / u_textureResolution.x), 1.0)
1214
);
13-
vec2 uv=vec2(
14-
vUv.x*ratio.x+(1.-ratio.x)*.5,
15-
vUv.y*ratio.y+(1.-ratio.y)*.5
16-
);
17-
gl_FragColor = texture2D(u_texture, uv);
18-
}
15+
vec2 adjustedUv = vUv * ratio + (1.0 - ratio) * 0.5;
16+
vec3 textureColor = texture2D(u_texture, adjustedUv).rgb;
17+
18+
// Based on https://mofu-dev.com/en/blog/three-dom-alignment/
19+
float maxSide = max(u_resolution.x, u_resolution.y);
20+
float minSide = min(u_resolution.x, u_resolution.y);
21+
vec2 aspect = u_resolution / maxSide;
22+
vec2 alphaUv = vUv - 0.5;
23+
24+
float borderRadius = min(u_borderRadius, minSide * 0.5);
25+
vec2 offset = vec2(borderRadius) / u_resolution;
26+
vec2 alphaXY = smoothstep(vec2(0.5 - offset), vec2(0.5 - offset - 0.001), abs(alphaUv));
27+
float alpha = min(1.0, alphaXY.x + alphaXY.y);
28+
29+
vec2 alphaUv2 = abs(vUv - 0.5);
30+
float radius = borderRadius / maxSide;
31+
alphaUv2 = (alphaUv2 - 0.5) * aspect + radius;
32+
float roundAlpha = smoothstep(radius + 0.001, radius, length(alphaUv2));
33+
34+
alpha = min(1.0, alpha + roundAlpha);
35+
36+
gl_FragColor = vec4(textureColor, alpha);
37+
}

packages/use-shader-fx/src/hooks/useDomSyncer/utils/createMesh.ts

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,12 @@ import fragmentShader from "../shader/main.frag";
77
export const createMesh = ({
88
params,
99
size,
10-
resolutionRef,
1110
scene,
1211
}: {
1312
params: DomSyncerParams;
1413
size: Size;
15-
resolutionRef: React.MutableRefObject<THREE.Vector2>;
1614
scene: THREE.Scene;
1715
}) => {
18-
// clean up
1916
if (scene.children.length > 0) {
2017
scene.children.forEach((child) => {
2118
if (child instanceof THREE.Mesh) {
@@ -26,13 +23,7 @@ export const createMesh = ({
2623
scene.remove(...scene.children);
2724
}
2825

29-
// add mesh to scene
30-
params.texture.forEach((texture, i) => {
31-
// If texture resolution is null, use size
32-
const textureResolution = params.resolution![i]
33-
? params.resolution![i]
34-
: new THREE.Vector2(size.width, size.height);
35-
26+
params.texture!.forEach((texture, i) => {
3627
const mesh = new THREE.Mesh(
3728
new THREE.PlaneGeometry(1, 1),
3829
new THREE.ShaderMaterial({
@@ -41,8 +32,11 @@ export const createMesh = ({
4132
transparent: true,
4233
uniforms: {
4334
u_texture: { value: texture },
44-
u_textureResolution: { value: textureResolution },
45-
u_resolution: { value: resolutionRef.current },
35+
u_textureResolution: { value: new THREE.Vector2(0, 0) },
36+
u_resolution: { value: new THREE.Vector2(0, 0) },
37+
u_borderRadius: {
38+
value: params.boderRadius![i] ? params.boderRadius![i] : 0.0,
39+
},
4640
},
4741
})
4842
);
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import { DomSyncerParams } from "..";
2+
3+
export const errorHandler = (params: DomSyncerParams) => {
4+
const domLength = params.dom?.length;
5+
const textureLength = params.texture?.length;
6+
const resolutionLength = params.resolution?.length;
7+
8+
if (!domLength || !textureLength || !resolutionLength) {
9+
throw new Error("No dom or texture or resolution is set");
10+
}
11+
12+
if (domLength !== textureLength || domLength !== resolutionLength) {
13+
throw new Error("Match dom, texture and resolution length");
14+
}
15+
};

packages/use-shader-fx/src/hooks/useDomSyncer/utils/errorHandling.ts

Lines changed: 0 additions & 10 deletions
This file was deleted.

packages/use-shader-fx/src/hooks/useDomSyncer/utils/intersectionHandler.ts

Lines changed: 0 additions & 39 deletions
This file was deleted.

0 commit comments

Comments
 (0)