threejs官網
https://threejs.org/docs/index.html#manual/zh/introduction/Installation (官網非常的詳細)
匯入安裝
npm install three (下載安裝threejs)
然後就可以在專案中匯入threejs
import * as THREE from 'three'
建立場景和相機
就是需要一個場景來呈現 3D效果 相機是 對於在哪個位置來觀察這個場景中的3D效果(這裡用到的Vue2)
<script> import * as THREE from 'three' export default { camera: null, //相機物件 scene: null, //場景物件 renderer: null, //渲染器物件 mounted () { this.init() }, methods: { init () { // 先建立一個場景 this.scene = new THREE.Scene(); // 建立一個相機 this.camera = new THREE.PerspectiveCamera( // 第一個引數是角度 75° 75, // 第二個引數傳入寬高比 // window.innerWidth / window.innerHeight, 600 / 600, // 近端 0.1, // 遠端 1000 ) // 建立相機定位 set 設定相機位置 x y z this.camera.position.set(0, 0, 10) // 把相機新增到場景 this.scene.add(this.camera) // 建立一個物體 引數是寬高比 一樣的大小 const cubeGeometry = new THREE.BoxGeometry(1, 1, 1) // 建立物體材質 const cubeMaterial = new THREE.MeshBasicMaterial({ color: 0Xffff00 }) // 根據幾何體和材質建立物體 引數一是物體體 引數二是材質 const cube = new THREE.Mesh(cubeGeometry, cubeMaterial) // 將幾何體新增到場景 this.scene.add(cube) //初始化渲染器 this.renderer = new THREE.WebGLRenderer() // 設定渲染的尺寸大小 可以填螢幕大小 引數是寬高 this.renderer.setSize(600, 600) // 其實現在的renderer就是畫布 把畫板的dom渲染到畫布上 document.querySelector('#container').appendChild(this.renderer.domElement) // 使用渲染器 透過相機將場景渲染出來 this.renderer.render(this.scene, this.camera) } } } </script>
上面就可以呈現基本的物體了(關於材質或者渲染器什麼的可以去 官網看看非常的詳細)
接下來引入軌道 就是物體可以跟隨滑鼠的移動而移動
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'
// 建立軌道控制器 第一個引數是相機 第二個引數是 要渲染的元素 const container = new OrbitControls(this.camera, this.renderer.domElement)
// 先執行一下那個渲染函式
this.render()
render () { // 每一幀都會渲染軌道控制器 引數是場景和攝像頭 this.renderer.render(this.scene, this.camera) // 瀏覽器自帶渲染 下一幀的時候就去在執行這個函式 requestAnimationFrame(this.render) }
還有一些小功能 (不要在意那些變數都是不同程式碼找到的所以有些變數不一樣嘿嘿)
// 新增座標 引數是大小的意思 const axesHelper = new THREE.AxesHelper(5) // 新增到場景 中 this.scene.add(axesHelper) // 設定物體的位置 // this.cube.position.set(5,0,0) // 也可以單獨設定 this.cube.position.x += 0.1 // 物體的縮放 set 引數是xyz this.cube.scale.set(2, 3, 1) // 也可以設定旋轉 set (x,y,z) // 下面例子 Math.PI代表180° 'XYZ' 表示先旋轉什麼 // eg:this.cube.rotation.set(Math.PI /4 0,0,'XYZ') // 下面是代表不斷的旋轉 this.cube.rotation.x += 0.1 // 這個是threejs 帶的算時間的 this.clock = new THREE.Clock() // 建立控制器 this.container = new OrbitControls(this.camera, this.renderer.domElement) // 設定控制阻尼器,讓控制器更有真實效果 必須在動畫循壞的時候呼叫update(). container.enableDamping = true
當然配合使用一些好用的庫 比如gsap(後期有時間會專門寫一些這個動畫庫特別的厲害各種線性動畫或者3D動畫都是牛的)
https://greensock.com/docs/v3/GSAP 官網
還有一個好用的庫 dat.gui 用來控制一些變數來幫助開發(非常的不錯用起來也非常簡單可以百度一下)
下載安裝 npm install --save dat.gui // 匯入dat.gui 控制變數 import * as dat from 'dat.gui'
知道了這些就可以做一些簡單的好玩的效果了 但是需要找一些好看的3D模型 這裡推薦一個
https://sketchfab.com/3d-models?features=downloadable&sort_by=-likeCount 這個也是threejs官網給的裡面有好多的模型而且特別多免費的
下面程式碼是gltf 模型和組合gsap 動畫 包括模型上面帶的動畫組成的一個會走的宇航員 (非常有意思可以試試)
<template> <div> <div id="container" @dblclick="shopOrPlay"></div> </div> </template> <script> import * as THREE from 'three' // 匯入控制器 import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js' // 匯入模型 import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; // 匯入動畫 import gsap from 'gsap' export default { data () { return { camera: null, //相機物件 scene: null, //場景物件 renderer: null, //渲染器物件 mode: null, // 航天員 container: null, flag: true, clock: new THREE.Clock(), mixer: '', mixers: [], animate1: null, baseZ: 3, baseR: 0.7, basePY: -6, basePX: -8, baseMixer: null, stars: null, //星星 mon: null,//月球 }; }, mounted () { this.init() }, methods: { init () { // 初始化場景 this.scene = new THREE.Scene(); // 設定背景 // 初始化相機 75度 寬高比 最小0.1 最大2000 this.camera = new THREE.PerspectiveCamera(75, (document.documentElement.offsetWidth || window.innerWidth) / (document.documentElement.offsetHeight || window.innerHeight), 0.1, 100000) // 設定攝像頭位置 this.camera.position.set(0, 0, 10) // // 初始化渲染器 this.renderer = new THREE.WebGLRenderer({ // 抗鋸齒 // antialias: true, alpha: true // 這個是背景透明色 }) // 設定渲染器寬高 this.renderer.setSize((document.documentElement.offsetWidth || window.innerWidth), (document.documentElement.offsetHeight || window.innerHeight)) // 例項化控制器 this.container = new OrbitControls(this.camera, this.renderer.domElement) this.container.enableDamping = true // 更新攝像頭寬高比 this.camera.aspect = (document.documentElement.offsetWidth || window.innerWidth) / (document.documentElement.offsetHeight || window.innerHeight); // // 更新攝像頭投影矩陣 this.camera.updateProjectionMatrix() // // 新增相機到場景 this.scene.add(this.camera); // 初始化模型 const loader = new GLTFLoader(); // 新增月球 loader.load('/mon/scene.gltf', (gltf) => { this.mon = gltf.scene this.mon.scale.set(1500, 1500, 1500) this.mon.position.set(0, -12, 0) this.scene.add(this.mon); }, undefined, function (error) { }); // 建立一個超大球體 半徑一千 後面的是經緯度 切分為各60 const skyGeomtry = new THREE.SphereGeometry(5000, 50, 50) // 建立一個紋理 const skMaterial = new THREE.MeshBasicMaterial({ side: THREE.DoubleSide, //兩面可見 // 新增紋理 為星河紋理 map: new THREE.TextureLoader().load('./images/bj.jpg') }) // 把球體翻到裡面能看見不然是黑色的 倆面可見就不翻轉了 skyGeomtry.scale(1, 1, 1) // 新增材質 const sky = new THREE.Mesh(skyGeomtry, skMaterial) // 新增到場景 this.scene.add(sky) // 建立宇航員 loader.load('./yuhangyuan/scene.gltf', (gltf) => { this.mode = gltf.scene this.mode.scale.set(3, 3, 3) this.mode.position.set(this.basePX, this.basePY, -90) this.mode.rotation.set(0, 0, 0) this.mixer = new THREE.AnimationMixer(gltf.scene.children[0]); this.baseMixer = this.mixer.clipAction(gltf.animations[0]).setDuration(1) this.baseMixer.play(); this.animate1 = gsap.to(this.mode.position, { z: this.baseZ, duration: 8, onComplete: () => { gsap.to(this.mode.rotation, { y: this.baseR * Math.PI, duration: 1, }) }, }) this.mixers.push(this.mixer); this.scene.add(this.mode); }, undefined, (error) => { }); // 新增光 let light2 = new THREE.DirectionalLight(0Xfffff, 0.3) light2.position.set(0, 10, 10) let light1 = new THREE.HemisphereLight(); this.scene.add(light1, light2 ) // // 設定渲染器編碼 this.renderer.outputEncoding = THREE.sRGBEncoding; // 監聽螢幕大小變化修改渲染器的寬高相機比例 window.addEventListener('resize', this.size) // 監聽螢幕按鍵 window.addEventListener('keyup', this.spacemanMove) document.querySelector('#container').appendChild(this.renderer.domElement) this.render() }, render () { // 在這裡設定阻尼感 this.container.update() var delta = this.clock.getDelta(); for (var i = 0; i < this.mixers.length; i++) { // 重複播放動畫 this.mixers[i].update(delta - 0.011); } this.renderer.render(this.scene, this.camera) requestAnimationFrame(this.render) }, size () { this.camera.aspect = (document.documentElement.offsetWidth || window.innerWidth) / (document.documentElement.offsetHeight || window.innerHeight); this.camera.updateProjectionMatrix() this.renderer.setSize((document.documentElement.offsetWidth || window.innerWidth), (document.documentElement.offsetHeight || window.innerHeight)) }, // 宇航員移動 spacemanMove (e) { if (!this.animate1) return if (!this.animate1.isActive()) { if (e.keyCode === 38) { this.animate1 = gsap.to(this.mode.position, { z: (this.baseZ -= 3) }) } if (e.keyCode === 40) { this.animate1 = gsap.to(this.mode.position, { z: (this.baseZ += 3) }) } if (e.keyCode === 37) { this.animate1 = gsap.to(this.mode.rotation, { y: (this.baseR -= 0.3) * Math.PI }) } if (e.keyCode === 39) { this.animate1 = gsap.to(this.mode.rotation, { y: (this.baseR += 0.3) * Math.PI }) } if (e.keyCode === 87) { this.animate1 = gsap.to(this.mode.position, { y: (this.basePY += 2) }) } if (e.keyCode === 83) { this.animate1 = gsap.to(this.mode.position, { y: (this.basePY -= 2) }) } if (e.keyCode === 65) { this.animate1 = gsap.to(this.mode.position, { x: (this.basePX -= 3) }) } if (e.keyCode === 68) { this.animate1 = gsap.to(this.mode.position, { x: (this.basePX += 3) }) } } }, shopOrPlay () { if (!this.flag) { this.flag = true this.baseMixer.play() } else { this.flag = false this.animate1 = gsap.to(this.mode.rotation, { y: (this.baseR += 2) * Math.PI, yoyo: true, duration: 10, }) this.baseMixer.stop() } } }, beforeDestroy () { window.removeEventListener('resize', this.size) window.removeEventListener('keyup', this.spacemanMove) }, } </script> <style scoped lang='scss'> #container { background: url("@/assets/bj1.jpg"); background-size: cover; } </style>