最近要作一個 3D 卡片的效果,設計圖以下: javascript
第一次嘗試選擇了我比較熟悉的 PixiJS,關於我如何用 PixiJS 中的 Sprite3d 作了一個失敗的 3D 卡片,能夠 戳這裏查看。css
有了第一次失敗的經歷,果斷老實選擇使用 three.js 來實現 3d 效果。html
但爲何選擇使用 CSS3DRenderer 實現,多是相中了 CSS3DRenderer 與 CSS 有關聯。java
CSS3DRenderer 能夠直接經過 THREE.CSS3DObject(DOMElement)
將 Dom 元素轉換爲 3d 元素,而後控制該對象的 position
和 rotation
屬性中的 x
、y
、 z
來實現動畫效果。git
效果圖以下,在線預覽 戳這裏。github
首先定義並初始化相機(camera)、場景(scene)、渲染器(renderer)和控制器(controls)。web
引入組件json
<script src="./js/three.js"></script> <script src="./js/tween.min.js"></script> <script src="./js/TrackballControls.js"></script> <script src="./js/CSS3DRenderer.js"></script> 複製代碼
搭建 three.js 框架,如下代碼就完成了 3D 場景的搭建,後續只須要往場景中添加元素便可markdown
let camera,scene,renderer; // 定義相機、場景和渲染器 let controls; // 定義控制器 window.onload = ()=>{ init(); animate(); }; function init() { // 相機初始化 camera = new THREE.PerspectiveCamera(40,window.innerWidth/window.innerHeight,1,10000); camera.position.z = 3000; // 場景初始化 scene = new THREE.Scene(); // 場景中的元素在此處添加,代碼在下一個片斷 .... // 渲染器初始化,這裏使用的是 CSS3DRenderer 渲染 renderer = new THREE.CSS3DRenderer(); renderer.setSize(window.innerWidth,window.innerHeight); document.getElementById('container').appendChild(renderer.domElement); // 控制器初始化 controls = new THREE.TrackballControls(camera, renderer.domElement); controls.addEventListener('change',render); window.addEventListener('resize',onWindowResize,false); render(); } function onWindowResize(){ camera.aspect = window.innerWidth/window.innerHeight; camera.updateProjectionMatrix(); renderer.setSize(window.innerWidth,window.innerHeight); render(); } function animate(){ requestAnimationFrame(animate); controls.update(); } function render(){ renderer.render(scene,camera); } 複製代碼
往場景中添加元素app
// 這裏我將元素的 css 屬性放到 json 當中,方便遍歷建立 const objectData = [ // 卡片 { verticalBg:{ url:'./assets/bg_0.jpg', width: 1203, height: 589, }, ground:{ url:'./assets/bg_1.jpg', width:1203, height: 589, rotation:-Math.PI/180*70, }, thingsRotation:Math.PI/180*70, things:[ { url:'./assets/card1_thing_0.png', width:403, height: 284, x:-80, y:-445, }, ... ] } ]; 複製代碼
// 最外層元素 const container = document.createElement('div'); // 使用 js 動態建立 DomElement container.className = 'container'; const objectContainer = new THREE.CSS3DObject(container); // 使用 CSS3DObject 將 DomElement 轉換爲 3d 元素 scene.add(objectContainer); // 將轉換好的 3d 元素添加到場景 objectData.forEach((cardItem,cardIndex)=>{ // 卡片 const cardContainer = document.createElement('div'); cardContainer.style.width = 1448+'px'; cardContainer.style.height = 750+'px'; const objectCardContainer = new THREE.CSS3DObject(cardContainer); objectContainer.add(objectCardContainer); // 經過 object3D 的 add 方法實現嵌套 //豎直背景 const card_bg_vertical = document.createElement('div'); card_bg_vertical.style.width = cardItem.verticalBg.width+'px'; card_bg_vertical.style.height = cardItem.verticalBg.height+'px'; card_bg_vertical.style.background = 'url('+cardItem.verticalBg.url+') no-repeat'; const objectCardBgVertical = new THREE.CSS3DObject(card_bg_vertical); objectCardBgVertical.position.y = -80; // 經過 object3D 的 position 屬性改變元素位置 objectCardContainer.add(objectCardBgVertical); // 地面 const card_groud = document.createElement('div'); card_groud.style.width = cardItem.ground.width+'px'; card_groud.style.height = cardItem.ground.height+'px'; card_groud.style.transformOrigin = 'center top'; // 經過 css 中的 transform-origin 來改變旋轉中心 card_groud.style.background = 'url('+cardItem.ground.url+') no-repeat'; const objectCardGround = new THREE.CSS3DObject(card_groud); objectCardGround.position.y = 80; objectCardGround.rotation.x = cardItem.ground.rotation; // 經過 object3D 的 rotation 屬性來旋轉元素 objectCardContainer.add(objectCardGround); // 元素 cardItem.things.forEach((item,index)=>{ const thing = document.createElement('div'); thing.style.width = item.width+'px'; thing.style.height = item.height+'px'; thing.style.background = 'url('+ item.url +') no-repeat'; const objectThing = new THREE.CSS3DObject(thing); objectThing.rotation.x = cardItem.thingsRotation; objectThing.position.y = -(index+1)*68; objectThing.position.x = item.x; objectThing.position.z = -item.y-300; objectCardGround.add(objectThing); }); }); 複製代碼
能夠直接 戳連接 查看,代碼沒有壓縮。
THREE.CSS3DObject(DOMElement)
能夠將 Dom 元素轉換爲 object3D
,這樣 Dom 元素就能夠直接享受 three.js 提供的 3d 場景了;position
和 rotation
屬性中的 x
、y
、 z
,能夠實現流暢的動畫。使用 js 來控制動畫比 css 更加靈活,且可使用 three.js 中的 lookat()
等現成的方法。css
中的 transform-origin
來改變旋轉中心,由於建立的 object3D
默認居中,所以改變中心位置後調試位置會有些麻煩。object3D
能夠實現嵌套,但在控制檯查看 Dom ,能夠看到父子 dom 元素是同級,這讓我剛開始覺得沒法實現嵌套;css
中的 transform-origin
來改變旋轉中心後,會出現一些沒法理解的狀況,儘可能減小改變旋轉中心;