为了让动画更灵活并且简单 借助gsap
让其具有更多可能,在未来更容易扩充其他动效
gsap Dom跟随鼠标移动 gsap.quickTo()
首先要监听鼠标移动,并且将移动的值转换到 -1 和 1 之间 方便处理
private mousemove(e: MouseEvent) {const x = (e.clientX / innerWidth) * 2 - 1;const y = (e.clientY / innerHeight) * 2 - 1;}
这样将 位置 / 屏幕宽高
将值缩放在 0 和 1
之间
然后通过 乘2减1
将其限制在-1 和 1
之间
private mousemove(e: MouseEvent) {const x = ((e.clientX / innerWidth) * 2 - 1) * 2 - 1;const y = ((e.clientY / innerHeight) * 2 - 1) * 2 - 1;}
在three中y轴 上面是1下面是-1 而我们窗口上面是-1 下面是1 所以取y轴剩余的高度让两者行为统一,也就是实现向着鼠标方向
private mousemove(e: MouseEvent) {const x = ((e.clientX / innerWidth) * 2 - 1) * 2 - 1;const y = (((innerHeight - e.clientY) / innerHeight) * 2 - 1) * 2 - 1;}
如此 我们获取了鼠标移动的增量,将这一向量加在camera的x和y轴即可,这里不能使用+=
这样会越来越偏 所以保存相机的原始位置。那么当前鼠标所在相机的位置,应当是原始位置加上鼠标移动的增量
这里就可以使用gsap来控制position变化
private xQuickTo = gsap.quickTo(this.camera.position, "x", {duration: 0.5,
});
上述代码 this.camera.position
的 .x
经过0.5秒后变化到传入值,如:this.xQuickTo(this.cameraPosition.x + x)
class Shake {cameraPosition = this.camera.position.clone();private xQuickTo = gsap.quickTo(this.camera.position, "x", {duration: 0.5,});private yQuickTo = gsap.quickTo(this.camera.position, "y", {duration: 0.5,});private mousemove(e: MouseEvent) {const x = ((e.clientX / innerWidth) * 2 - 1) / this.amplitude;const y =(((innerHeight - e.clientY) / innerHeight) * 2 - 1) /this.amplitude;this.xQuickTo(this.cameraPosition.x + x).play();this.yQuickTo(this.cameraPosition.y + y).play();}}
如此 核心逻辑便完成,丰富事件监听并且加入振幅,控制相机移动范围 后完成这个class
export class Shake {/** 振幅 鼠标晃动的影响 */amplitude = 1;cameraPosition = this.camera.position.clone();private xQuickTo = gsap.quickTo(this.camera.position, "x", {duration: 0.5,});private yQuickTo = gsap.quickTo(this.camera.position, "y", {duration: 0.5,});constructor(public camera: THREE.Camera, public domElement: HTMLElement) {this.domElement.addEventListener("mousemove", this.selfMouseMove);}private mousemove(e: MouseEvent) {const x = ((e.clientX / innerWidth) * 2 - 1) / this.amplitude;const y =(((innerHeight - e.clientY) / innerHeight) * 2 - 1) /this.amplitude;this.xQuickTo(this.cameraPosition.x + x)this.yQuickTo(this.cameraPosition.y + y)}private selfMouseMove = (e: MouseEvent) => this.mousemove.call(this, e);destroyMouseMove() {this.domElement.removeEventListener("mousemove", this.selfMouseMove);}
}
接下来可以扩充一下功能, 加入鼠标按下时可以拖拽旋转,鼠标松开后回到原来的位置
为了支持这些功能 需要给shake增加暂停动画的能力
export class Shake {pause = false;/** 振幅 鼠标晃动的影响 */amplitude = 1;cameraPosition = this.camera.position.clone();xQuickTo = gsap.quickTo(this.camera.position, "x", {duration: 0.5,});yQuickTo = gsap.quickTo(this.camera.position, "y", {duration: 0.5,});private yQuickToTween: gsap.core.Tween | undefined;private xQuickToTween: gsap.core.Tween | undefined;point = new Vector2();constructor(public camera: THREE.Camera, public domElement: HTMLElement) {this.domElement.addEventListener("mousemove", this.selfMouseMove);}private mousemove(e: MouseEvent) {if (this.pause) {this.xQuickToTween && this.xQuickToTween.pause();this.yQuickToTween && this.yQuickToTween.pause();return;}// -1 ~ 1const x = ((e.clientX / innerWidth) * 2 - 1) / this.amplitude;const y =(((innerHeight - e.clientY) / innerHeight) * 2 - 1) /this.amplitude;this.point.set(x, y);this.xQuickToTween = this.xQuickTo(this.cameraPosition.x + x);this.yQuickToTween = this.yQuickTo(this.cameraPosition.y + y);}private selfMouseMove = (e: MouseEvent) => this.mousemove.call(this, e);destroyMouseMove() {this.domElement.removeEventListener("mousemove", this.selfMouseMove);}
}
export class CameraShake extends Shake {constructor(...params: ConstructorParameters<typeof Shake>) {super(...params);this.domElement.addEventListener("mousedown", this.selfMouseDown);}mousedown() {if (this.pause) return;this.pause = true;document.addEventListener("mouseup", this.selfMouseUp, { once: true });}mouseup() {const { x, y, z } = this.cameraPosition;gsap.to(this.camera.position, {x: x + this.point.x,y: y + this.point.y,z,duration: 0.8,onComplete: () => {this.pause = false;},});}selfMouseDown = () => this.mousedown.call(this);selfMouseUp = () => this.mouseup.call(this);destroyMouseEvent() {this.destroyMouseMove();this.domElement.removeEventListener("mousedown", this.selfMouseDown);}
}
ok 上述代码将支持轨道控制器 旋转回来能力