Three.js–》实现2D转3D的元素周期表

41,092次阅读
没有评论

共计 7021 个字符,预计需要花费 18 分钟才能阅读完成。

今天简单实现一个 three.js 的小 Demo,加强自己对 three 知识的掌握与学习,只有在项目中才能灵活将所学知识运用起来,话不多说直接开始。

目录

项目搭建

平铺元素周期表

螺旋元素周期表 

网格元素周期表

球状元素周期表

加底部交互按钮


项目搭建

本案例还是借助框架书写 three 项目,借用 vite 构建工具搭建 vue 项目,vite 这个构建工具如果有不了解的朋友,可以参考我之前对其讲解的文章:vite 脚手架的搭建与使用。搭建完成之后,用编辑器打开该项目,在终端执行 npm i 安装一下依赖,安装完成之后终端在安装 npm i three 即可。

因为我搭建的是 vue3 项目,为了便于代码的可读性,所以我将 three.js 代码单独抽离放在一个 js 文件当中,在 views 下的 index.vue 文件中使用该 js 文件,然后再将 index.vue 组件引入根组件。具体如下:





接下来我们重点的 three 代码就不像之前的项目 Demo 一样直接写在 vue 组件中,例子。这里我们直接将其放在一个 js 文件中,当然这里也是需要对 three 代码进行初始化代码处理,如下我们先定义一个基础的 class 类,将要使用的场景、相机、渲染器和渲染函数先定义起来:

import * as THREE from 'three'

class Base {constructor(selector) {this.container = document.querySelector(selector)
        this.scene      
        this.camera
        this.renderer
        this.init()
        this.animate()}
    init() {this.initScene() // 初始化场景
        this.initCamera() // 初始化相机
        this.initRenderer() // 初始化渲染器
        this.initControl() // 初始化控制器
        this.windowSizeChange() // 初始化窗口大小}
}
export default Base

初始化场景

initScene() { // 初始化场景
    this.scene = new THREE.Scene() // 创建场景}

初始化相机

initCamera() {
    // 创建透视相机
    this.camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 10);
    // 设置相机位置
    this.camera.position.set(0, 15, 20);
    // 将相机添加到场景中
    if (this.scene) {this.scene.add(this.camera);
    } else {console.error("Scene is not initialized!");
    }
    // 设置相机观察目标并更新相关矩阵
    this.camera.lookAt(new THREE.Vector3(0, 0, 0));
    this.camera.updateProjectionMatrix();
    this.camera.updateMatrixWorld();} 

初始化渲染器

initRenderer() { // 初始化渲染器
    this.renderer = new THREE.WebGLRenderer({antialias: true});
    // 设置渲染器尺寸
    this.renderer.setPixelRatio(window.devicePixelRatio) // 设置屏幕像素比
    this.renderer.setSize(window.innerWidth, window.innerHeight) // 渲染的尺寸大小
    this.renderer.toneMapping = THREE.ACESFilmicToneMapping // 色调映射
    this.renderer.toneMappingExposure = 2 // 曝光程度
    this.container.appendChild(this.renderer.domElement)
}  

初始化控制器

initControl() { // 初始化控制器
    this.controls = new OrbitControls(this.camera, this.renderer.domElement)
    this.controls.enableDamping = true // 启用阻尼或指数衰减的轨道控制
}

初始化窗口大小

windowSizeChange() { // 初始化窗口大小
    window.addEventListener("resize", () => {
        // 重置渲染器宽高比
        this.renderer.setSize(window.innerWidth, window.innerHeight);
        // 重置相机宽高比
        this.camera.aspect = window.innerWidth / window.innerHeight;
        // 更新相机投影矩阵
        this.camera.updateProjectionMatrix();});
}

设置渲染函数

render() { // 渲染函数
    this.renderer.render(this.scene, this.camera)
}
animate() { // 动画函数
    this.renderer.setAnimationLoop(this.render.bind(this))
}

完整代码如下:

import * as THREE from 'three'
import {OrbitControls} from 'three/examples/jsm/controls/OrbitControls'

class Base {constructor(selector) {this.container = document.querySelector(selector)
        this.scene      
        this.camera
        this.renderer
        this.init()
        this.animate()}
    init() {this.initScene() // 初始化场景
        this.initCamera() // 初始化相机
        this.initRenderer() // 初始化渲染器
        this.initControl() // 初始化控制器
        this.windowSizeChange() // 初始化窗口大小}
    initScene() { // 初始化场景
        this.scene = new THREE.Scene() // 创建场景}
    initCamera() {
        // 创建透视相机
        this.camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 10);
        // 设置相机位置
        this.camera.position.set(0, 15, 20);
        // 将相机添加到场景中
        if (this.scene) {this.scene.add(this.camera);
        } else {console.error("Scene is not initialized!");
        }
        // 设置相机观察目标并更新相关矩阵
        this.camera.lookAt(new THREE.Vector3(0, 0, 0));
        this.camera.updateProjectionMatrix();
        this.camera.updateMatrixWorld();}     
    initRenderer() { // 初始化渲染器
        this.renderer = new THREE.WebGLRenderer({antialias: true});
        // 设置渲染器尺寸
        this.renderer.setPixelRatio(window.devicePixelRatio) // 设置屏幕像素比
        this.renderer.setSize(window.innerWidth, window.innerHeight) // 渲染的尺寸大小
        this.renderer.toneMapping = THREE.ACESFilmicToneMapping // 色调映射
        this.renderer.toneMappingExposure = 2 // 曝光程度
        this.container.appendChild(this.renderer.domElement)
    }  
    initControl() { // 初始化控制器
        this.controls = new OrbitControls(this.camera, this.renderer.domElement)
        this.controls.enableDamping = true // 启用阻尼或指数衰减的轨道控制
    }
    windowSizeChange() { // 初始化窗口大小
        window.addEventListener("resize", () => {
            // 重置渲染器宽高比
            this.renderer.setSize(window.innerWidth, window.innerHeight);
            // 重置相机宽高比
            this.camera.aspect = window.innerWidth / window.innerHeight;
            // 更新相机投影矩阵
            this.camera.updateProjectionMatrix();});
    }
    render() { // 渲染函数
        this.renderer.render(this.scene, this.camera)
    }
    animate() { // 动画函数
        this.renderer.setAnimationLoop(this.render.bind(this))
    }
}
export default Base

写完之后,最后页面呈现一个黑色的背景说明我们的场景加载成功了:

Three.js--》实现 2D 转 3D 的元素周期表

ok,写完基础代码之后,接下来开始具体的 Demo 实操。

平铺元素周期表

本次项目元素周期表并不是使用我们常用的 WebGLRenderer 渲染器,而是 CSS3DRenderer 渲染器,两者区别如下,代码中是可以同时存在这两个渲染器的,它们各自负责不同类型的渲染任务。

WebGLRenderer:用于渲染基于 WebGL 的 3D 场景

CSS3DRenderer:用于渲染基于 CSS 的 3D 对象。这种情况通常用于在 Web 页面中同时显示 3D 对象和其他 HTML 元素,例如在 3D 场景中嵌入文字、按钮等。

因为本次项目单纯就使用基于 CSS 的 3D 对象,所以我们要对之前的代码进行修改,切换渲染器:

createCSS3DRenderer() { // 创建 CSS3D 渲染器
    this.renderer3D = new CSS3DRenderer();
    this.renderer3D.setSize(window.innerWidth, window.innerHeight);
    this.renderer3D.domElement.style.backgroundColor = 'black';
    this.container.appendChild(this.renderer3D.domElement);
}

接下来将元素周期表的相关数据进行如下的总结,将元素周期表的数据和位置抽离成 js 文件:

Three.js--》实现 2D 转 3D 的元素周期表

然后接下来在 scene.js 文件中引入元素周期表.js 获取相关数据,进行如下函数创建元素周期表:

createElement() {for (let i = 0; i ' + element[i + 2]
        parent.appendChild(detail)
        // 实例化 CSS3D 对象
        let element3D = new CSS3DObject(parent)
        this.objects.push(element3D)
        // 加载 3D 场景
        this.scene.add(element3D)
    }
}

然后我们在 App 根组件中删除 scoped 设置全局 css 样式,给上面创建的 div 类名设置样式:

Three.js--》实现 2D 转 3D 的元素周期表

接下来我们开始处理元素周期表的位置样式,将 element 获取的位置数据进行放大,然后通过页面进行细微的调整:

// 处理元素周期表样式
handleTableStyle() {for (let i = 0; i 

然后根据情况设置相机位置进行细微的调整,使得整个场景处于正中央即可:

// 设置相机位置
this.camera.position.set(0, 15, 2800);

最终呈现的效果如下:

Three.js--》实现 2D 转 3D 的元素周期表

螺旋元素周期表 

根据上面实现的基础上,接下来我们实现将元素周期表的位置进行一个螺旋状的展示,在 three 中提供了一个 3D 的函数,这个函数通常用于设置一个三维向量的坐标,其中柱面坐标系由一个半径、一个角度和一个高度组成。这种坐标系通常用于描述圆柱体表面上的点的位置,如下:

Three.js--》实现 2D 转 3D 的元素周期表

具体来说,setFromCylindricalCoords 函数接受柱面坐标系的三个参数:

1)radius:柱面坐标系中的半径。

2)theta:柱面坐标系中的角度,以弧度表示。

3)y:柱面坐标系中的高度。

当需要根据柱面坐标系来定位或者旋转一个对象时,可以使用这个函数来方便地设置该对象的位置或者方向,接下来通过如下代码进行简单的测试一下:

// 螺旋元素周期表
spiralTable() {for (let i = 0; i 

呈现的效果如下所示,可见是一圈圆,但我们想实现螺旋式的效果应该这么做,这里需要调整:

Three.js--》实现 2D 转 3D 的元素周期表

接下来对上面螺旋周期表函数进行一些参数的调整,然后设置一些 rotation 参数:

// 螺旋元素周期表
spiralTable() {for (let i = 0; i 

最终呈现的效果如下,大体效果还是不错的:

Three.js--》实现 2D 转 3D 的元素周期表

网格元素周期表

对于网格处理的函数也很简单,如下该函数的主要逻辑是遍历 this.objects 数组,并为每个元素(即每个物体)计算其在三维空间中的新位置。每个物体在 x、y 和 z 轴上的位置都基于其索引 i 来计算,以达到这种排列效果:

// 网格元素周期表
gridTable() {for (let i = 0; i 

最终呈现的效果如下:

Three.js--》实现 2D 转 3D 的元素周期表

球状元素周期表

在写球状元素周期表之前,我们先了解一下球概念,如下:

Three.js--》实现 2D 转 3D 的元素周期表

在 threejs 官网上,也有关于球状相关的 api 方法,如下:

Three.js--》实现 2D 转 3D 的元素周期表

在一个三维场景中,根据球状元素周期表的规则来排列和旋转一系列的物体。这里根据一定的数学规则(这里使用了反余弦函数和平方根函数)来调整 this.objects 数组中每个物体的位置和旋转模拟一种特殊的排列或动画效果:

// 球状元素周期表
ballTable() {for (let i = 0; i 

最终呈现的效果如下:

Three.js--》实现 2D 转 3D 的元素周期表

加底部交互按钮

接下来我们实现点击底部的按钮进行不同的场景切换,如下:



效果如下所示:

Three.js--》实现 2D 转 3D 的元素周期表

接下来我们设置其点击后样式:

最终呈现的效果如下:

原文地址: Three.js–》实现 2D 转 3D 的元素周期表

    正文完
     0
    Yojack
    版权声明:本篇文章由 Yojack 于2024-10-29发表,共计7021字。
    转载说明:
    1 本网站名称:优杰开发笔记
    2 本站永久网址:https://yojack.cn
    3 本网站的文章部分内容可能来源于网络,仅供大家学习与参考,如有侵权,请联系站长进行删除处理。
    4 本站一切资源不代表本站立场,并不代表本站赞同其观点和对其真实性负责。
    5 本站所有内容均可转载及分享, 但请注明出处
    6 我们始终尊重原创作者的版权,所有文章在发布时,均尽可能注明出处与作者。
    7 站长邮箱:laylwenl@gmail.com
    评论(没有评论)