使用 three.js 中的 CSS3DRenderer 实现 3d 卡片的效果

最近要做一个 3D 卡片的效果,设计图如下:

第一次尝试

第一次尝试选择了我比较熟悉的 PixiJS,关于我如何用 PixiJS 中的 Sprite3d 做了一个失败的 3D 卡片,可以戳这里查看。

第二次尝试

有了第一次失败的经历,果断老实选择使用 three.js 来实现 3d 效果。

但为什么选择使用 CSS3DRenderer 实现,可能是相中了 CSS3DRenderer 与 CSS 有关联。

CSS3DRenderer 可以直接通过 THREE.CSS3DObject(DOMElement) 将 Dom 元素转换为 3d 元素,然后控制该对象的 positionrotation 属性中的 xyz 来实现动画效果。

效果

效果图如下,在线预览戳这里。

实现过程

首先定义并初始化相机(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

  1. 通过 THREE.CSS3DObject(DOMElement) 可以将 Dom 元素转换为 object3D ,这样 Dom 元素就可以直接享受 three.js 提供的 3d 场景了;
  2. 可以借助动画库 tween.js 来控制 object3D 对象的 positionrotation 属性中的 xyz ,可以实现流畅的动画。使用 js 来控制动画比 css 更加灵活,且可以使用 three.js 中的 lookat() 等现成的方法。
  3. 可以通过 css 中的 transform-origin 来改变旋转中心,因为创建的 object3D 默认居中,因此改变中心位置后调试位置会有些麻烦。
我来评几句
登录后评论

已发表评论数()

相关站点

+订阅
热门文章