Last active
December 30, 2023 13:05
-
-
Save Bendzae/47d7be8762d8418e9f990c6cf30c2225 to your computer and use it in GitHub Desktop.
Three.js camera movement on scroll
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Three JS scene component | |
const Scene = () => { | |
const camPositionKeyframes = new Map([ | |
[0, new Vector3(0,1000,0)], | |
[0.5, new Vector3(500,1000,-200)], | |
[1.0, new Vector3(1000,1000,400)], | |
[1.5, new Vector3(1500,1000,-800)], | |
]; | |
const lookTargetKeyframes = new Map([ | |
[0, new Vector3(0,0,0)], | |
[0.5, new Vector3(500,0,-200)], | |
[1.0, new Vector3(1000,0,400)], | |
[1.5, new Vector3(1500,0,-800)], | |
]; | |
let lookTarget = lookTargetKeyframes[0].clone(); | |
const zoomKeyFrames = new Map([ | |
[0, 0.25], | |
[0.5, 0.6], | |
[1.0, 0.2], | |
[1.5, 0.6], | |
] | |
]); | |
useFrame((state, delta) => { | |
const scroll = window.scrollY / document.documentElement.clientHeight; | |
updateCameraZoom(scroll, state, delta, zoomKeyFrames); | |
updateCameraMovement( | |
scroll, | |
camPositionKeyframes, | |
lookTargetKeyframes, | |
state, | |
lookTarget, | |
delta | |
); | |
return (...) | |
} | |
function updateCameraZoom( | |
scroll: number, | |
state: RootState, | |
delta: number, | |
zoomKeyFrames: Map<number, number> | |
) { | |
const zoom = getInterpolatedValue<number>( | |
scroll, | |
zoomKeyFrames, | |
(v0, v1, t) => lerp(v0, v1, t) | |
); | |
easing.damp(state.camera, 'zoom', zoom, 0.3, delta); | |
} | |
function updateCameraMovement( | |
scroll: number, | |
camPositionKeys: Map<number, Vector3>, | |
camTargetKeys: Map<number, Vector3>, | |
state: RootState, | |
lookTarget: Vector3, | |
delta: number | |
) { | |
let p = getInterpolatedValue<Vector3>(scroll, camPositionKeys, (v0, v1, t) => | |
v0.clone().lerp(v1, t) | |
); | |
let look = getInterpolatedValue<Vector3>(scroll, camTargetKeys, (v0, v1, t) => | |
v0.clone().lerp(v1, t) | |
); | |
if (state.camera.position.distanceTo(p) > 1) { | |
easing.damp3(state.camera.position, p, 0.3, delta); | |
} else { | |
state.camera.position.set(p.x, p.y, p.z); | |
} | |
if (look.distanceTo(lookTarget) > 1) { | |
easing.damp3(lookTarget, look, 0.3, delta); | |
} else { | |
lookTarget = look; | |
} | |
state.camera.lookAt(lookTarget); | |
state.camera.updateProjectionMatrix(); | |
} | |
function getInterpolatedValue<T>( | |
time: number, | |
keyFrames: Map<number, T>, | |
interpolate: (v0: T, v1: T, t: number) => T | |
): T { | |
const keys = Array.from(keyFrames.keys()); | |
let k1 = keys.findIndex((k) => k > time); | |
k1 = k1 < 0 ? keys.length - 1 : k1; | |
const k0 = Math.max(k1 - 1, 0); | |
const k0_val = keys[k0]; | |
const k1_val = keys[k1]; | |
const distance = k1_val - k0_val; | |
const t = clamp((time - k0_val) / distance, 0, 1); | |
return interpolate(keyFrames.get(k0_val)!, keyFrames.get(k1_val)!, t); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment