gasp动画控制相机的位置变换
关键代码threejs/Cameras.ts
| |
| |
| private changePerspectiveCameraPosition() { |
| |
| gsap.to(this.perspectiveCamera.position, { |
| x: 5, |
| z: 5, |
| duration: 5, |
| }); |
| } |
| |
| |
| |
| |
| private changePerspectiveCameraPositionGroup() { |
| |
| const tl = gsap.timeline(); |
| |
| |
| tl.to(this.perspectiveCamera.position, { |
| x: 5, |
| z: 5, |
| duration: 3, |
| }) |
| .to(this.perspectiveCamera.position, { |
| x: -5, |
| z: 5, |
| duration: 3, |
| }) |
| .to(this.perspectiveCamera.position, { |
| x: 5, |
| z: 5, |
| duration: 3, |
| }); |
| } |
threejs/Cameras.ts
| import { PerspectiveCamera, Scene } from "three"; |
| import Base from "./Base"; |
| import type Size from "./Size"; |
| import { gsap } from "gsap"; |
| |
| export default class Camera { |
| private size: Size; |
| private scene: Scene; |
| private canvas: HTMLCanvasElement; |
| public perspectiveCamera: PerspectiveCamera; |
| |
| constructor() { |
| const base = Base.getInstance(); |
| this.size = base.size; |
| this.scene = base.scene; |
| this.canvas = base.canvas; |
| |
| this.perspectiveCamera = this.createPerspectiveCamera(); |
| this.scene.add(this.perspectiveCamera); |
| |
| this.changePerspectiveCameraPositionGroup(); |
| } |
| |
| |
| private createPerspectiveCamera() { |
| const perspectiveCamera = new PerspectiveCamera( |
| 35, |
| this.size.aspect, |
| 0.1, |
| 1000 |
| ); |
| perspectiveCamera.position.set(0, 10, 0); |
| return perspectiveCamera; |
| } |
| |
| |
| |
| |
| private changePerspectiveCameraPosition() { |
| |
| gsap.to(this.perspectiveCamera.position, { |
| x: 5, |
| z: 5, |
| duration: 5, |
| }); |
| } |
| |
| |
| |
| |
| private changePerspectiveCameraPositionGroup() { |
| |
| const tl = gsap.timeline(); |
| |
| |
| tl.to(this.perspectiveCamera.position, { |
| x: 5, |
| z: 5, |
| duration: 3, |
| }) |
| .to(this.perspectiveCamera.position, { |
| x: -5, |
| z: 5, |
| duration: 3, |
| }) |
| .to(this.perspectiveCamera.position, { |
| x: 5, |
| z: 5, |
| duration: 3, |
| }); |
| } |
| |
| |
| public resize() { |
| |
| this.perspectiveCamera.aspect = this.size.aspect; |
| this.perspectiveCamera.updateProjectionMatrix(); |
| } |
| |
| } |
| |
| |
| |
| |
threejs/Base.ts
| import { Object3D, Scene } from "three"; |
| |
| import Camera from "./Cameras"; |
| import Helper from "./Helpers"; |
| import Renderer from "./Renderer"; |
| import Size from "./Size"; |
| import Time from "./Time"; |
| import Control from "./Controls"; |
| import Lights from "./Lights"; |
| import EventManger from "./Events"; |
| |
| import Box from "../threejs/World/Box"; |
| |
| export default class Base { |
| public static base: Base; |
| public canvas: HTMLCanvasElement; |
| public scene: Scene; |
| public cameras: Camera; |
| public size: Size; |
| public time: Time; |
| public renderer: Renderer; |
| public control: Control; |
| public helper: Helper; |
| public eventManger: EventManger; |
| public noTransfromControlObjs: Object3D[] = []; |
| |
| constructor(canvas: HTMLCanvasElement) { |
| Base.base = this; |
| |
| this.canvas = canvas as HTMLCanvasElement; |
| this.scene = new Scene(); |
| this.size = new Size(); |
| this.cameras = new Camera(); |
| this.time = new Time(); |
| this.renderer = new Renderer(); |
| new Lights(); |
| |
| |
| const helper = new Helper(); |
| this.helper = helper; |
| this.noTransfromControlObjs.push(helper.gridHelper, helper.axesHelper); |
| |
| |
| const control = new Control(); |
| this.control = control; |
| this.noTransfromControlObjs.push(this.control.transformControl); |
| |
| this.eventManger = new EventManger(); |
| |
| |
| this.eventManger.addEventListener("click", (e) => { |
| console.log("点击射线穿过了下面这些物体"); |
| console.log(e); |
| }); |
| |
| |
| new Box(); |
| |
| this.time.on("update", () => { |
| this.update(); |
| }); |
| this.size.on("resize", () => { |
| this.resize(); |
| }); |
| } |
| |
| public static getInstance(canvas?: HTMLCanvasElement) { |
| if (canvas === undefined && Base.base !== undefined) { |
| return Base.base; |
| } |
| Base.base = new Base(canvas as HTMLCanvasElement); |
| return Base.base; |
| } |
| |
| private resize() { |
| this.cameras.resize(); |
| this.renderer.resize(); |
| } |
| |
| private update() { |
| this.control.update(); |
| this.renderer.update(); |
| } |
| } |
| |
| |
threejs/Helpers.ts
| import { AxesHelper, GridHelper } from "three"; |
| import Base from "./Base"; |
| |
| export default class { |
| public axesHelper: AxesHelper; |
| public gridHelper: GridHelper; |
| constructor() { |
| const base = Base.getInstance(); |
| const axesHelper = new AxesHelper(5); |
| this.axesHelper = axesHelper; |
| base.scene.add(axesHelper); |
| |
| const size = 10; |
| const divisions = 10; |
| |
| const gridHelper = new GridHelper(size, divisions); |
| this.gridHelper = gridHelper; |
| base.scene.add(gridHelper); |
| } |
| } |
| |
threejs/Lights.ts
| import { AmbientLight, DirectionalLight } from "three"; |
| import Base from "./Base"; |
| |
| export default class Environment { |
| private sunLight: DirectionalLight; |
| private ambientLight: AmbientLight; |
| constructor() { |
| const base = Base.getInstance(); |
| this.sunLight = new DirectionalLight("#ffffff", 2); |
| this.sunLight.castShadow = true; |
| this.sunLight.shadow.camera.far = 20; |
| this.sunLight.shadow.mapSize.set(2048, 2048); |
| this.sunLight.shadow.normalBias = 0.05; |
| |
| this.sunLight.position.set(-1.5, 7, 10); |
| base.scene.add(this.sunLight); |
| |
| this.ambientLight = new AmbientLight("#ffffff", 0.1); |
| base.scene.add(this.ambientLight); |
| } |
| } |
| |
threejs/Size.ts
| import EventEmitter from "eventemitter2"; |
| export default class Size extends EventEmitter { |
| public width: number; |
| public height: number; |
| public aspect: number; |
| public pixelRatio: number; |
| |
| constructor() { |
| super(); |
| this.width = window.innerWidth; |
| this.height = window.innerHeight; |
| this.aspect = this.width / this.height; |
| this.pixelRatio = Math.min(window.devicePixelRatio, 2); |
| |
| window.addEventListener("resize", () => { |
| this.width = window.innerWidth; |
| this.height = window.innerHeight; |
| this.aspect = this.width / this.height; |
| this.pixelRatio = Math.min(window.devicePixelRatio, 2); |
| this.emit("resize"); |
| }); |
| } |
| } |
| |
threejs/Events.ts
| import { |
| AxesHelper, |
| Camera, |
| EventDispatcher, |
| GridHelper, |
| Line, |
| Object3D, |
| Raycaster, |
| Scene, |
| Vector2, |
| } from "three"; |
| import { |
| TransformControlsGizmo, |
| TransformControlsPlane, |
| } from "three/examples/jsm/controls/TransformControls"; |
| import Base from "./Base"; |
| |
| export default class EventManger extends EventDispatcher { |
| private base: Base; |
| private raycaster: Raycaster = new Raycaster(); |
| private mouse: Vector2 = new Vector2(); |
| private dom: HTMLCanvasElement; |
| private scene: Scene; |
| private camera: Camera; |
| constructor() { |
| super(); |
| const base = Base.getInstance(); |
| this.base = base; |
| |
| this.dom = base.canvas; |
| this.scene = base.scene; |
| this.camera = base.cameras.perspectiveCamera; |
| |
| const mouse = this.mouse; |
| const raycaster = this.raycaster; |
| const dom = this.dom; |
| |
| let cacheObject: Object3D | null = null; |
| |
| dom.addEventListener("mousedown", (event) => { |
| |
| raycaster.setFromCamera(mouse, this.camera); |
| const intersection = raycaster.intersectObjects(this.scene.children); |
| |
| |
| this.dispatchEvent({ |
| type: "mousedown", |
| intersection, |
| }); |
| |
| if (intersection.length) { |
| const object = intersection[0].object; |
| |
| object.dispatchEvent({ |
| type: "mousedown", |
| }); |
| } |
| }); |
| |
| dom.addEventListener("mousemove", (event) => { |
| mouse.x = (event.offsetX / dom.offsetWidth) * 2 - 1; |
| mouse.y = (-event.offsetY * 2) / dom.offsetHeight + 1; |
| |
| |
| raycaster.setFromCamera(mouse, this.camera); |
| |
| const intersection = raycaster.intersectObjects(this.scene.children); |
| |
| |
| this.dispatchEvent({ |
| type: "mousemove", |
| intersection, |
| }); |
| |
| if (intersection.length) { |
| const object = intersection[0].object; |
| |
| if (object !== cacheObject) { |
| if (cacheObject) { |
| cacheObject.dispatchEvent({ |
| type: "mouseleave", |
| }); |
| } |
| object.dispatchEvent({ |
| type: "mouseenter", |
| }); |
| } else if (object === cacheObject) { |
| object.dispatchEvent({ |
| type: "mousemove", |
| }); |
| } |
| cacheObject = object; |
| } else { |
| if (cacheObject) { |
| cacheObject.dispatchEvent({ |
| type: "mouseleave", |
| }); |
| } |
| cacheObject = null; |
| } |
| }); |
| |
| dom.addEventListener("mouseup", (event) => { |
| |
| raycaster.setFromCamera(mouse, this.camera); |
| const intersection = raycaster.intersectObjects(this.scene.children); |
| |
| this.dispatchEvent({ |
| type: "mouseup", |
| intersection, |
| }); |
| if (intersection.length) { |
| const object = intersection[0].object; |
| object.dispatchEvent({ |
| type: "mouseup", |
| }); |
| } |
| }); |
| |
| dom.addEventListener("click", (event) => { |
| |
| raycaster.setFromCamera(mouse, this.camera); |
| let intersections = raycaster.intersectObjects(this.scene.children); |
| |
| this.dispatchEvent({ |
| type: "click", |
| intersections, |
| }); |
| |
| |
| this.scene.remove(...this.base.noTransfromControlObjs); |
| intersections = raycaster.intersectObjects(this.scene.children); |
| |
| this.scene.add(...this.base.noTransfromControlObjs); |
| |
| const targetIntersection = intersections.filter((intersection) => { |
| return ( |
| |
| !(intersection.object instanceof TransformControlsPlane) && |
| !(intersection.object instanceof AxesHelper) && |
| !(intersection.object instanceof GridHelper) && |
| !(intersection.object instanceof Line) && |
| !(intersection.object.parent instanceof TransformControlsGizmo) |
| |
| ); |
| }); |
| |
| if (targetIntersection.length) { |
| const object = targetIntersection[0].object; |
| |
| console.log(object); |
| |
| object.dispatchEvent({ |
| type: "click", |
| }); |
| } |
| }); |
| } |
| } |
| |
threejs/Renderer.ts
| import * as THREE from "three"; |
| import Base from "./Base"; |
| import { Scene, WebGLRenderer } from "three"; |
| import Camera from "./Cameras"; |
| import Size from "./Size"; |
| import { CSS2DRenderer } from "three/examples/jsm/renderers/CSS2DRenderer"; |
| |
| export default class Renderer { |
| private size: Size; |
| private scene: Scene; |
| private canvas: HTMLCanvasElement; |
| private camera: Camera; |
| private labelRenderer: CSS2DRenderer; |
| private renderer: WebGLRenderer; |
| |
| constructor() { |
| const base = Base.getInstance(); |
| |
| this.size = base.size; |
| this.scene = base.scene; |
| this.canvas = base.canvas; |
| this.camera = base.cameras; |
| |
| this.renderer = this.setRenderer(); |
| |
| const labelRenderer = this.setLabelRenderer(); |
| this.labelRenderer = labelRenderer; |
| } |
| |
| setRenderer() { |
| const renderer = new WebGLRenderer({ |
| canvas: this.canvas, |
| antialias: true, |
| }); |
| |
| renderer.physicallyCorrectLights = true; |
| renderer.outputEncoding = THREE.sRGBEncoding; |
| renderer.toneMapping = THREE.CineonToneMapping; |
| renderer.toneMappingExposure = 1.75; |
| renderer.shadowMap.enabled = true; |
| renderer.shadowMap.type = THREE.PCFSoftShadowMap; |
| renderer.setSize(this.size.width, this.size.height); |
| renderer.setPixelRatio(this.size.pixelRatio); |
| |
| return renderer; |
| } |
| private setLabelRenderer() { |
| const labelRenderer = new CSS2DRenderer(); |
| labelRenderer.setSize(this.size.width, this.size.height); |
| document.body.appendChild(labelRenderer.domElement); |
| return labelRenderer; |
| } |
| |
| resize() { |
| this.renderer.setSize(this.size.width, this.size.height); |
| this.renderer.setPixelRatio(this.size.pixelRatio); |
| |
| this.labelRenderer.setSize(this.size.width, this.size.height); |
| } |
| |
| update() { |
| this.renderer.render(this.scene, this.camera.perspectiveCamera); |
| this.labelRenderer.render(this.scene, this.camera.perspectiveCamera); |
| } |
| } |
| |
threejs/Controls.ts
| import { |
| TransformControls, |
| TransformControlsGizmo, |
| TransformControlsPlane, |
| } from "three/examples/jsm/controls/TransformControls"; |
| import Base from "./Base"; |
| import { |
| AxesHelper, |
| GridHelper, |
| Line, |
| MOUSE, |
| Object3D, |
| PerspectiveCamera, |
| Raycaster, |
| Scene, |
| Vector2, |
| } from "three"; |
| import { OrbitControls } from "three/examples/jsm/controls/OrbitControls"; |
| |
| export default class Control { |
| public transformControl: TransformControls; |
| public orbitControl: OrbitControls; |
| private base: Base; |
| private camera: PerspectiveCamera; |
| private canvas: HTMLCanvasElement; |
| private scene: Scene; |
| private transing: boolean; |
| constructor() { |
| const base = Base.getInstance(); |
| this.base = base; |
| |
| this.camera = base.cameras.perspectiveCamera; |
| this.canvas = base.canvas; |
| this.scene = base.scene; |
| |
| |
| this.transing = false; |
| this.transformControl = this.createTransformControl(); |
| this.initTransformControlEvent(); |
| this.scene.add(this.transformControl); |
| this.orbitControl = this.createOrbitControl(); |
| } |
| |
| private createTransformControl() { |
| const transformControls = new TransformControls(this.camera, this.canvas); |
| return transformControls; |
| } |
| |
| private initTransformControlEvent() { |
| this.transformControl.addEventListener("mouseDown", (e) => { |
| this.transing = true; |
| }); |
| } |
| |
| public update() { |
| this.orbitControl.update(); |
| } |
| |
| private createOrbitControl() { |
| const controls = new OrbitControls(this.camera, this.canvas); |
| controls.enableDamping = true; |
| controls.enableZoom = true; |
| |
| |
| |
| |
| |
| |
| |
| return controls; |
| } |
| } |
| |
| |
threejs/Time.ts
| import EventEmitter from "eventemitter2"; |
| import { Clock } from "three"; |
| |
| export default class Time extends EventEmitter { |
| public clock: Clock; |
| constructor() { |
| super(); |
| this.clock = new Clock(); |
| |
| this.update(); |
| } |
| |
| update() { |
| this.emit("update"); |
| window.requestAnimationFrame(() => this.update()); |
| } |
| } |
| |
threejs/World/Box.ts
| import { |
| BoxGeometry, |
| MeshBasicMaterial, |
| Mesh, |
| MeshLambertMaterial, |
| Color, |
| } from "three"; |
| import Base from "../Base"; |
| |
| export default class Box { |
| private cube: Mesh; |
| private cubeMaterial: MeshBasicMaterial; |
| |
| constructor() { |
| const base = Base.getInstance(); |
| this.cubeMaterial = new MeshBasicMaterial({ color: 0x00ff00 }); |
| const cube = this.createCube(); |
| this.cube = cube; |
| base.scene.add(cube); |
| } |
| private createCube() { |
| const cube = new Mesh(new BoxGeometry(1, 1, 1), this.cubeMaterial); |
| cube.position.y = 0.5; |
| return cube; |
| } |
| } |
| |
| |
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 清华大学推出第四讲使用 DeepSeek + DeepResearch 让科研像聊天一样简单!
· 推荐几款开源且免费的 .NET MAUI 组件库
· 实操Deepseek接入个人知识库
· 易语言 —— 开山篇
· Trae初体验
2022-10-19 以三角形的形式遍历二维数组