three.js 汽车行驶效果

这篇具有很好参考价值的文章主要介绍了three.js 汽车行驶效果。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

实现原理是使用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();

总结

  1. 解耦:依赖的scene, renderer参数是通过构造函数传到Car和Cars对象中的
  2. 汽车行驶和转向等方法都是异步方法,可以避免回调地狱,这样汽车多段行驶的代码会写的比较清晰
  3. 在实现并完善功能的过程中不断重构:回调地狱的实现方式-->调用moveCar和rotateCar时直接传递坐标,很多坐标及转向数据和方法调用掺和在一起,看着眼花-->坐标和转向数据和方法调用逻辑分离,看着很清晰

运行效果

three.js 汽车行驶效果文章来源地址https://www.toymoban.com/news/detail-748958.html

到了这里,关于three.js 汽车行驶效果的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • three.js实现雷达扫描效果(纹理贴图)

    three.js实现雷达扫描效果(纹理贴图) 图例 步骤 创建两个平面,分别纹理贴图,底图模型.add(光波模型) 关闭材质的深度测试 光波旋转 代码 图片(透明的)

    2024年02月01日
    浏览(45)
  • Three.js--》实现图片转3D效果展示

    目录 项目搭建 初始化three.js基础代码 加载图片纹理 设置着色器 今天简单实现一个three.js的小Demo,加强自己对three知识的掌握与学习,只有在项目中才能灵活将所学知识运用起来,话不多说直接开始。 项目搭建 本案例还是借助框架书写three项目,借用vite构建工具搭建vue项目

    2024年02月08日
    浏览(61)
  • 【js&threeJS】入门three,并实现3D汽车展示厅,附带全码

    首先放个最终效果图:   三维(3D)概念: 三维(3D)是一个描述物体在三个空间坐标轴上的位置和形态的概念。相比于二维(2D)只有长度和宽度的平面,三维增加了高度或深度这一维度 在三维空间中,我们使用三个独立的坐标轴来描述物体的位置。通常使用笛卡尔坐标系

    2024年02月11日
    浏览(130)
  • Three.js 实现模型材质分解,拆分,拆解效果

    原理:通过修改模型材质的 x,y,z 轴坐标 positon.set( x,y,z) 来实现拆解,分解的效果。 注意:支持模型材质position 修改的材质类型为 type=“Mesh” ,其他类型的材质修改了 position 可能没有实际效果 在上一篇 Three.js加载外部glb,fbx,gltf,obj 模型文件 的文章基础上新增一个 setModelMeshD

    2024年02月11日
    浏览(82)
  • Three.js实现模型,模型材质可拖拽效果 DragControls

    DragControls:是一个用于在Three.js中实现拖拽控制的辅助类。它简化了在Three.js中实现拖拽物体的过程。 DragControls的构造函数接受三个参数: objects:一个包含需要进行拖拽的物体的数组。 camera:相机对象,用于将屏幕坐标转换为三维空间坐标。 domElement:渲染器的DOM元素,用于

    2024年02月11日
    浏览(41)
  • three.js实现鼠标点击控制物体移动(带动画效果,位置精确)

    通过查阅各种资料,最终确定解决方案,动画效果使用gsap组件库实现,也可不用,代码稍作修改即可。解决鼠标点击坐标偏移问题,复制可直接运行。 完整代码如下:

    2024年02月07日
    浏览(61)
  • vue2+three.js实现类似VR、3D全景效果

    效果图: 俩图标是我自己加的前进后退按钮,也是百度了好久,再加上GPT的帮助,才给搞出来。因为需求急,都不看官方文档,百度到一个能跑的demo之后改吧改吧,就先用着了。 下面是代码: 这里 代码有很多用不到的地方和需要优化的地方,我是来不及改了,就先这样吧

    2024年02月15日
    浏览(54)
  • vue2+three.js+blender(实现3d 模型引入并可点击效果)

    2023.9.13今天我学习了如何把3d建模里面的模型引入到vue中,并可以实现拖动,点击的效果: 首先安装: npm install three 相关代码如下:  如果没有图片可以去 Three.js--》建模软件如何加载外部3D模型?_threejs加载3d模型_亦世凡华、的博客-CSDN博客

    2024年02月03日
    浏览(64)
  • Three.js--》Gsap动画库基本使用与原理

    目录 Gsap动画库使用讲解 Gsap动画库基本使用 修改自适应画面及双击进入全屏 设置stats性能监视器 GSAP的全名是GreenSock Animation Platform,是一个从flash时代一直发展到今天的专业动画库,今天将其与three.js进行结合,看看能够创作出怎样的效果,首先我们先要理解动画渲染循环的

    2024年02月02日
    浏览(41)
  • Three.js中的3D文字效果

    对于一些设计网页中经常会出现一些3D的文字效果,本文将利用Three.js实现各种动画WebGL文本输入效果。 示例效果 原文章 通常情况下,文本网格是2D的平面形状,我们所要实现的3D文本形状则是要在2D的平面下,再生成z值形成一个立体的效果。 首先,我们创建一个canvas元素,

    2024年02月02日
    浏览(103)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包