马黑黑 发表于 2025-5-10 17:31

three.js : 与音频联动的球体动画

本帖最后由 马黑黑 于 2025-5-10 19:25 编辑 <br /><br /><style>
        #btnPrev { box-shadow: 2px 2px 6px rgba(0,0,0,.5); }
        #btnPrev:hover { box-shadow: 2px 2px 12px rgba(0,0,0,.5); font-weight: bold; }
</style>

<div id="hEdiv"><pre id="hEpre">
&lt;audio id="aud" src="https://music.163.com/song/media/outer/url?id=1464843172" autoplay loop controls&gt;&lt;/audio&gt;

&lt;script type="module"&gt;

import * as THREE from 'https://esm.sh/three';
import { OrbitControls } from "https://esm.sh/three/examples/jsm/controls/OrbitControls";

let rotRaf, isPaused = true;

const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.z = 20;
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);

const geometry = new THREE.SphereGeometry(2, 32, 32, 0, 2 * Math.PI, 0, 2 * Math.PI);
for (let i = 0; i &lt;= 8; i++) {
        const material = new THREE.MeshNormalMaterial({
                transparent: true,
                opacity: 0.6,
                side: THREE.DoubleSide
        });
        const ball = new THREE.Mesh(geometry, material);
        const radian = (360 / 8) * (Math.PI / 180);
        if (i &lt; 8) ball.position.set(8 * Math.sin(radian * i), 8 * Math.cos(radian * i), 0);
        scene.add(ball);
}

const controls = new OrbitControls(camera, renderer.domElement);
controls.enablePan = true;
controls.maxPolarAngle = 1.5;
controls.minPolarAngle = 0.0;
controls.autoRotate = true;
controls.dampingFactor = 0.5;

isPaused = aud.paused;

const mState = () =&gt; {
        isPaused = aud.paused;
        aud.paused ? cancelAnimationFrame(rotRaf) : rotating();
};

const rotating = () =&gt; {
        if (isPaused ) return;
        controls.update();
        renderer.render(scene, camera);
        rotRaf = requestAnimationFrame(rotating);
};

aud.onplaying = aud.onpause = () =&gt; mState();
aud.onseeked = () => cancelAnimationFrame(rotRaf);

rotating();

&lt;/script&gt;

&lt;style&gt;
        body { margin: 0; }
        audio { position: absolute; bottom: 20px; right: 20px; }
&lt;/style&gt;
</pre></div>
<blockquote><button id="btnPrev">运行代码</button></button></blockquote>

<script type="module">
import hlight from 'https://638183.freep.cn/638183/web/helight/helight1.js';
hlight.hl(hEdiv, hEpre);

btnPrev.onclick = () => {
        const value = hEpre.textContent;
        const previewWindow = window.open('', 'preview1', 'width=1200,height=768,top=100,left=100');
        previewWindow.document.open();
        previewWindow.document.write(value);
        setTimeout(function() { previewWindow.document.title = "预览" }, 100);
        previewWindow.document.close();
};
</script>

马黑黑 发表于 2025-5-10 17:45

本帖最后由 马黑黑 于 2025-5-10 17:50 编辑

本帖,在 three.js : 画球 - 马黑黑教程专版 - 花潮论坛 - Powered by Discuz! 第二个示例基础加入一些其它元素,比如音频、three.js 的 OrbitControls 对象(相机控件,用于控制相机)。

设置好相关 OrbitControls 控件参数,尤其是 autoRotate,就能启用相机绕场景旋转,但仅是启用,要真正实现旋转,还得在动画部分更新(update)。一楼代码中,我们将 OrbitControls 实例化 为 controls,所以,rotating 动画函数中使用 controls.update() 来真正启动相机的旋转。

注意,相机的旋转是绕场景 scene 进行的,观者看到的则是场景中 3d对象 的旋转。

音频联动方面,首先设置一个函数 mState,它根据 audio 音频控件的暂停与否来决定控制three动画开关 isPaused 的布尔值,并直接参与动画播放暂停的管控。其中的 rotRaf 变量用于管理 requestAnimationFrame(请求关键帧动画);其次,监听 audio 音频控件的 pause 和 playing 两个事件,用以联动管理动画。

请求关键帧动画必须做好管理,否则会出现一些意外,比如动画无法运行或运行的越来越快,变量 rotRaf 的赋值与注销因此至关重要。


9个球体自身没有任何动画,但给人的感觉是它们也在不停地旋转。原理:球体使用了双面渲染,side: THREE.DoubleSide,就会出现球面上的“斑块”,由于相对密集,相机在旋转的时候在这些斑块上掠过,造成球体旋转的错觉。

花飞飞 发表于 2025-5-10 18:03

{:4_173:}
刚看到这神奇的效果,过来看看说明。。。
我的天,它本身没有转的么,从看到的第一眼就觉得它在自转

花飞飞 发表于 2025-5-10 18:21

本帖最后由 花飞飞 于 2025-5-10 18:23 编辑

两边的 计算还是有所不同的,粒子数量都是8,这里显示的是9个。
整体合在一起旋转,还要透视各方面都对得上,3D效果功能强大

花飞飞 发表于 2025-5-10 18:30

旋转之前立方体里出现过,最近这个THREE出现好多新的名词,虽然每次看都是满头黑线,看不懂
但效果足够炫酷。。
现在音乐也能够一起联动了,是不是不久就可以看到新效果的实例贴子了。{:4_173:}

马黑黑 发表于 2025-5-10 18:37

花飞飞 发表于 2025-5-10 18:03
刚看到这神奇的效果,过来看看说明。。。
我的天,它本身没有转的么,从看到的第一眼就觉得它 ...

心甘情愿的错觉{:4_170:}

马黑黑 发表于 2025-5-10 18:39

本帖最后由 马黑黑 于 2025-5-10 18:43 编辑

花飞飞 发表于 2025-5-10 18:21
两边的 计算还是有所不同的,粒子数量都是8,这里显示的是9个。
整体合在一起旋转,还要透视各方面都对得 ...
那个for语句你看看, 小于,小于等于

花飞飞 发表于 2025-5-10 18:44

马黑黑 发表于 2025-5-10 18:37
心甘情愿的错觉

利用错觉可以出许多精美效果
没办法,你选用的材质太{:4_173:}亮眼,头一回见

马黑黑 发表于 2025-5-10 18:44

花飞飞 发表于 2025-5-10 18:30
旋转之前立方体里出现过,最近这个THREE出现好多新的名词,虽然每次看都是满头黑线,看不懂
但效果足够炫 ...

这是两种不同形式的旋转:之前的是立方体绕自己的中心旋转,现在这个是相机绕场景旋转

马黑黑 发表于 2025-5-10 18:44

花飞飞 发表于 2025-5-10 18:30
旋转之前立方体里出现过,最近这个THREE出现好多新的名词,虽然每次看都是满头黑线,看不懂
但效果足够炫 ...

现在你都可以做到帖子里面去了

马黑黑 发表于 2025-5-10 18:45

花飞飞 发表于 2025-5-10 18:44
利用错觉可以出许多精美效果
没办法,你选用的材质太亮眼,头一回见

{:4_191:}

花飞飞 发表于 2025-5-10 18:45

马黑黑 发表于 2025-5-10 18:39
那个for语句你看看, 小于,小于等于

{:4_170:}看了。。知道算的方法不同。。
最近这3D画儿跟星球一样不好懂

花飞飞 发表于 2025-5-10 18:50

马黑黑 发表于 2025-5-10 18:44
这是两种不同形式的旋转:之前的是立方体绕自己的中心旋转,现在这个是相机绕场景旋转

{:4_173:}原来有这样的区别
这个可以理解,像是舞台上人在唱歌,摄像大哥拿着机器围着歌手绕转,出来的效果就特写中的特写

花飞飞 发表于 2025-5-10 18:51

马黑黑 发表于 2025-5-10 18:44
现在你都可以做到帖子里面去了

加个背景就成了{:4_173:}但还是想看你做的效果

花飞飞 发表于 2025-5-10 18:52

马黑黑 发表于 2025-5-10 18:45


喝得晕乎乎的一会

马黑黑 发表于 2025-5-10 19:26

花飞飞 发表于 2025-5-10 18:52
喝得晕乎乎的一会

挺好挺好

马黑黑 发表于 2025-5-10 19:26

花飞飞 发表于 2025-5-10 18:51
加个背景就成了但还是想看你做的效果

暂时不做

马黑黑 发表于 2025-5-10 19:26

花飞飞 发表于 2025-5-10 18:50
原来有这样的区别
这个可以理解,像是舞台上人在唱歌,摄像大哥拿着机器围着歌手绕转,出来的 ...

对对对,就是这个意思

花飞飞 发表于 2025-5-10 19:46

马黑黑 发表于 2025-5-10 19:26
挺好挺好

{:4_181:}来看看怎么个好法

花飞飞 发表于 2025-5-10 19:47

马黑黑 发表于 2025-5-10 19:26
暂时不做

好在是暂时的{:4_170:}
页: [1] 2 3 4 5
查看完整版本: three.js : 与音频联动的球体动画