我們在項目中會有一些這樣的需求,我們可視化一個場景,需要俯視、平移、縮放,方便觀察場景中的數(shù)據(jù)或者模型,之所以把這個案例拿出來
1、這是個很實用的需求,我相信很多人會用到
2、我自己認為在實際案例中我們可以學習相關知識點更易吸收些
為了豐富本篇文章知識點,我還加入了一個物體沿軌跡運動的場景,下面代碼會介紹到,回到之前的問題three中可以利用對 OrbitControls 的設置很輕松的實現(xiàn)相關場景,代碼如下:
controls = new OrbitControls( camera, renderer.domElement );
// 移動端控制平移 縮放
// controls.touches = {
// ONE: THREE.TOUCH.PAN,
// TWO: THREE.TOUCH.DOLLY_PAN
// };
// PC 左平移,右旋轉(zhuǎn) 滾輪放大縮小 默認是右平移
controls.mouseButtons = {
LEFT: THREE.MOUSE.PAN,
MIDDLE: THREE.MOUSE.DOLLY,
RIGHT: THREE.MOUSE.ROTATE
};
// 設置最大最小視距
controls.minDistance = 20;
controls.maxDistance = 1000;
controls.autoRotate = false;
controls.enableRotate = false;
controls.enablePan = true;
需要注意的是移動端和PC是不同的配置,minDistance和maxDistance按實際需求設置即可,到這一步只是對controls的操作,已經(jīng)有能力實現(xiàn)效果了,但是camera的設置也是不可或缺的,即camera必須是俯視的
先上下整體效果圖
看下代碼:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
body {
width: 100%;
height: 100%;
}
* {
margin: 0;
padding: 0;
}
.label {
font-size: 20px;
color: #000;
font-weight: 700;
}
</style>
</head>
<body>
<div id="container"></div>
<script type="importmap">
{
"imports": {
"three": "../three-155/build/three.module.js",
"three/addons/": "../three-155/examples/jsm/"
}
}
</script>
<script type="module">
import * as THREE from 'three';
import Stats from 'three/addons/libs/stats.module.js';
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
import { CSS2DRenderer, CSS2DObject } from 'three/addons/renderers/CSS2DRenderer.js';
let stats, labelRenderer, curve, movingObjects;
let camera, scene, renderer, mesBox, target, controls;
const group = new THREE.Group();
let progress = 0; // 物體運動時在運動路徑的初始位置,范圍0~1
const velocity = 0.0005; // 影響運動速率的一個值,范圍0~1,需要和渲染頻率結(jié)合計算才能得到真正的速率
let widthImg = 200;
let heightImg = 200;
init();
initHelp();
initLight();
axesHelperWord();
animate();
// 添加一個物體
mesBox = addGeometries();
// 添加平面
addPlane();
// 添加路徑
makeCurve();
// 添加一個運動的物體
movingObjects = addMovingObjects();
window.addEventListener('mousemove', mousemoveFun);
function addPlane() {
// 創(chuàng)建一個平面 PlaneGeometry(width, height, widthSegments, heightSegments)
const planeGeometry = new THREE.PlaneGeometry(widthImg, heightImg, 1, 1);
// 創(chuàng)建 Lambert 材質(zhì):會對場景中的光源作出反應,但表現(xiàn)為暗淡,而不光亮。
const planeMaterial = new THREE.MeshLambertMaterial({
color: 0xffffff
});
const plane = new THREE.Mesh(planeGeometry, planeMaterial);
// 以自身中心為旋轉(zhuǎn)軸,繞 x 軸順時針旋轉(zhuǎn) 45 度
plane.rotation.x = -0.5 * Math.PI;
plane.position.set(0, -4, 0);
scene.add(plane);
}
function addGeometries() {
let img = new THREE.TextureLoader().load('../materials/img/view4.jpg');
img.repeat.x = img.repeat.y = 5;
const geometry = new THREE.BoxGeometry( 10, 10, 10 );
let material = new THREE.MeshPhongMaterial({
map: img,
flatShading: true,
side: THREE.DoubleSide,
transparent: 1
});
const mesh = new THREE.Mesh( geometry, material );
mesh.position.x = 0;
mesh.position.y = 0;
mesh.position.z = -20;
scene.add( mesh );
return mesh;
}
function addMovingObjects() {
let img = new THREE.TextureLoader().load('../materials/img/view3.jpg');
const geometry = new THREE.BoxGeometry( 10, 10, 10 );
const material = new THREE.MeshPhongMaterial({
map: img,
flatShading: true,
side: THREE.DoubleSide,
transparent: 1
});
const mesh = new THREE.Mesh( geometry, material );
scene.add( mesh );
return mesh;
}
function makeCurve() {
// 創(chuàng)建一個環(huán)形路徑
curve = new THREE.CatmullRomCurve3([
new THREE.Vector3(80, 0, 80),
new THREE.Vector3(80, 0, -80),
new THREE.Vector3(-80, 0, -80),
new THREE.Vector3(-80, 0, 80)
]);
curve.curveType = "catmullrom";
curve.closed = true;//設置是否閉環(huán)
curve.tension = 0; //設置線的張力,0為無弧度折線
// 為曲線添加材質(zhì)在場景中顯示出來,不顯示也不會影響運動軌跡,相當于一個Helper
const points = curve.getPoints(20);
const geometry = new THREE.BufferGeometry().setFromPoints(points);
const material = new THREE.LineBasicMaterial({
color: '#f00',
linewidth: 1
});
// Create the final object to add to the scene
const curveObject = new THREE.Line(geometry, material);
curveObject.position.y = 1;
curveObject.visible = false;
scene.add(curveObject);
}
function mousemoveFun() {
let width = widthImg;
let height = heightImg;
let newPo;
let newPoHeight;
const prevPos = camera.position.clone();
if (camera.position['x'] > width / 2) {
newPo = width / 2 - 0.01;
camera.position.set(newPo, prevPos.y, prevPos.z);
controls.target = new THREE.Vector3(newPo, 0, prevPos.z);
controls.enablePan = false;
} else if (camera.position['x'] < -width / 2) {
newPo = -width / 2 + 0.01;
camera.position.set(newPo, prevPos.y, prevPos.z);
controls.target = new THREE.Vector3(newPo, 0, prevPos.z);
controls.enablePan = false;
} else if (camera.position['z'] > height / 2) {
// 因為我們的坐標系 y軸在上 所以平移時 height 即是 z
newPoHeight = height / 2 - 0.01;
camera.position.set(prevPos.x, prevPos.y, newPoHeight);
controls.target = new THREE.Vector3(prevPos.x, 0, newPoHeight);
controls.enablePan = false;
} else if (camera.position['z'] < -height / 2) {
newPoHeight = -height / 2 + 0.01;
camera.position.set(prevPos.x, prevPos.y, newPoHeight);
controls.target = new THREE.Vector3(prevPos.x, 0, newPoHeight);
controls.enablePan = false;
} else {
controls.enablePan = true;
}
}
function init() {
camera = new THREE.PerspectiveCamera( 70, window.innerWidth / window.innerHeight, 10, 2000 );
camera.up.set(0, 1, 0);
camera.position.set(0, 100, 0);
camera.lookAt(0, 0, 0);
scene = new THREE.Scene();
// scene.background = new THREE.Color( '#ccc' );
renderer = new THREE.WebGLRenderer( { antialias: true } );
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );
labelRenderer = new CSS2DRenderer();
labelRenderer.setSize( window.innerWidth, window.innerHeight );
labelRenderer.domElement.style.position = 'absolute';
labelRenderer.domElement.style.top = '0px';
labelRenderer.domElement.style.pointerEvents = 'none';
document.getElementById( 'container' ).appendChild( labelRenderer.domElement );
controls = new OrbitControls( camera, renderer.domElement );
// 移動端控制平移 縮放
// controls.touches = {
// ONE: THREE.TOUCH.PAN,
// TWO: THREE.TOUCH.DOLLY_PAN
// };
// PC 左平移,右旋轉(zhuǎn) 滾輪放大縮小 默認是右平移
controls.mouseButtons = {
LEFT: THREE.MOUSE.PAN,
MIDDLE: THREE.MOUSE.DOLLY,
RIGHT: THREE.MOUSE.ROTATE
};
// 設置最大最小視距
controls.minDistance = 20;
controls.maxDistance = 1000;
controls.autoRotate = false;
controls.enableRotate = false;
controls.enablePan = true;
window.addEventListener( 'resize', onWindowResize );
stats = new Stats();
stats.setMode(1); // 0: fps, 1: ms
document.body.appendChild( stats.dom );
scene.add( group );
}
function initLight() {
let spotLight;
spotLight = new THREE.SpotLight( 0xffffff, 500 );
spotLight.name = 'Spot Light';
spotLight.angle = Math.PI / 5;
spotLight.penumbra = 0.1;
spotLight.position.set( 0, 5, 10 );
spotLight.castShadow = true;
spotLight.shadow.camera.near = 2;
spotLight.shadow.camera.far = 100;
spotLight.shadow.mapSize.width = 1024;
spotLight.shadow.mapSize.height = 1024;
scene.add( spotLight );
const AmbientLight = new THREE.AmbientLight(0xCCCCCC, 2);
scene.add( AmbientLight );
}
function initHelp() {
// const size = 100;
// const divisions = 5;
// const gridHelper = new THREE.GridHelper( size, divisions );
// scene.add( gridHelper );
// The X axis is red. The Y axis is green. The Z axis is blue.
const axesHelper = new THREE.AxesHelper( 100 );
scene.add( axesHelper );
}
function axesHelperWord() {
let xP = addWord('X軸');
let yP = addWord('Y軸');
let zP = addWord('Z軸');
xP.position.set(50, 0, 0);
yP.position.set(0, 50, 0);
zP.position.set(0, 0, 50);
}
function addWord(word) {
let name = `<span>${word}</span>`;
let moonDiv = document.createElement( 'div' );
moonDiv.className = 'label';
// moonDiv.textContent = 'Moon';
// moonDiv.style.marginTop = '-1em';
moonDiv.innerHTML = name;
const label = new CSS2DObject( moonDiv );
group.add( label );
return label;
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
}
function animate() {
requestAnimationFrame( animate );
// mesh繞自身中心的 y 軸自轉(zhuǎn)
if (mesBox) {
mesBox.rotation.y += 0.01;
}
if (curve && movingObjects) {
if (progress <= 1 - velocity) {
// 獲取樣條曲線指定點坐標
const point = curve.getPointAt(progress);
movingObjects.position.set(point.x, point.y, point.z);
progress += velocity;
} else {
progress = 0;
}
}
stats.update();
controls.update();
labelRenderer.render( scene, camera );
renderer.render( scene, camera );
}
</script>
</body>
</html>
這里是全部代碼,可以看出 three用的是 155版本,大家從官網(wǎng)上下載到本地改下路徑即可。圖片url你可以換成本地圖片,項目就能跑起來了。
中間立方體在做了一個環(huán)繞自身y軸旋轉(zhuǎn),外面的立方體通過獲取curve上的坐標點,沿著curve的軌跡運動。
這里有一些新的知識點:
new THREE.TextureLoader().load(‘…/materials/img/view3.jpg’);
TextureLoader是three加載圖片的方法,我們還可以這樣用
let loader = new THREE.TextureLoader();
loader.load(‘url’, (img) => {
// img 這個img就是加載完成后的圖片
});
這時就一個疑問了,同步還是異步,我們來看源碼
其實還是異步,拿到圖片后我們發(fā)現(xiàn)material有一個map參數(shù),可以接收圖片作為材質(zhì),這樣material就可以做的很豐富了,side是指物體的正反兩面,THREE.DoubleSide即兩面都要渲染,如果我們只看外面,不要設置這里,這樣也能節(jié)省一些性能文章來源:http://www.zghlxwxcb.cn/news/detail-771650.html
let material = new THREE.MeshPhongMaterial({
map: img,
flatShading: true,
side: THREE.DoubleSide,
transparent: 1
});
最后一個知識點,即物體沿著軌跡運動,我們把這一行代碼
curveObject.visible = false; 改為 true 大家就可以看到這個軌跡了。
邏輯代碼中已經(jīng)寫的很詳細了,大家也可以思考下還有哪些方法可以作為curve使用。
如果感覺有用,點個贊不過分吧?。?!文章來源地址http://www.zghlxwxcb.cn/news/detail-771650.html
到了這里,關于ThreeJS-3D教學三:平移縮放+物體沿軌跡運動的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網(wǎng)!