|
实现原理是使用TWEEN.Tween实现动画效果
实现
汽车模型加载
使用Promise编写模型的异步加载方法
参数position是汽车初始位置,参数rotation是汽车初始朝向- Car.prototype.loadCar = function (position, rotation) {
- let onProgress = function (xhr) {
- };
- return new Promise((resolve, reject) => {
- if (!this.model) {
- let loader = new THREE.GLTFLoader();
- loader.load(this.url, gltf => {
- const model = gltf.scene || gltf.scenes[0];
- model.position.x = position.x;
- model.position.y = position.y;
- model.position.z = position.z;
- model.scale.set(0.25, 0.25, 0.25);
- model.rotation.set(rotation.x, rotation.y, rotation.z);
- this.model = model;
- this.scene.add(model);
- resolve(model);
- }, onProgress, xhr => {
- console.error(xhr);
- console.info('模型 ' + url + ' 加载失败');
- reject(xhr);
- });
- } else {
- resolve(this.model);
- }
- });
- }
复制代码 调用:
第1个参数是汽车初始位置,第2个参数表示汽车初始朝向:西- await car.loadCar(positions[0], car.WEST);
复制代码 汽车行驶
参数start是行驶起点位置,参数end是行驶终点位置,参数speed是速度
this.model是汽车模型,onUpdate事件中,不断更新它的position
this.label是汽车车牌号标签,onUpdate事件中,不断更新它的position- Car.prototype.moveCar = function (start, end, speed) {
- let distance = this.distance(start, end);
- let time = distance / speed * 1000;
- return new Promise((resolve, reject) => {
- this.tween = new TWEEN.Tween({
- x: start.x,
- y: start.y,
- z: start.z
- }).to({
- x: end.x,
- y: end.y,
- z: end.z
- }, time).start().onUpdate(e => {
- if (this.model) {
- this.model.position.x = e.x;
- this.model.position.y = e.y;
- this.model.position.z = e.z;
- }
- if (this.label) {
- this.label.position.x = e.x;
- this.label.position.y = e.y + 1.2;
- this.label.position.z = e.z;
- }
- }).onComplete(() => {
- TWEEN.remove(this.tween);
- resolve();
- });
- });
- }
复制代码 汽车转弯
参数start是动画开始时的汽车朝向,end是动画结束时的汽车朝向- Car.prototype.rotateCar = function (start, end) {
- return new Promise((resolve, reject) => {
- this.tween = new TWEEN.Tween({
- x: start.x,
- y: start.y,
- z: start.z
- }).to({
- x: end.x,
- y: end.y,
- z: end.z
- }, 300).start().onUpdate(e => {
- if (this.model) {
- this.model.rotation.set(e.x, e.y, e.z);
- }
- }).onComplete(() => {
- TWEEN.remove(this.tween);
- resolve();
- });
- });
- }
复制代码 汽车行驶多段路线
上述汽车行驶和汽车转弯方法都是异步方法,所以避免了回调地狱,不然下面的多段行驶及转弯就不好写了- Cars.prototype.carLine1 = function () {
- if (!this.run) return;
- let car = new Car(this.scene, this.renderer, './models/车红.glb');
- this.cars.push(car);
- let positions = [
- { x: -121, y: 1.5, z: -16 },
- { x: -130.5, y: 1.5, z: -16 },
- { x: -130.5, y: 1.5, z: 4 },
- { x: -82, y: 1.5, z: 4 },
- { x: -82, y: 1.5, z: 14.7 },
- { x: -18.8, y: 1.5, z: 14.7 },
- { x: -18.8, y: 1.5, z: 70 },
- ];
- let speed = 5;
- setTimeout(async () => {
- await car.loadCar(
- positions[0],
- car.WEST);
- car.showLabel(positions[0], "皖A67893");
- await car.moveCar(
- positions[0],
- positions[1],
- speed);
- await car.rotateCar(
- car.WEST,
- car.SOUTH);
- await car.moveCar(
- positions[1],
- positions[2],
- speed);
- await car.rotateCar(
- car.SOUTH,
- car.EAST);
- await car.moveCar(
- positions[2],
- positions[3],
- speed);
- await car.rotateCar(
- car.EAST,
- car.SOUTH);
- await car.moveCar(
- positions[3],
- positions[4],
- speed);
- await car.rotateCar(
- car.SOUTH,
- car.EAST);
- await car.moveCar(
- positions[4],
- positions[5],
- speed);
- await car.rotateCar(
- car.EAST,
- car.SOUTH);
- await car.moveCar(
- positions[5],
- positions[6],
- speed);
- car.unloadCar();
- this.carLine1(2000);
- }, 5000);
- }
- Cars.prototype.carLine2 = function () {
- if (!this.run) return;
- let car = new Car(this.scene, this.renderer, './models/车蓝.glb');
- this.cars.push(car);
- let positions = [
- { x: -5, y: 1.5, z: 70 },
- { x: -5, y: 1.5, z: 14.7 },
- { x: 70, y: 1.5, z: 14.7 }
- ];
- let speed = 5;
- setTimeout(async () => {
- await car.loadCar(
- positions[0],
- car.NORTH);
- car.showLabel(positions[0], "皖AD887U");
- await car.moveCar(
- positions[0],
- positions[1],
- speed);
- await car.rotateCar(
- car.NORTH,
- car.EAST);
- await car.moveCar(
- positions[1],
- positions[2],
- speed);
- car.unloadCar();
- this.carLine2(3000);
- }, 6000);
- }
复制代码 汽车行驶多段路线改进
上述汽车行驶多段路线的代码可以改进:- // 汽车朝向
- let EAST = { x: 0, y: 1.5707963, z: 0 };
- let SOUTH = { x: 0, y: 0, z: 0 };
- let WEST = { x: 0, y: -1.5707963, z: 0 };
- let NORTH = { x: 0, y: 3.1415926, z: 0 };
- Cars.prototype.carLine1 = function () {
- if (!this.run) return;
- let car = new Car(this.scene, this.renderer, './models/车红.glb');
- this.cars.push(car);
- let positions = [
- { x: -121, y: 1.5, z: -16 },
- { x: -130.5, y: 1.5, z: -16 },
- [WEST, SOUTH],
- { x: -130.5, y: 1.5, z: 4 },
- [SOUTH, EAST],
- { x: -82, y: 1.5, z: 4 },
- [EAST, SOUTH],
- { x: -82, y: 1.5, z: 14.7 },
- [SOUTH, EAST],
- { x: -18.8, y: 1.5, z: 14.7 },
- [EAST, SOUTH],
- { x: -18.8, y: 1.5, z: 70 },
- ];
- let speed = 5;
- setTimeout(async () => {
- await car.loadCar(
- positions[0],
- WEST);
- car.showLabel(positions[0], "皖A67893");
- for (let i = 1; i < positions.length; i++) {
- if (positions[i].length) {
- await car.rotateCar(positions[i][0], positions[i][1]);
- } else {
- let start = positions[i - 1].length ? positions[i - 2] : positions[i - 1];
- await car.moveCar(start, positions[i], speed);
- }
- }
- car.unloadCar();
- this.carLine1(2000);
- }, 5000);
- }
复制代码 调用- let cars = new Cars(app.scene, app.renderer);
- cars.carLine1();
- cars.carLine2();
复制代码 显示车牌号
- Car.prototype.showLabel = function (position, text) {
- let canvasDraw = new CanvasDraw();
- let canvasTexture = canvasDraw.drawCarLabel(THREE, this.renderer, text, '#006688'); //标签
- let spriteMaterial = new THREE.SpriteMaterial({
- map: canvasTexture,
- color: 0xffffff,
- depthTest: false,
- side: THREE.DoubleSide,
- sizeAttenuation: false,
- transparent: true,
- opacity: 0.8
- });
- let sprite = new THREE.Sprite(spriteMaterial);
- sprite.scale.set(0.2, 0.1, 0.2)
- sprite.position.x = position.x;
- sprite.position.y = position.y + 1.2;
- sprite.position.z = position.z;
- this.label = sprite;
- this.scene.add(sprite);
- return sprite;
- }
复制代码 完整代码
car.js
- // 汽车
- let Car = (function () {
- function Car(scene, renderer, url) {
- this.scene = scene;
- this.renderer = renderer;
- this.url = url;
- this.clock = new THREE.Clock();
- }
- Car.prototype.loadCar = function (position, rotation) {
- let onProgress = function (xhr) {
- };
- return new Promise((resolve, reject) => {
- if (!this.model) {
- let loader = new THREE.GLTFLoader();
- loader.load(this.url, gltf => {
- const model = gltf.scene || gltf.scenes[0];
- model.position.x = position.x;
- model.position.y = position.y;
- model.position.z = position.z;
- model.scale.set(0.25, 0.25, 0.25);
- model.rotation.set(rotation.x, rotation.y, rotation.z);
- this.model = model;
- this.scene.add(model);
- resolve(model);
- }, onProgress, xhr => {
- console.error(xhr);
- console.info('模型 ' + url + ' 加载失败');
- reject(xhr);
- });
- } else {
- resolve(this.model);
- }
- });
- }
- Car.prototype.unloadCar = function () {
- this.stopTween();
- this.removeModel();
- this.removeLabel();
- }
- Car.prototype.stopTween = function () {
- if (this.tween) {
- TWEEN.remove(this.tween);
- } else {
- setTimeout(() => {
- this.stopTween();
- }, 100);
- }
- }
- Car.prototype.removeModel = function () {
- if (this.model) {
- this.scene.remove(this.model);
- } else {
- setTimeout(() => {
- this.removeModel();
- }, 100);
- }
- }
- Car.prototype.removeLabel = function () {
- if (this.label) {
- this.scene.remove(this.label);
- } else {
- setTimeout(() => {
- this.removeLabel();
- }, 100);
- }
- }
- Car.prototype.moveCar = function (start, end, speed) {
- let distance = this.distance(start, end);
- let time = distance / speed * 1000;
- return new Promise((resolve, reject) => {
- this.tween = new TWEEN.Tween({
- x: start.x,
- y: start.y,
- z: start.z
- }).to({
- x: end.x,
- y: end.y,
- z: end.z
- }, time).start().onUpdate(e => {
- if (this.model) {
- this.model.position.x = e.x;
- this.model.position.y = e.y;
- this.model.position.z = e.z;
- }
- if (this.label) {
- this.label.position.x = e.x;
- this.label.position.y = e.y + 1.2;
- this.label.position.z = e.z;
- }
- }).onComplete(() => {
- TWEEN.remove(this.tween);
- resolve();
- });
- });
- }
- Car.prototype.rotateCar = function (start, end) {
- return new Promise((resolve, reject) => {
- this.tween = new TWEEN.Tween({
- x: start.x,
- y: start.y,
- z: start.z
- }).to({
- x: end.x,
- y: end.y,
- z: end.z
- }, 300).start().onUpdate(e => {
- if (this.model) {
- this.model.rotation.set(e.x, e.y, e.z);
- }
- }).onComplete(() => {
- TWEEN.remove(this.tween);
- resolve();
- });
- });
- }
- Car.prototype.showLabel = function (position, text) {
- let canvasDraw = new CanvasDraw();
- let canvasTexture = canvasDraw.drawCarLabel(THREE, this.renderer, text, '#006688'); //标签
- let spriteMaterial = new THREE.SpriteMaterial({
- map: canvasTexture,
- color: 0xffffff,
- depthTest: false,
- side: THREE.DoubleSide,
- sizeAttenuation: false,
- transparent: true,
- opacity: 0.8
- });
- let sprite = new THREE.Sprite(spriteMaterial);
- sprite.scale.set(0.2, 0.1, 0.2)
- sprite.position.x = position.x;
- sprite.position.y = position.y + 1.2;
- sprite.position.z = position.z;
- this.label = sprite;
- this.scene.add(sprite);
- return sprite;
- }
- Car.prototype.distance = function (p1, p2) {
- return Math.sqrt(Math.pow(p1.x - p2.x, 2) + Math.pow(p1.y - p2.y, 2) + Math.pow(p1.z - p2.z, 2));
- }
- return Car;
- })();
复制代码 cars.js
- // 多个车辆
- let Cars = (function () {
- // 汽车朝向
- let EAST = { x: 0, y: 1.5707963, z: 0 };
- let SOUTH = { x: 0, y: 0, z: 0 };
- let WEST = { x: 0, y: -1.5707963, z: 0 };
- let NORTH = { x: 0, y: 3.1415926, z: 0 };
- function Cars(scene, renderer) {
- this.scene = scene;
- this.renderer = renderer;
- this.cars = [];
- this.run = true;
- }
- Cars.prototype.carLine1 = function () {
- if (!this.run) return;
- let car = new Car(this.scene, this.renderer, './models/车红.glb');
- this.cars.push(car);
- let positions = [
- { x: -121, y: 1.5, z: -16 },
- { x: -130.5, y: 1.5, z: -16 },
- [WEST, SOUTH],
- { x: -130.5, y: 1.5, z: 4 },
- [SOUTH, EAST],
- { x: -82, y: 1.5, z: 4 },
- [EAST, SOUTH],
- { x: -82, y: 1.5, z: 14.7 },
- [SOUTH, EAST],
- { x: -18.8, y: 1.5, z: 14.7 },
- [EAST, SOUTH],
- { x: -18.8, y: 1.5, z: 70 },
- ];
- let speed = 5;
- setTimeout(async () => {
- await car.loadCar(
- positions[0],
- WEST);
- car.showLabel(positions[0], "皖A67893");
- for (let i = 1; i < positions.length; i++) {
- if (positions[i].length) {
- await car.rotateCar(positions[i][0], positions[i][1]);
- } else {
- let start = positions[i - 1].length ? positions[i - 2] : positions[i - 1];
- await car.moveCar(start, positions[i], speed);
- }
- }
- car.unloadCar();
- this.carLine1(2000);
- }, 5000);
- }
- Cars.prototype.carLine2 = function () {
- if (!this.run) return;
- let car = new Car(this.scene, this.renderer, './models/车蓝.glb');
- this.cars.push(car);
- let positions = [
- { x: -5, y: 1.5, z: 70 },
- { x: -5, y: 1.5, z: 14.7 },
- { x: 70, y: 1.5, z: 14.7 }
- ];
- let speed = 5;
- setTimeout(async () => {
- await car.loadCar(
- positions[0],
- NORTH);
- car.showLabel(positions[0], "皖AD887U");
- await car.moveCar(
- positions[0],
- positions[1],
- speed);
- await car.rotateCar(
- NORTH,
- EAST);
- await car.moveCar(
- positions[1],
- positions[2],
- speed);
- car.unloadCar();
- this.carLine2(3000);
- }, 6000);
- }
- Cars.prototype.clear = function () {
- this.run = false;
- this.cars.forEach(car => {
- car.unloadCar();
- });
- }
- return Cars;
- })();
复制代码 调用
- // 显示汽车
- function showCars() {
- cars = new Cars(app.scene, app.renderer);
- cars.carLine1();
- cars.carLine2();
- }
- // 清除汽车
- function clearCars() {
- cars.clear();
- }
- // 显示汽车
- showCars();
复制代码 总结
- 解耦:依赖的scene, renderer参数是通过构造函数传到Car和Cars对象中的
- 汽车行驶和转向等方法都是异步方法,可以避免回调地狱,这样汽车多段行驶的代码会写的比较清晰
- 在实现并完善功能的过程中不断重构:回调地狱的实现方式-->调用moveCar和rotateCar时直接传递坐标,很多坐标及转向数据和方法调用掺和在一起,看着眼花-->坐标和转向数据和方法调用逻辑分离,看着很清晰
运行效果
来源:https://www.cnblogs.com/s0611163/p/17879849.html
免责声明:由于采集信息均来自互联网,如果侵犯了您的权益,请联系我们【E-Mail:cb@itdo.tech】 我们会及时删除侵权内容,谢谢合作! |
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有账号?立即注册
x
|