牵动
本帖最后由 马黑黑 于 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 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: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:49 编辑
由于加载的资源相对较多,以及JS变量的声明和模块的引用使用的是新规范、和论坛机制的兼容不是特好,有时候打开帖子时原木并未滚动,若此,点击一下播放器暂停音乐,再点击一下播放音乐,原木就会滚动。 这个版本加了漂亮的转盘小播,触动变色。。。这个图没见过,蛮漂亮的。。
圆柱形缓冲几何体教程有了,我先看看去。。。 {:4_173:} 花飞飞 发表于 2025-5-14 21:08
这个版本加了漂亮的转盘小播,触动变色。。。这个图没见过,蛮漂亮的。。
圆柱形缓冲几何体教程有了,我先 ...
类似的图应该有过SVG版的 马黑黑 发表于 2025-5-14 20:28
帖子翻滚的原木特效,使用 three.js 制作,圆柱几何体 +(颜色+贴图)材质。
圆柱体相关介绍请参阅 thre ...
这个颜色和贴图的搭配是绝了,无限接近于真实原木。。。
自身转动在教程里看到了,再实现左右滚动还是比较神奇的。。。
{:4_199:}
仔细看了下,理解得还是不透,还需要多看几遍 之前说几何体当小播代码量太大,所以放弃了~~
今天看到你先发的牵引,又觉得哇,实现了喂,点击即停,用着也很方便。。{:4_170:}
超赞。。 马黑黑 发表于 2025-5-14 21:09
类似的图应该有过SVG版的
画面阳光,绿意盎然,远处有露营的帐篷装备。。
视频有童话版摩天轮和气球,一看就联想到休闲娱乐,轻松自在。。
仔细看这个图案小播跟着木头跑的呢。。
不远不近的一直在身边,哈哈,的确有牵着的感觉。。{:4_173:} 马黑黑 发表于 2025-5-14 20:47
由于加载的资源相对较多,以及JS变量的声明和模块的引用使用的是新规范、和论坛机制的兼容不是特好,有时候 ...
你还特意说明一下这种情况。。目前我这里还好。。。 代入感极强,仿佛进入儿童公园,谢谢老师精彩分享{:4_191:} 杨帆 发表于 2025-5-14 21:50
代入感极强,仿佛进入儿童公园,谢谢老师精彩分享
{:4_190:} 花飞飞 发表于 2025-5-14 21:38
你还特意说明一下这种情况。。目前我这里还好。。。
一切看浏览器性能、网速吧 花飞飞 发表于 2025-5-14 21:37
画面阳光,绿意盎然,远处有露营的帐篷装备。。
视频有童话版摩天轮和气球,一看就联想到休闲娱乐,轻松 ...
牵手{:4_170:} 花飞飞 发表于 2025-5-14 21:26
这个颜色和贴图的搭配是绝了,无限接近于真实原木。。。
自身转动在教程里看到了,再实现左右滚动还是比 ...
左右滚动需要自己编程控制,其实就是设置一个速度、上下阈值,然后就可以循环驱动了 小播牵引着原木的滚动,这个太神奇了。原来 three.js 的 pos 变量不但能驱动原木还能驱动小播呢{:4_187:} 刚进帖子时先入为主了,因为风车上的圆形木头就是做的圆柱效果呢,后来看到地上滚动的圆木,才知道弄错了{:4_173:} 仔细看了一下代码,这个背景MV配的效果太好了,开始以为是小球球是代码编制的 还是不是很明白,只能套用玩了 小辣椒 发表于 2025-5-14 22:48
还是不是很明白,只能套用玩了
看不懂没关系,看懂需要改的地方就好