【前端实习生入职】之-threejs实现可拖动立体框


theme: hydrogen

前言

小伙伴们大家好,已经有一段时间没有更新了,这段时间匆匆忙忙来到北京实习,从一个南方的前端在校学生到北方来生活实习,一切都在慢慢适应当中,来公司之前就了解到这家公司是做自动驾驶以及ai方面的,到公司后果不其然疯狂学习threejs,webGL等3D开发,来第一周主管就给我发布一个任务如下:
image.png

以下这张图片很好的展示了threejs的系统模块,你可以把知识点模块化加以学习~3~
image.png
所以这周就疯狂学习threejs,在实战案例之前,你需要了解一些threejs的基础知识,以下是我的学习途径,大家可以参考。

  • threejs官方文档(可切换中文哟~):https://threejs.org/
  • b站教学视频(听了好几个,这个讲的我觉得很通俗易懂): https://www.bilibili.com/video/BV1Gg411X7FY/?spm_id_from=333.337.search-card.all.click
  • 入门教程(官方文档的稍详细解说):http://www.webgl3d.cn/Three.js/
  • 掘金拓展学习文章(看了一篇项目实战,写的很优秀):https://juejin.cn/post/6981249521258856456

成品展示

chrome-capture-2022-10-16.gif

开始实战 GO!GO!GO!

在此之前,你有很多方式来引入threejs以及其插件

  1. 直接在script标签中引入
    下面是一些你会用到的threejs插件源码http://www.yanhuangxueyuan.com/links.html
  2. 使用es6 模块化 别忘了加type=”module”
    在我们有一定threejs基础知识后,其实上手就容易得多,让我们先搭建一些基本的框架吧!

基本框架搭建

    // 创建渲染器
    renderer = new THREE.WebGLRenderer();
    // 设备像素比
    renderer.setPixelRatio( window.devicePixelRatio );
    renderer.setSize( window.innerWidth, window.innerHeight );
    document.body.appendChild( renderer.domElement );
    // 摄像机视锥体的长宽比,通常是使用画布的宽/画布的高
    const aspect = window.innerWidth / window.innerHeight;
    // 透视摄像机
    cameraPersp = new THREE.PerspectiveCamera( 50, aspect, 0.01, 30000 );
    // 正交摄像机
    cameraOrtho = new THREE.OrthographicCamera( - 600 * aspect, 600 * aspect, 600, - 600, 0.01, 30000 );
    currentCamera = cameraPersp;
    currentCamera.position.set( 1000, 500, 1000 );
    currentCamera.lookAt( 0, 200, 0 );
    // 创建场景
    scene = new THREE.Scene();
    // 添加网格辅助平面
    scene.add( new THREE.GridHelper( 1000, 10, 0x888888, 0x444444 ) );

这时候框架的基本的三要素搭建完成,当前屏幕中怎么啥也没有???(好像有一个网状平面)当然了,
相机和渲染器是不会显示到屏幕当中的,相机相当于你的眼睛,透过眼睛所要看到的才会显示,你现在什么也没有加入到场景中!

模型创建 + 环境因素

你玩过我的世界吗?是不是有种模拟真实世界的感觉?threejs可以做到,像影子,树木纹理,玻璃球反光效果等等。。
那接下来我们加入一些元素和环境因素吧!

// 设置光源 - 平行光
    const light = new THREE.DirectionalLight( 0xffffff, 5 );
    light.position.set( 1, 1, 1 );
    scene.add( light );
// 创建模型
    const geometry = new THREE.BoxGeometry( 200, 200, 200 );
    // 设置模型材质 MeshBasicMaterial 表示基础材质不受光照影响
    const material = new THREE.MeshBasicMaterial(
{
            color: 0x00ff00,
            wireframe:true,
});
    // 添加网格 组合模型和材质
    const mesh = new THREE.Mesh( geometry, material );
    // 把网格添加到场景中
    scene.add( mesh );

旋转?跳跃!

如果想让模型旋转起来,使得相机随着鼠标拖动绕轨道运动,或许你需要Orbitcontrols(轨道控制器)

    // 创建轨道控制器
    const orbit = new OrbitControls( currentCamera, renderer.domElement );
    // 实时更新
    orbit.update();
    // 监听dom事件改变
    orbit.addEventListener( 'change', render );

如果需要拖动模型?threejs提供一种变换控制器(TransformControls)类似于在数字内容创建工具中对模型进行交互的方式,来在3D空间中变换物体,和其他控制器不同的是,TransformControls不倾向于对场景摄像机的变换进行改变。

    // 创建变换控制器
    const control = new TransformControls(currentCamera,renderer.domElement );
    // 监听dom改变事件
    control.addEventListener( 'change', render );
    // 监听鼠标拖拽事件
    control.addEventListener( 'dragging-changed', function ( event ) {
            orbit.enabled = ! event.value;
    });
    // TransfromControls当中的方法 设置应当变换的3D对象,并确保控制器UI是可见的
    control.attach( mesh );
    // 把控制器加入到场景中
    scene.add( control );

最后的优化 – 窗口自适应

// 监听窗口大小的变化
window.addEventListener( 'resize', onWindowResize );

function onWindowResize() {
    const aspect = window.innerWidth / window.innerHeight;
    cameraPersp.aspect = aspect;
    cameraPersp.updateProjectionMatrix();
    cameraOrtho.left = cameraOrtho.bottom * aspect;
    cameraOrtho.right = cameraOrtho.top * aspect;
    cameraOrtho.updateProjectionMatrix();
    renderer.setSize( window.innerWidth, window.innerHeight );
    render();
}

规整代码

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

let cameraPersp, cameraOrtho, currentCamera;
let scene, renderer, control, orbit;

function init() {
// 创建渲染器
renderer = new THREE.WebGLRenderer();
// 设备像素比
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );
// 摄像机视锥体的长宽比,通常是使用画布的宽/画布的高
const aspect = window.innerWidth / window.innerHeight;
// 透视摄像机
cameraPersp = new THREE.PerspectiveCamera( 50, aspect, 0.01, 30000 );
// 正交摄像机
cameraOrtho = new THREE.OrthographicCamera( - 600 * aspect, 600 * aspect, 600, - 600, 0.01, 30000 );
currentCamera = cameraPersp;

currentCamera.position.set( 1000, 500, 1000 );
currentCamera.lookAt( 0, 200, 0 );

// 创建场景
scene = new THREE.Scene();
// 添加网格辅助平面
scene.add( new THREE.GridHelper( 1000, 10, 0x888888, 0x444444 ) );
// 设置光源 - 平行光
const light = new THREE.DirectionalLight( 0xffffff, 5 );
light.position.set( 1, 1, 1 );
scene.add( light );

// const texture = new THREE.TextureLoader().load( '../assets/crate.gif', render );
// texture.anisotropy = renderer.capabilities.getMaxAnisotropy();

// 导入纹理
// const textureLoader = new THREE.TextureLoader()
// const doorColorTexture = textureLoader.load('../assets/crate.gif')
// 创建模型
const geometry = new THREE.BoxGeometry( 200, 200, 200 );
const material = new THREE.MeshBasicMaterial(
{
color: 0x00ff00,
wireframe:true,
});
// 添加网格
const mesh = new THREE.Mesh( geometry, material );
scene.add( mesh );
const orbit = new OrbitControls( currentCamera, renderer.domElement );
// 实时更新旋转状态
orbit.update();
orbit.addEventListener( 'change', render );

const control = new TransformControls( currentCamera, renderer.domElement );
control.addEventListener( 'change', render );
control.addEventListener( 'dragging-changed', function ( event ) {
orbit.enabled = ! event.value;
});

// 添加坐标轴辅助器
// const axesHelper = new THREE.AxesHelper(5)
// scene.add(axesHelper)
control.attach( mesh );
scene.add( control );
window.addEventListener( 'resize', onWindowResize );
}
// 窗口自适应
function onWindowResize() {
const aspect = window.innerWidth / window.innerHeight;
cameraPersp.aspect = aspect;
cameraPersp.updateProjectionMatrix();
cameraOrtho.left = cameraOrtho.bottom * aspect;
cameraOrtho.right = cameraOrtho.top * aspect;
cameraOrtho.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
render();
}
function render() {
renderer.render( scene, currentCamera );
}

init();
render();

总结

把以上的结合起来就完成了咱们这个小实例,是不是很鸡肋,但没关系,你可以加上自己喜欢的纹理贴图,光照效果,或者很多高大上的样式!如果有什么新的学习思路可以与我沟通!欢迎交流~

© 版权声明
THE END
喜欢就支持一下吧
点赞7 分享
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

取消
昵称表情代码图片

    暂无评论内容