马黑黑 发表于 2025-7-4 07:23

那匹马

<div id="tz" style="margin:auto;width:740px;height:560px;position:relative;">
        <div id="msgDiv" style="margin:8px auto;padding:6px;position:absolute;z-index:3;">
                <span>运动模式:</span>
        </div>
</div>

<script type="importmap">
        {
                "imports": {
                        "three": "https://638183.freep.cn/638183/3dev/build/three.module.min.js",
                        "three/addons/": "https://638183.freep.cn/638183/3dev/examples/jsm/"
                }
        }
</script>

<script type="module">
    import { THREE, scene, camera, renderer, clock, basic3 } from 'https://638183.freep.cn/638183/3dev/3/3basic.js';
    import { OrbitControls } from "three/addons/controls/OrbitControls.js";
    import { GLTFLoader } from "three/addons/loaders/GLTFLoader.js";

        basic3(tz); // 启动ThreeJS运行环境

        renderer.outputEncoding = THREE.sRGBEncoding; // 渲染器启用sRGB编码

        const controls = new OrbitControls(camera, renderer.domElement); // 启用轨道控制器

        let mixer, actions = [], btns = []; // 混合器、动画数组、按钮
        const url = 'https://638183.freep.cn/638183/web/3models/Horse.glb'; // 模型路径
        //const url = '/three.js-dev/examples/models/gltf/RobotExpressive/RobotExpressive.glb'

        scene.add(new THREE.AmbientLight()); // 环境光

        // 加载模型 :gltf为模型对象
        new GLTFLoader().load(url, gltf => {
                const model = gltf.scene; // 模型场景
                const scale = 0.01; // 缩放系数
                model.scale.set(scale, scale, scale); // 缩放
                model.rotateY(Math.PI / 5); // 旋转
                model.position.y -= 1; // 下移
                scene.add(model); // 添加模型场景到ThreeJS场景
                mixer = new THREE.AnimationMixer(model); // 混合器赋值
                // 若模型集成有动画 :添加到数组、创建交互按钮
                if (gltf.animations.length > 0) {
                        gltf.animations.forEach( (a, k) => {
                                actions.push(a); // 加到数组
                                //创建按钮
                                const btn = document.createElement('button');
                                btn.style.cssText += 'margin: 4px; padding: 6px;';
                                btn.value = k;
                                btn.textContent = a.name;
                                btn.onclick = () => playModel(k); // 播放指定动画
                                btns.push(btn);
                                msgDiv.appendChild(btn);
                        });
                        playModel(); // 随机播放动画
                }
                animate();
        });

        // 循环播放ThreeJS动画
        function animate() {
                requestAnimationFrame(animate);
                camera.lookAt(0, 0, 0); // 相机总是对准场景中央
                const delta = clock.getDelta();
                mixer.update(delta);
                renderer.render(scene, camera);
        }

        // 播放模型动画 :不指定索引则随机播放
        function playModel(idx = null) {
                const playIdx = idx !== null ? idx : Math.floor(Math.random() * actions.length);
                for (let k = 0; k < actions.length; k ++) {
                        const clip = mixer.clipAction(actions);
                        k === playIdx ? (clip.play(), btns.disabled = true) : (clip.stop(), btns.disabled = false);
                }
        }
</script>

马黑黑 发表于 2025-7-4 07:24

代码:

<div id="tz" style="margin:auto;width:740px;height:560px;position:relative;">
        <div id="msgDiv" style="margin:8px auto;padding:6px;position:absolute;z-index:3;">
                <span>运动模式:</span>
        </div>
</div>

<script type="importmap">
        {
                "imports": {
                        "three": "https://638183.freep.cn/638183/3dev/build/three.module.min.js",
                        "three/addons/": "https://638183.freep.cn/638183/3dev/examples/jsm/"
                }
        }
</script>

<script type="module">
    import { THREE, scene, camera, renderer, clock, basic3 } from 'https://638183.freep.cn/638183/3dev/3/3basic.js';
    import { OrbitControls } from "three/addons/controls/OrbitControls.js";
    import { GLTFLoader } from "three/addons/loaders/GLTFLoader.js";

        basic3(tz); // 启动ThreeJS运行环境

        renderer.outputEncoding = THREE.sRGBEncoding; // 渲染器启用sRGB编码

        const controls = new OrbitControls(camera, renderer.domElement); // 启用轨道控制器

        let mixer, actions = [], btns = []; // 混合器、动画数组、按钮
        const url = 'https://638183.freep.cn/638183/web/3models/Horse.glb'; // 模型路径
        //const url = '/three.js-dev/examples/models/gltf/RobotExpressive/RobotExpressive.glb'

        scene.add(new THREE.AmbientLight()); // 环境光

        // 加载模型 :gltf为模型对象
        new GLTFLoader().load(url, gltf => {
                const model = gltf.scene; // 模型场景
                const scale = 0.01; // 缩放系数
                model.scale.set(scale, scale, scale); // 缩放
                model.rotateY(Math.PI / 5); // 旋转
                model.position.y -= 1; // 下移
                scene.add(model); // 添加模型场景到ThreeJS场景
                mixer = new THREE.AnimationMixer(model); // 混合器赋值
                // 若模型集成有动画 :添加到数组、创建交互按钮
                if (gltf.animations.length > 0) {
                        gltf.animations.forEach( (a, k) => {
                                actions.push(a); // 加到数组
                                //创建按钮
                                const btn = document.createElement('button');
                                btn.style.cssText += 'margin: 4px; padding: 6px;';
                                btn.value = k;
                                btn.textContent = a.name;
                                btn.onclick = () => playModel(k); // 播放指定动画
                                btns.push(btn);
                                msgDiv.appendChild(btn);
                        });
                        playModel(); // 随机播放动画
                }
                animate();
        });

        // 循环播放ThreeJS动画
        function animate() {
                requestAnimationFrame(animate);
                camera.lookAt(0, 0, 0); // 相机总是对准场景中央
                const delta = clock.getDelta();
                mixer.update(delta);
                renderer.render(scene, camera);
        }

        // 播放模型动画 :不指定索引则随机播放
        function playModel(idx = null) {
                const playIdx = idx !== null ? idx : Math.floor(Math.random() * actions.length);
                for (let k = 0; k < actions.length; k ++) {
                        const clip = mixer.clipAction(actions);
                        k === playIdx ? (clip.play(), btns.disabled = true) : (clip.stop(), btns.disabled = false);
                }
        }
</script>

马黑黑 发表于 2025-7-4 07:42

本帖最后由 马黑黑 于 2025-7-4 07:52 编辑

代码中相应的地方已有说明。

需要注意的是:不同的模型其内部机制有所不同,模型场景的缩放系数需要调整,模型场景在ThreeJS场景中的初始位置、初始旋转角度则根据模型的具体情况和展现模型需求而定。

另外,如果是静态模型,左上角运动模式后面的按钮不会出现,反之,如果是动态模型,则,模型中集成的全部动画名称均以按钮的方式罗列出来、点击按钮会运行对应动画。一楼的马,只有一个动画,所以按钮是灰色不可点的。

可以将代码存为自己电脑里的 .html 文档,并将外层div宽高设为更大(例如 1600 * 900),然后用浏览器打开,观感可能更好。但是若替换模型文件,请确保URL源支持跨域,若是本地资源,则请确保你的电脑已经启动了虚拟Web服务且 .html 文档和模型均在Web服务器虚拟目录内。

下面是一个多动画模型的演示例子:ThreeJS:罗列并运行GLTF模型所有动画

梦江南 发表于 2025-7-4 07:47

问好黑黑老师,谢谢辛苦!{:4_190:}

花飞飞 发表于 2025-7-4 14:02

这又是一个新的模型马,跟之前万马奔腾用的不相同。。。。

花飞飞 发表于 2025-7-4 14:02

原来多个动画就会按顺序排出来,一个动画就灰色按不动。。
记得飞鸟也只有一个动画,当时并没留意有按纽存在。。。。{:4_173:}

花飞飞 发表于 2025-7-4 14:09

马黑黑 发表于 2025-7-4 07:42
代码中相应的地方已有说明。

需要注意的是:不同的模型其内部机制有所不同,模型场景的缩放系数需要调整 ...

试了本地大屏,设1900*900,因背景相同,看上去就是全屏效果
模型替换试了之前的飞鸟Parrot.glb,{:4_173:}也好看,只有一个灰色按纽

杨帆 发表于 2025-7-4 17:26

“不同的模型其内部机制有所不同,模型场景的缩放系数需要调整,模型场景在ThreeJS场景中的初始位置、初始旋转角度则根据模型的具体情况和展现模型需求而定。”,这个调整就不易了{:4_173:}

马黑黑 发表于 2025-7-4 17:51

杨帆 发表于 2025-7-4 17:26
“不同的模型其内部机制有所不同,模型场景的缩放系数需要调整,模型场景在ThreeJS场景中的初始位置、初始 ...

手头有模型,有运行环境,可以多加尝试,很快上手

马黑黑 发表于 2025-7-4 17:52

花飞飞 发表于 2025-7-4 14:09
试了本地大屏,设1900*900,因背景相同,看上去就是全屏效果
模型替换试了之前的飞鸟Parrot.glb,{:4_17 ...

这个三个禽类的模型,和那匹马一样,都只有一个动画

马黑黑 发表于 2025-7-4 17:53

花飞飞 发表于 2025-7-4 14:02
原来多个动画就会按顺序排出来,一个动画就灰色按不动。。
记得飞鸟也只有一个动画,当时并没留意有按纽存 ...

那个没有遍历动画,只播放第 0 个索引的动画即第一个动画

马黑黑 发表于 2025-7-4 17:54

梦江南 发表于 2025-7-4 07:47
问好黑黑老师,谢谢辛苦!

{:4_180:}

杨帆 发表于 2025-7-4 18:01

马黑黑 发表于 2025-7-4 17:51
手头有模型,有运行环境,可以多加尝试,很快上手

多加尝试,跟着老师学,谢谢马老师{:4_191:}

花飞飞 发表于 2025-7-4 19:41

马黑黑 发表于 2025-7-4 17:52
这个三个禽类的模型,和那匹马一样,都只有一个动画

嗯哪,一个到多个,越来越先进了

花飞飞 发表于 2025-7-4 19:42

马黑黑 发表于 2025-7-4 17:53
那个没有遍历动画,只播放第 0 个索引的动画即第一个动画

好哒,只有一个动画就不用遍历了。。。看上去也是无限循环

马黑黑 发表于 2025-7-4 20:10

花飞飞 发表于 2025-7-4 19:42
好哒,只有一个动画就不用遍历了。。。看上去也是无限循环

差不多

马黑黑 发表于 2025-7-4 20:11

花飞飞 发表于 2025-7-4 19:41
嗯哪,一个到多个,越来越先进了

模型不好找:要么太大,要么没动画

红影 发表于 2025-7-4 20:46

看到这匹马,想起黑黑做的无数的马匹如云般的奔涌。
去看了索引里的罗列并运行GLTF模型所有动画,居然有那么多不同的动作呢。

红影 发表于 2025-7-4 20:49

做这个好像要用到很多封装的东西啊。

花飞飞 发表于 2025-7-4 20:54

马黑黑 发表于 2025-7-4 20:10
差不多

{:4_187:}
页: [1] 2 3
查看完整版本: 那匹马