使用 three.js 中的 CSS3DRenderer 实现 3d 卡片的效果
Posted 掘金开发者社区
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了使用 three.js 中的 CSS3DRenderer 实现 3d 卡片的效果相关的知识,希望对你有一定的参考价值。
前言
最近要做一个 3D 卡片的效果,设计图如下:
第一次尝试
第一次尝试选择了我比较熟悉的 PixiJS,关于我如何用 PixiJS 中的 Sprite3d 做了一个失败的 3D 卡片,可以 戳这里查看。
第二次尝试
有了第一次失败的经历,果断老实选择使用 three.js 来实现 3d 效果。
但为什么选择使用 CSS3DRenderer 实现,可能是相中了 CSS3DRenderer 与 CSS 有关联。
CSS3DRenderer 可以直接通过 THREE.CSS3DObject(DOMElement)
将 Dom 元素转换为 3d 元素,然后控制该对象的 position
和 rotation
属性中的 x
、 y
、 z
来实现动画效果。
效果
实现过程
首先定义并初始化相机(camera)、场景(scene)、渲染器(renderer)和控制器(controls)。
核心代码
引入组件
<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 场景的搭建,后续只需要往场景中添加元素即可
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);
}
往场景中添加元素
// 这里我将元素的 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);
});
});
完整代码
可以直接 戳链接 查看,代码没有压缩。
总结
关于 API
通过
THREE.CSS3DObject(DOMElement)
可以将 Dom 元素转换为object3D
,这样 Dom 元素就可以直接享受 three.js 提供的 3d 场景了;可以借助动画库 tween.js 来控制 object3D 对象的
position
和rotation
属性中的x
、y
、z
,可以实现流畅的动画。使用 js 来控制动画比 css 更加灵活,且可以使用 three.js 中的lookat()
等现成的方法。可以通过
css
中的transform-origin
来改变旋转中心,因为创建的object3D
默认居中,因此改变中心位置后调试位置会有些麻烦。
使用心得
必须承认, three.js 搭建的 3d 场景确实比 PixiJS 搭建的场景炫酷;
虽然
object3D
可以实现嵌套,但在控制台查看 Dom ,可以看到父子 dom 元素是同级,这让我刚开始以为无法实现嵌套;通过
css
中的transform-origin
来改变旋转中心后,会出现一些无法理解的情况,尽量减少改变旋转中心;虽然场景搭建好了,但也是一次失败的尝试。因为我嵌套的层级较多,改变了旋转中心后,旋转出现元素偏移的情况,这个问题我还没有理解,导致这次尝试止步于此,只能另起炉灶再想一种办法尝试了;
如果只是想用图片或简单的图片搭建一个 3d 场景,CSS3DRenderer 是一个不错的选择。
以上是关于使用 three.js 中的 CSS3DRenderer 实现 3d 卡片的效果的主要内容,如果未能解决你的问题,请参考以下文章