注册
web

2D的雪碧图已经够炫了,那么3D的呢?

output-16_6_11.gif


前言


前2篇文章,分别介绍了dat.gui纹理贴图,老是理论没有实战也是没有什么意思的,今天我们就来着手一个小案例,赶紧实现起来,让你的博客更加炫酷!


这个案例包含了tweenjs动画库的使用,该动画库已在three.js中内置,路径为: examples/jsm/libs/tween.module.js,使用起来也是比较简单。


初始化


老样子,场景、相机、渲染器三要素初始化,并导入需要的插件库,插件库都已在three中内置:


import * as THREE from 'three';
// tween动画库
import TWEEN from 'three/addons/libs/tween.module.js';
//通过轨迹球控件TrackballControls 我们可以实现场景的旋转、缩放、平移等功能
import { TrackballControls } from 'three/addons/controls/TrackballControls.js'
// 雪碧图
import { CSS3DRenderer, CSS3DSprite } from 'three/addons/renderers/CSS3DRenderer.js'

// 定义场景、相机、渲染器
let scene, camera, renderer;

// 初始化
init()
// 渲染
animate();

function init() {
// 透视相机 远端距离最好设大一点 不然会展示不全
camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 10000);
// 相机位置
camera.position.set(600, 400, 1500);
// 相机朝向位置
camera.lookAt(0, 0, 0);

// 场景
scene = new THREE.Scene();

// 渲染画布
renderer = new CSS3DRenderer();
renderer.setSize(innerWidth, window.innerHeight);
document.getElementById('container').appendChild(renderer.domElement);
}

// 渲染
function animate() {
requestAnimationFrame(animate);
renderer.render(scene, camera)
}

CSS3DSprite创建521个水球


// 定义小球数量
const particlesTotal = 512;
// 定义位置
const positions = [];
// 定义物体
const objects = []
const image = document.createElement('img');
image.addEventListener('load', () => {
for (let i = 0; i < particlesTotal; i++) {
// cloneNode() 方法可创建指定的节点的精确拷贝
const object = new CSS3DSprite(image.cloneNode())
// 随机分布位置 -2000 2000的立方体内
object.position.x = Math.random() * 4000 - 2000,
object.position.y = Math.random() * 4000 - 2000,
object.position.z = Math.random() * 4000 - 2000,
scene.add(object)

objects.push(object);
}
})
image.src = './static/img/sprite.png';


上面的代码中,我们创建了img标签,并使用CSS3DSpriteHTML元素转化为threejs的CSS3精灵模型,类似与转换成了three中的网格,并随机分布在-2000,2000的立方体中。


看下效果:


three04-1.jpg


添加控制器


关于控制器,前面也已经介绍过啦,通过控制器,我们就可以改变相机的位置,观察不同角度的物体。


// 定义控制器
let controls;
controls = new TrackballControls( camera, renderer.domElement );

// 渲染
function animate() {
...
controls.update();
...
}

注意哦,controls.update需要防止在animate中,每帧执行。


有了控制器。我们就可以实现交互啦:


output-15_37_12.gif


让小球按规律放大缩小


让小球按照正弦时间,放大缩小,即有一种闪烁的效果:


const time = performance.now();

for (let i = 0, l = objects.length; i < l; i++) {
const object = objects[i];
const scale = Math.sin((Math.floor(object.position.x) + time) * 0.002) * 0.3 + 1;
object.scale.set(scale, scale, scale);
}

output-15_37_59.gif


让小球生成特定图形


生成矩形,对应的每个小球坐标:


const amount = 8;
const separationCube = 150;
const offset = ( ( amount - 1 ) * separationCube ) / 2;

for ( let i = 0; i < particlesTotal; i ++ ) {

const x = ( i % amount ) * separationCube;
const y = Math.floor( ( i / amount ) % amount ) * separationCube;
const z = Math.floor( i / ( amount * amount ) ) * separationCube;

positions.push( x - offset, y - offset, z - offset );

}

tween.js使用


const position = {x: 0,y: 0};
;//创建一段tween动画
const tween = new TWEEN.Tween(position)
//经过2秒,position对象的x和y属性分别从零变化为100、50
tween.to({x: 100,y: 50}, 2000);
//tween动画开始执行
tween.start();
// 动画效果 类似annimation
tween.easing()
// 完成时执行的钩子,里面可以继续执行下一个操作
tween.onComplete()

使杂乱的小球变成矩形


import * as THREE from 'three';
// tween动画库
import TWEEN from 'three/addons/libs/tween.module.js';
//通过轨迹球控件TrackballControls 我们可以实现场景的旋转、缩放、平移等功能
import { TrackballControls } from 'three/addons/controls/TrackballControls.js'
// 雪碧图
import { CSS3DRenderer, CSS3DSprite } from 'three/addons/renderers/CSS3DRenderer.js'

// 定义场景、相机、渲染器
let scene, camera, renderer;
// 定义控制器
let controls;

// 定义小球数量
const particlesTotal = 512;
// 定义位置
const positions = [];
// 定义物体
const objects = []
let current = 0;
// 初始化
init()
// 渲染
animate();

function init() {
// 透视相机 远端距离最好设大一点 不然会展示不全
camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 10000);
// 相机位置
camera.position.set(600, 400, 1500);
// 相机朝向位置
camera.lookAt(0, 0, 0);


scene = new THREE.Scene();

const image = document.createElement('img');
image.addEventListener('load', () => {
for (let i = 0; i < particlesTotal; i++) {
// cloneNode() 方法可创建指定的节点的精确拷贝
const object = new CSS3DSprite(image.cloneNode())
// 随机分布位置 -2000 2000的立方体内
object.position.x = Math.random() * 4000 - 2000,
object.position.y = Math.random() * 4000 - 2000,
object.position.z = Math.random() * 4000 - 2000,
scene.add(object)

objects.push(object);
}
transition()
})
image.src = './static/img/sprite.png';

// cube
const amount = 8;
const separationCube = 150;
const offset = ((amount - 1) * separationCube) / 2;

for (let i = 0; i < particlesTotal; i++) {

const x = (i % amount) * separationCube;
const y = Math.floor((i / amount) % amount) * separationCube;
const z = Math.floor(i / (amount * amount)) * separationCube;

positions.push(x - offset, y - offset, z - offset);

}

// 渲染画布
renderer = new CSS3DRenderer();
renderer.setSize(innerWidth, window.innerHeight);
document.getElementById('container').appendChild(renderer.domElement);

controls = new TrackballControls(camera, renderer.domElement);

}


// 动画
function transition() {
const offset = current * particlesTotal * 3;
const duration = 2000;
for (let i = 0, j = offset; i < particlesTotal; i++, j += 3) {
const object = objects[i];
new TWEEN.Tween(object.position)
.to({
x: positions[j],
y: positions[j + 1],
z: positions[j + 2]
}, Math.random() * duration + duration)
.easing(TWEEN.Easing.Exponential.InOut)
.start();
}
new TWEEN.Tween(this)
.to({}, duration * 3)
.onComplete(transition)
.start();
current = (current + 1) % 4;
}

// 渲染
function animate() {
requestAnimationFrame(animate);
controls.update();
TWEEN.update();

// 让小球按照正弦时间,放大缩小
const time = performance.now();

for (let i = 0, l = objects.length; i < l; i++) {

const object = objects[i];
const scale = Math.sin((Math.floor(object.position.x) + time) * 0.002) * 0.3 + 1;
object.scale.set(scale, scale, scale);

}
renderer.render(scene, camera)
}

杂乱的小球变成多种形态完整代码


限制文件大小啦,没法完全展示,大家自行运行看看吧!


output-15_49_5.gif


<div id="container"></div>
<script type="module">
import * as THREE from 'three';
// tween动画库
import TWEEN from 'three/addons/libs/tween.module.js';
//通过轨迹球控件TrackballControls 我们可以实现场景的旋转、缩放、平移等功能
import { TrackballControls } from 'three/addons/controls/TrackballControls.js'
// 雪碧图
import { CSS3DRenderer, CSS3DSprite } from 'three/addons/renderers/CSS3DRenderer.js'

// 定义场景、相机、渲染器
let scene, camera, renderer;
// 定义控制器
let controls;

// 定义小球数量
const particlesTotal = 512;
// 定义位置
const positions = [];
// 定义物体
const objects = []
let current = 0;
// 初始化
init()
// 渲染
animate();

function init() {
// 透视相机 远端距离最好设大一点 不然会展示不全
camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 10000);
// 相机位置
camera.position.set(600, 400, 1500);
// 相机朝向位置
camera.lookAt(0, 0, 0);


scene = new THREE.Scene();

const image = document.createElement('img');
image.addEventListener('load', () => {
for (let i = 0; i < particlesTotal; i++) {
// cloneNode() 方法可创建指定的节点的精确拷贝
const object = new CSS3DSprite(image.cloneNode())
// 随机分布位置 -2000 2000的立方体内
object.position.x = Math.random() * 4000 - 2000,
object.position.y = Math.random() * 4000 - 2000,
object.position.z = Math.random() * 4000 - 2000,
scene.add(object)

objects.push(object);
}
transition()
})
image.src = './static/img/sprite.png';

// Plane
const amountX = 16;
const amountZ = 32;
const separationPlane = 150;
const offsetX = ((amountX - 1) * separationPlane) / 2;
const offsetZ = ((amountZ - 1) * separationPlane) / 2;
for (let i = 0; i < particlesTotal; i++) {
const x = (i % amountX) * separationPlane;
const z = Math.floor(i / amountX) * separationPlane;
const y = (Math.sin(x * 0.5) + Math.sin(z * 0.5)) * 200;
positions.push(x - offsetX, y, z - offsetZ);
}

// Cube
const amount = 8;
const separationCube = 150;
const offset = ((amount - 1) * separationCube) / 2;
for (let i = 0; i < particlesTotal; i++) {
const x = (i % amount) * separationCube;
const y = Math.floor((i / amount) % amount) * separationCube;
const z = Math.floor(i / (amount * amount)) * separationCube;
positions.push(x - offset, y - offset, z - offset);
}

// Random
for (let i = 0; i < particlesTotal; i++) {
positions.push(
Math.random() * 4000 - 2000,
Math.random() * 4000 - 2000,
Math.random() * 4000 - 2000
);
}

// Sphere
const radius = 750;
for (let i = 0; i < particlesTotal; i++) {
const phi = Math.acos(- 1 + (2 * i) / particlesTotal);
const theta = Math.sqrt(particlesTotal * Math.PI) * phi;
positions.push(
radius * Math.cos(theta) * Math.sin(phi),
radius * Math.sin(theta) * Math.sin(phi),
radius * Math.cos(phi)
);
}

// 渲染画布
renderer = new CSS3DRenderer();
renderer.setSize(innerWidth, window.innerHeight);
document.getElementById('container').appendChild(renderer.domElement);
controls = new TrackballControls(camera, renderer.domElement);
}


// 动画
function transition() {
const offset = current * particlesTotal * 3;
const duration = 2000;
for (let i = 0, j = offset; i < particlesTotal; i++, j += 3) {
const object = objects[i];
new TWEEN.Tween(object.position)
.to({
x: positions[j],
y: positions[j + 1],
z: positions[j + 2]
}, Math.random() * duration + duration)
.easing(TWEEN.Easing.Exponential.InOut)
.start();
}
new TWEEN.Tween(this)
.to({}, duration * 3)
.onComplete(transition)
.start();
current = (current + 1) % 4;
}

// 渲染
function animate() {
requestAnimationFrame(animate);
controls.update();
TWEEN.update();

// 让小球按照正弦时间,放大缩小
const time = performance.now();

for (let i = 0, l = objects.length; i < l; i++) {

const object = objects[i];
const scale = Math.sin((Math.floor(object.position.x) + time) * 0.002) * 0.3 + 1;
object.scale.set(scale, scale, scale);

}
renderer.render(scene, camera)
}

</script>

作者:八月十八
来源:juejin.cn/post/7294301361835147290

0 个评论

要回复文章请先登录注册