马黑黑 发表于 2025-5-14 20:28

牵动

本帖最后由 马黑黑 于 2025-5-14 21:03 编辑 <br /><br /><style>
        #tz { --state: running; margin: 30px 0; left: calc(50% - 81px); transform: translateX(-50%); width: clamp(600px, 90vw, 1400px); min-height: 80vh; aspect-ratio: 16/9; background: url('https://638183.freep.cn/638183/t24/5/qmyb.jpg') no-repeat center/cover; box-shadow: 2px 2px 8px #000; display: grid; place-items: center; z-index: 1; position: relative; }
        #btnFs { right: 20px; top: 20px; text-align: center; color: darkgreen; border-color: teal !important; }
        #btnFs:hover { color: red; }
        #player { position: absolute; z-index: 10; cursor: pointer; animation: rot 8s infinite linear var(--state); }
        #player:hover { filter: hue-rotate(60deg); }
        #vid {position: absolute; width: 100%; height: 100%; object-fit: cover; mask: radial-gradient(transparent 20%, red); -webkit-mask: radial-gradient(transparent 20%, red); pointer-events: none; }
        @keyframes rot { to { transform: rotate(360deg); } }
</style>

<div id="tz">
        <audio id="aud" src="https://music.163.com/song/media/outer/url?id=5275053" autoplay loop></audio>
        <video id="vid" src="https://bpic.588ku.com/video_listen/588ku_video/22/11/05/03/10/58/video636563c24169e.mp4" autoplay loop muted></video>
        <img id="player" src="https://638183.freep.cn/638183/small/fi1.webp" width="10%" title="播放/暂停" />
</div>

<script type="module">
        import * as THREE from 'https://esm.sh/three';
        import { FS } from 'https://638183.freep.cn/638183/web/ku/fscreen.js';

        let step = 0.01, pos = 0, isPaused = false, raf;
        let playerLeft = (tz.offsetWidth - player.offsetWidth) / 2;;

        const scene = new THREE.Scene;
        const camera = new THREE.PerspectiveCamera(75, tz.offsetWidth / tz.offsetHeight, 0.1, 1000);
        camera.position.set(0, 8, 20);
        const renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
        renderer.setSize(tz.offsetWidth, tz.offsetHeight);
        tz.appendChild(renderer.domElement);

        const geometry = new THREE.CylinderGeometry(1, 1, 10, 16);
        const texture = new THREE.TextureLoader().load('https://638183.freep.cn/638183/small/texture/wd3.jpg', () => renderer.render(scene, camera));

        const material = new THREE.MeshBasicMaterial({ color: 'tan', map: texture });       
        const cylinder = new THREE.Mesh(geometry, material);
        cylinder.rotateX(2);
        scene.add(cylinder);

        const animate = () => {
                pos -= step;
                let xx = playerLeft + pos * 15;
                player.style.setProperty('left', xx + 'px');
                if(pos >= 15 || pos <= -15) step = -step;
                cylinder.rotation.y += step;
                cylinder.position.x = pos;
                renderer.render(scene, camera);
                isPaused ?raf = requestAnimationFrame(animate) : cancelAnimationFrame(raf);
        };

        window.onresize = () => {
                renderer.setSize(tz.offsetWidth, tz.offsetHeight);
                playerLeft = (tz.offsetWidth - player.offsetWidth) / 2;
        };

        const mState = () => {
                isPaused = !aud.paused;
                animate();
        };

        aud.onplaying = aud.onpause = () => mState();
        aud.onseeked = () => cancelAnimationFrame(raf);

        animate();
        FS(tz, player);
</script>

马黑黑 发表于 2025-5-14 20:28

本帖最后由 马黑黑 于 2025-5-14 21:04 编辑

帖子代码

<style>
        #tz { --state: running; margin: 30px 0; left: calc(50% - 81px); transform: translateX(-50%); width: clamp(600px, 90vw, 1400px); min-height: 80vh; aspect-ratio: 16/9; background: url('https://638183.freep.cn/638183/t24/5/qmyb.jpg') no-repeat center/cover; box-shadow: 2px 2px 8px #000; display: grid; place-items: center; z-index: 1; position: relative; }
        #btnFs { right: 20px; top: 20px; text-align: center; color: darkgreen; border-color: teal !important; }
        #btnFs:hover { color: red; }
        #player { position: absolute; z-index: 10; cursor: pointer; animation: rot 8s infinite linear var(--state); }
        #player:hover { filter: hue-rotate(60deg); }
        #vid {position: absolute; width: 100%; height: 100%; object-fit: cover; mask: radial-gradient(transparent 20%, red); -webkit-mask: radial-gradient(transparent 20%, red); pointer-events: none; }
        @keyframes rot { to { transform: rotate(360deg); } }
</style>

<div id="tz">
        <audio id="aud" src="https://music.163.com/song/media/outer/url?id=5275053" autoplay loop></audio>
        <video id="vid" src="https://bpic.588ku.com/video_listen/588ku_video/22/11/05/03/10/58/video636563c24169e.mp4" autoplay loop muted></video>
        <img id="player" src="https://638183.freep.cn/638183/small/fi1.webp" width="10%" title="播放/暂停" />
</div>

<script type="module">
        import * as THREE from 'https://esm.sh/three';
        import { FS } from 'https://638183.freep.cn/638183/web/ku/fscreen.js';

        let step = 0.01, pos = 0, isPaused = false, raf;
        let playerLeft = (tz.offsetWidth - player.offsetWidth) / 2;;

        const scene = new THREE.Scene;
        const camera = new THREE.PerspectiveCamera(75, tz.offsetWidth / tz.offsetHeight, 0.1, 1000);
        camera.position.set(0, 8, 20);
        const renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
        renderer.setSize(tz.offsetWidth, tz.offsetHeight);
        tz.appendChild(renderer.domElement);

        const geometry = new THREE.CylinderGeometry(1, 1, 10, 16);
        const texture = new THREE.TextureLoader().load('https://638183.freep.cn/638183/small/texture/wd3.jpg', () => renderer.render(scene, camera));

        const material = new THREE.MeshBasicMaterial({ color: 'tan', map: texture });       
        const cylinder = new THREE.Mesh(geometry, material);
        cylinder.rotateX(2);
        scene.add(cylinder);

        const animate = () => {
                pos -= step;
                let xx = playerLeft + pos * 15;
                player.style.setProperty('left', xx + 'px');
                if(pos >= 15 || pos <= -15) step = -step;
                cylinder.rotation.y += step;
                cylinder.position.x = pos;
                renderer.render(scene, camera);
                isPaused ?raf = requestAnimationFrame(animate) : cancelAnimationFrame(raf);
        };

        window.onresize = () => {
                renderer.setSize(tz.offsetWidth, tz.offsetHeight);
                playerLeft = (tz.offsetWidth - player.offsetWidth) / 2;
        };

        const mState = () => {
                isPaused = !aud.paused;
                animate();
        };

        aud.onplaying = aud.onpause = () => mState();
        aud.onseeked = () => cancelAnimationFrame(raf);

        animate();
        FS(tz, player);
</script>

马黑黑 发表于 2025-5-14 20:28

帖子特效简要说明:

本帖最后由 马黑黑 于 2025-5-14 20:45 编辑

帖子翻滚的原木特效,使用 three.js 制作,圆柱几何体 +(颜色+贴图)材质。

圆柱体相关介绍请参阅 three.js几何体之圆柱缓冲几何体(CylinderGeometry) - 马黑黑教程专版 - 花潮论坛 - Powered by Discuz! 或其它网络资料。

纹理贴图 texture 是 three.js 的一种重要内容,它需要加载器将图片加载,然后应用于材质(material),材质如果设置有颜色,颜色会和贴图共同产生作用,所以最好开启材质的透明度以便营造晶莹剔透或其它的渲染效果。本帖的原木纹理图片和 tan 颜色相得益彰,看上去不会走眼。

原木滚动需要借用一些变量,其中,pos 变量表示X轴位置,从 0 开始,往左 -15 个距离单位,往右 15 个距离单位,每一帧步进(step变量) 0.01 个距离单位。动画函数 animate 还涉及到播放器 player 的移动,通过驱使 #player 的 left 属性实现,借用的是 pos 变量,pos* 15 以加大像素值。

本帖最初制作的时候,是使用原木做播放控制器,感兴趣的朋友可以点击访问:牵引

马黑黑 发表于 2025-5-14 20:47

本帖最后由 马黑黑 于 2025-5-14 20:49 编辑

由于加载的资源相对较多,以及JS变量的声明和模块的引用使用的是新规范、和论坛机制的兼容不是特好,有时候打开帖子时原木并未滚动,若此,点击一下播放器暂停音乐,再点击一下播放音乐,原木就会滚动。

花飞飞 发表于 2025-5-14 21:08

这个版本加了漂亮的转盘小播,触动变色。。。这个图没见过,蛮漂亮的。。
圆柱形缓冲几何体教程有了,我先看看去。。。 {:4_173:}

马黑黑 发表于 2025-5-14 21:09

花飞飞 发表于 2025-5-14 21:08
这个版本加了漂亮的转盘小播,触动变色。。。这个图没见过,蛮漂亮的。。
圆柱形缓冲几何体教程有了,我先 ...

类似的图应该有过SVG版的

花飞飞 发表于 2025-5-14 21:26

马黑黑 发表于 2025-5-14 20:28
帖子翻滚的原木特效,使用 three.js 制作,圆柱几何体 +(颜色+贴图)材质。

圆柱体相关介绍请参阅 thre ...

这个颜色和贴图的搭配是绝了,无限接近于真实原木。。。
自身转动在教程里看到了,再实现左右滚动还是比较神奇的。。。
{:4_199:}
仔细看了下,理解得还是不透,还需要多看几遍

花飞飞 发表于 2025-5-14 21:32

之前说几何体当小播代码量太大,所以放弃了~~
今天看到你先发的牵引,又觉得哇,实现了喂,点击即停,用着也很方便。。{:4_170:}
超赞。。

花飞飞 发表于 2025-5-14 21:37

马黑黑 发表于 2025-5-14 21:09
类似的图应该有过SVG版的

画面阳光,绿意盎然,远处有露营的帐篷装备。。
视频有童话版摩天轮和气球,一看就联想到休闲娱乐,轻松自在。。

仔细看这个图案小播跟着木头跑的呢。。
不远不近的一直在身边,哈哈,的确有牵着的感觉。。{:4_173:}

花飞飞 发表于 2025-5-14 21:38

马黑黑 发表于 2025-5-14 20:47
由于加载的资源相对较多,以及JS变量的声明和模块的引用使用的是新规范、和论坛机制的兼容不是特好,有时候 ...

你还特意说明一下这种情况。。目前我这里还好。。。

杨帆 发表于 2025-5-14 21:50

代入感极强,仿佛进入儿童公园,谢谢老师精彩分享{:4_191:}

马黑黑 发表于 2025-5-14 21:51

杨帆 发表于 2025-5-14 21:50
代入感极强,仿佛进入儿童公园,谢谢老师精彩分享

{:4_190:}

马黑黑 发表于 2025-5-14 21:51

花飞飞 发表于 2025-5-14 21:38
你还特意说明一下这种情况。。目前我这里还好。。。

一切看浏览器性能、网速吧

马黑黑 发表于 2025-5-14 21:52

花飞飞 发表于 2025-5-14 21:37
画面阳光,绿意盎然,远处有露营的帐篷装备。。
视频有童话版摩天轮和气球,一看就联想到休闲娱乐,轻松 ...

牵手{:4_170:}

马黑黑 发表于 2025-5-14 21:53

花飞飞 发表于 2025-5-14 21:26
这个颜色和贴图的搭配是绝了,无限接近于真实原木。。。
自身转动在教程里看到了,再实现左右滚动还是比 ...

左右滚动需要自己编程控制,其实就是设置一个速度、上下阈值,然后就可以循环驱动了

红影 发表于 2025-5-14 22:08

小播牵引着原木的滚动,这个太神奇了。原来 three.js 的 pos 变量不但能驱动原木还能驱动小播呢{:4_187:}

红影 发表于 2025-5-14 22:10

刚进帖子时先入为主了,因为风车上的圆形木头就是做的圆柱效果呢,后来看到地上滚动的圆木,才知道弄错了{:4_173:}

小辣椒 发表于 2025-5-14 22:43

仔细看了一下代码,这个背景MV配的效果太好了,开始以为是小球球是代码编制的

小辣椒 发表于 2025-5-14 22:48

还是不是很明白,只能套用玩了

马黑黑 发表于 2025-5-14 23:06

小辣椒 发表于 2025-5-14 22:48
还是不是很明白,只能套用玩了

看不懂没关系,看懂需要改的地方就好
页: [1] 2 3 4
查看完整版本: 牵动