ThreeJS面向速记部分业务
面向速记涉及的知识点:
1.相机
2.场景
3.渲染器
4.形状、材质、模型
5.位置、旋转、缩放
6.灯光
7.抗锯齿
8.光线投射器(拾取物体对象)
9.动画时间轴(采用第三方插件)
开始动手之前,先引入两个框架:ThreeJS框架和TweenMax动画插件。
<!-- CDN引入threeJS框架 -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/102/three.js"></script>
<!-- CDN引入TimeMax动画插件 -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/2.1.2/TweenMax.min.js"></script>
ThreeJS三大的基本:场景、相机、渲染器,因此是我们必须创建它们。
// 创建场景
var scene = new THREE.Scene();
// 创建相机,采用透视相机,四个参数
var camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000)
//相机位置
camera.position.set(0, 0, 5);
// 创建WebGL渲染器,渲染器就像好几种类型的渲染器,比如css2D、css3D、SVG等渲染器,性能非常灵活
var renderer = new THREE.WebGLRenderer({
antialias: true
}); //创建抗锯齿,为了增加形状的边缘水平或垂直
// 渲染器:设置背景颜色
renderer.setClearColor("#e5e5e5"); //灰色
// 渲染器:设置浏览器窗口的大小
renderer.setSize(window.innerWidth, window.innerHeight);
最后如何为创建在HTML文档的canvas元素?首先可以在js中创建为canvas元素,如下图:
// canvas对象相当于返回一个画布元素
document.body.appendChild(renderer.domElement);
ThreeJS的三大基本已经OK了!接下来是创建形状、材质和模型:
// 创建形状,立方体
var geometry = new THREE.BoxGeometry(1, 1, 1);
// 创建面的材质
var material = new THREE.MeshLambertMaterial({
color: 0xF7F7F7
});
// 把形状和材质保存到网格模型中
var mesh = new THREE.Mesh(geometry, material);
// 网格模型添加到场景中
scene.add(mesh);
根据以上的内容,可以说ThreeJS基本的知识已经完成了。
接下来是灯光,创建了2个灯光环境方向,光源类型为点光源(PointLight),只是两个灯光的位置不同。一个是原点的位置(0,0,0),另一个是向屏幕画面离的值为25个单位。
// 创建灯光环境方向,增加两个灯光(点光源)
var light = new THREE.PointLight(0xFFFFFF, 1, 1000);
light.position.set(0, 0, 0);
scene.add(light);
var light = new THREE.PointLight(0xFFFFFF, 2, 1000);
light.position.set(0, 0, 25);
scene.add(light);
但是另外考虑到有些方面,比如需要去监听那些事件,按键盘、窗口大小等,而且看渲染DOM元素是如何的?
当我把浏览器窗口大小的变化时,发现渲染DOM元素的位置还是不动的,没反应。而且需要去手动刷新才能使渲染DOM元素变化,这样太麻烦了。如下图:
因此,需要设置去监听浏览器窗口大小的变化。
// 所以需要监听窗口的大小发生变化
window.addEventListener('resize', () => {
// 每次重新调整窗口大小
renderer.setSize(window.innerWidth, window.innerHeight);
// 每次重新调整相机的变化
camera.aspect = window.innerWidth / window.innerHeight;
// 必须每次在相机上调用更新相机投影矩阵
camera.updateProjectionMatrix();
})
另外,为什么需要设置相机大小的变化?如果没有设置相机大小的变化,那么会让整个模型就会发生扭曲或变形。因此,需要设置相机大小的变化。
接下来是做简单的动画,能不能像做到那种时间轴的实时动画过程,来试下。
首先需要创建render函数,其实一般的浏览器每次实时刷新,每一秒相当于总是循环60次,也就是说60FPS,最重要的是时间间隔,解决的是通过requestAnimationFrame()提供了这些的需求,例如:大多数渲染的时候间隔是每秒60秒,一般会把每一帧DOM操作集中起来,一般来说频率为每秒60帧。
简单来说,当每一帧刷新的变化时都会重新绘制渲染一次。
另外设置给它模型的动画旋转为X轴和Y轴的变化,就像动画时间轴,这样实现动画的效果。
var render = function () {
// 修复形状发生扭曲,并请求动画帧
requestAnimationFrame(render);
// 每次调用渲染器时候都会添加(每秒帧数),就像动画序列一样,看起来很有趣的动画
mesh.rotation.x += 0.03;
mesh.rotation.y += 0.01;
// renderer.render渲染我们需要渲染的场景和相机保存
renderer.render(scene, camera);
}
//全局加载
render();
接下来是光线投射器(拾取物体对象),例如说允许你拖动鼠标在画布上的位置,然后可以确定你点击位置的物体对象并作出行为发生变化。
// 光线投射器
var raycaster = new THREE.Raycaster();
var mouse = new THREE.Vector2();
另外还需要设置监听鼠标移动悬停的状态。
//在窗口鼠标移动悬停时的情况下就播放动画物体运动,
//在定义鼠标移动悬停的时候就调用它。
window.addEventListener('mousemove', onMouseMove);
创建鼠标移动悬停的本身函数,一般在场景中有些不应的物体对象就会默认发生执行事件,所以需要设置preventDefault()。还需要获取鼠标x和y的位置,然后让它们放入光线投射器里面,同时设置鼠标和相机。最后,intersectObjects() 来判断指定对象有没有被光线点击,如果被点击就会返回对象的信息,一般返回类型就是数组的形式。
// 鼠标移动悬停的功能函数
function onMouseMove(event) {
// 就是防止点击默认事件,例如:点击不应的物体对象就会阻止点击事件。
event.preventDefault();
// 直接从文档中获取,所以我们必须获取鼠标x和y的位置
mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
// 把它们放入我们的光线投射器,同时从相机设置,通过鼠标和相机。
raycaster.setFromCamera(mouse, camera);
// 计算与拾取射线相交的物体
var intersects = raycaster.intersectObjects(scene.children, true);
// console.log(scene.children);
//
光线投射器Raycaster基础:https://threejs.org/docs/#api/en/core/Raycaster
接下来是采用TimelineMax第三方动画时间轴插件,主要是为了解决构建复杂的动画时间轴,因此它能使方便地、简单地构建动画时间轴。
TimelineMax中文手册:https://www.tweenmax.com.cn/api/timelinemax/
这些部分动画代码放在鼠标移动悬停函数里面,是根据intersectObject()返回类型为数组的形式,所以需要加循环。
// TimelineMax是GreenSock 动画平台
for (var i = 0; i < intersects.length; i++) {
// 创建新的时间线动画序列
this.tl = new TimelineMax();
// 有四帧动画运动
this.tl.to(intersects[i].object.scale, 1, { x: 2, ease: Expo.easeOut })
this.tl.to(intersects[i].object.scale, .5, { x: .5, ease: Expo.easeOut })
this.tl.to(intersects[i].object.position, .5, { x: 2, ease: Expo.easeOut })
this.tl.to(intersects[i].object.rotation, .5, { y: Math.PI * .5, ease: Expo.easeOut }, "=-1.5")
}
大部分的知识点已经差不多完成了!
还有如果想要多个物体对象的场景,根据已经设置了光线投射器,因此可以实现拾取物体对象。
我设置了15个模型,所以需要循环15次并任何物体对象随机位置。另外手动刷新的时候,任何物体对象的位置总是会变化的。
效果图:
参考完整代码示例及效果图:http://192.168.10.26/digitalvisualization/threejs_tutorial/-/blob/master/hjb/shortindex.html