苏海珍·后序
<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/sea2025.jpg') no-repeat center/cover; box-shadow: 2px 2px 8px #000; display: grid; place-items: center; z-index: 1; position: relative; }
#btnFs { bottom: 20px; color: #eee; text-align: center; }
#btnFs:hover { color: red; }
#vid {position: absolute; width: 100%; height: 100%; opacity: .5; object-fit: cover; mask: radial-gradient(transparent 20%, red); -webkit-mask: radial-gradient(transparent 20%, red); pointer-events: none; }
#player { position: absolute; left: 30px; top: 30px; z-index: 10; mix-blend-mode: multiply; transition: filter .7s; cursor: pointer; animation: rot 8s infinite linear var(--state); }
#player:hover { filter: hue-rotate(60deg); }
@keyframes rot { to { transform: rotate(360deg); } }
</style>
<div id="tz">
<audio id="aud" src="https://music.163.com/song/media/outer/url?id=291348" autoplay loop></audio>
<video id="vid" src="https://bpic.588ku.com/video_listen/588ku_video/23/09/13/14/30/14/video650156f622ba9.mp4" autoplay loop muted></video>
<img id="player" src="https://638183.freep.cn/638183/small/hdxk.png" width="10%" title="播放/暂停" />
</div>
<script type="module">
import * as THREE from 'https://esm.sh/three';
import { OrbitControls } from "https://esm.sh/three/examples/jsm/controls/OrbitControls";
import { FS } from 'https://638183.freep.cn/638183/web/ku/fscreen.js';
let isPlaying = true, raf;
const scene = new THREE.Scene;
const camera = new THREE.PerspectiveCamera(60, tz.offsetWidth / tz.offsetHeight, 0.1, 1000);
camera.position.set(0, -4, 10);
const renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
renderer.setSize(tz.offsetWidth, tz.offsetHeight);
tz.appendChild(renderer.domElement);
const controller = new OrbitControls(camera, renderer.domElement);
controller.autoRotate = true;
const geometry = new THREE.ConeGeometry(1, 2, 5);
const texture = new THREE.TextureLoader().load("https://638183.freep.cn/638183/Pic/spidernet.jpg");
const material = new THREE.MeshBasicMaterial({
map: texture,
side: THREE.DoubleSide,
transparent: true,
opacity: 0.6,
});
const mainCone = new THREE.Mesh(geometry, material);
var cones = [];
const poses = [ , [-5, 0, 0], , ];
poses.forEach(pos => {
const cone = mainCone.clone();
cone.position.set(...pos);
cones.push(cone);
});
cones.forEach(cone => mainCone.add(cone));
scene.add(mainCone);
const render = () => {
isPlaying ? raf = requestAnimationFrame(render) : cancelAnimationFrame(raf);
cones.forEach(cone => cone.rotation.y += 0.01);
controller.update();
renderer.render(scene, camera);
};
window.onresize = () => {
renderer.setSize(tz.offsetWidth, tz.offsetHeight);
renderer.render(scene, camera);
};
tz.onclick = (e) => {
if (e.target.id !== 'player') return;
isPlaying = !aud.paused;
render();
};
render();
FS(tz, player);
</script>
帖子代码:
<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/sea2025.jpg') no-repeat center/cover; box-shadow: 2px 2px 8px #000; display: grid; place-items: center; z-index: 1; position: relative; }
#btnFs { bottom: 20px; color: #eee; text-align: center; }
#btnFs:hover { color: red; }
#vid {position: absolute; width: 100%; height: 100%; opacity: .5; object-fit: cover; mask: radial-gradient(transparent 20%, red); -webkit-mask: radial-gradient(transparent 20%, red); pointer-events: none; }
#player { position: absolute; left: 30px; top: 30px; z-index: 10; mix-blend-mode: multiply; transition: filter .7s; cursor: pointer; animation: rot 8s infinite linear var(--state); }
#player:hover { filter: hue-rotate(60deg); }
@keyframes rot { to { transform: rotate(360deg); } }
</style>
<div id="tz">
<audio id="aud" src="https://music.163.com/song/media/outer/url?id=291348" autoplay loop></audio>
<video id="vid" src="https://bpic.588ku.com/video_listen/588ku_video/23/09/13/14/30/14/video650156f622ba9.mp4" autoplay loop muted></video>
<img id="player" src="https://638183.freep.cn/638183/small/hdxk.png" width="10%" title="播放/暂停" />
</div>
<script type="module">
import * as THREE from 'https://esm.sh/three';
import { OrbitControls } from "https://esm.sh/three/examples/jsm/controls/OrbitControls";
import { FS } from 'https://638183.freep.cn/638183/web/ku/fscreen.js';
let isPlaying = true, raf;
const scene = new THREE.Scene;
const camera = new THREE.PerspectiveCamera(60, tz.offsetWidth / tz.offsetHeight, 0.1, 1000);
camera.position.set(0, -4, 10);
const renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
renderer.setSize(tz.offsetWidth, tz.offsetHeight);
tz.appendChild(renderer.domElement);
const controller = new OrbitControls(camera, renderer.domElement);
controller.autoRotate = true;
const geometry = new THREE.ConeGeometry(1, 2, 5);
const texture = new THREE.TextureLoader().load("https://638183.freep.cn/638183/Pic/spidernet.jpg");
const material = new THREE.MeshBasicMaterial({
map: texture,
side: THREE.DoubleSide,
transparent: true,
opacity: 0.6,
});
const mainCone = new THREE.Mesh(geometry, material);
var cones = [];
const poses = [ , [-5, 0, 0], , ];
poses.forEach(pos => {
const cone = mainCone.clone();
cone.position.set(...pos);
cones.push(cone);
});
cones.forEach(cone => mainCone.add(cone));
scene.add(mainCone);
const render = () => {
isPlaying ? raf = requestAnimationFrame(render) : cancelAnimationFrame(raf);
cones.forEach(cone => cone.rotation.y += 0.01);
controller.update();
renderer.render(scene, camera);
};
window.onresize = () => {
renderer.setSize(tz.offsetWidth, tz.offsetHeight);
renderer.render(scene, camera);
};
tz.onclick = (e) => {
if (e.target.id !== 'player') return;
isPlaying = !aud.paused;
render();
};
render();
FS(tz, player);
</script>
本帖最后由 马黑黑 于 2025-5-13 12:54 编辑
three.js 特效部分简要说明:
一、几何体使用 ConeGeometry 即锥形几何体,其构造器可以简单、可以复杂,主要是前三个参数:
1. radius — 圆锥底部的半径,默认值为1
2. height — 圆锥的高度,默认值为1
3. radialSegments — 圆锥侧面周围的分段数,默认为32
更多追星集合体的介绍请参阅 :three.js几何体之圆锥缓冲几何体 - 马黑黑教程专版 - 花潮论坛 - Powered by Discuz!
二、锥体图形克隆
本帖创建了一个母体锥体图形 mainCone,其余四个锥体图形从它那里克隆并加入到它那里作为它的子锥体而存在,子锥体按预设与母体锥体拉开距离,两个在X轴、另外两个在Z轴上拉开,俩俩对称。克隆锥体最初我叫DS完成,DS帮我弄成了8个子锥体,我跟它说结果后它还支持说理论上是不会有问题的,但既然客户说有问题,我再去分析一下,嗯嗯,原理酱紫……然后它给出的解决方案是再创建一个模版锥体,通过模板进行克隆。方法没问题,但是不够优雅,于是自己重写克隆方法,预设一个数组装载子锥体,克隆后再一同添加到母锥体中。
三、相机位置设置
为了能够看到锥体的底部,也是为了令动画的渲染更具立体感,相机除了在 Z 方向拉开距离,在 Y 方向也做了挪动,代码在第 26 行。
还有其他实现方式,比如改变 geometry 或 mainCone 的 rotate 等等。
四、three.js 图形完美融入帖子背景
核心在第 27 行,创建场景的参数,一个是 antialias: true, ,它负责反锯齿,让图形边缘更为平滑,另一个是 alpha: true,开启 alpha 通道令场景背景透明。
这三角圆锥体真漂亮!{:4_199:} 黑黑老师辛苦了!这个有难度,套用也难了。 这圆锥体的贴膜有意思,还是蜘蛛网呢,这个效果和背景十分相符。
黑黑还细心地把小海星的播放按钮也弄成了相似色,让效果浑然一体{:4_199:} 这个帖子多了子椎体的克隆内容和相机的调整方式,又是新知识呢{:4_199:} 四个子椎体的位置也是可以设置的,这个不是球体的排布方式,是直接设置位置的呢。
three.js 里的东西还真不少{:4_187:} 红影 发表于 2025-5-13 14:59
四个子椎体的位置也是可以设置的,这个不是球体的排布方式,是直接设置位置的呢。
three.js 里的东西还真 ...
该考虑到的绝大多数都考虑到了。有一个遗憾 是,它没有封装图形对象的点击操作,需要使用者额外编程。 红影 发表于 2025-5-13 14:55
这个帖子多了子椎体的克隆内容和相机的调整方式,又是新知识呢
图形等对象有克隆、复制等操作,这个很方便 梦江南 发表于 2025-5-13 14:03
黑黑老师辛苦了!这个有难度,套用也难了。
纹理贴图要求图片同源或支持跨域 梦江南 发表于 2025-5-13 14:02
这三角圆锥体真漂亮!
可以一看吧 欣赏老师高科技的制作 流水光阴 发表于 2025-5-13 18:26
欣赏老师高科技的制作
下午好 真的跟水晶一样,半透明的设定,让它跟钻石一样看上去绝美。。{:4_173:} 不仅自转,还绕着中间块公转,这立体的效果一次比一次惊艳 马黑黑 发表于 2025-5-13 12:31
three.js 特效部分简要说明:
一、几何体使用 ConeGeometry 即锥形几何体,其构造器可以简单、可以复杂 ...
这个跟教程比更复杂了,多了克隆复制这个新的内容。
你跟DS的对话也好玩,满网找不到满意的,还得自己动手,才能达到预期。。{:4_173:}
AI靠不住耶。。 花飞飞 发表于 2025-5-13 19:05
这个跟教程比更复杂了,多了克隆复制这个新的内容。
你跟DS的对话也好玩,满网找不到满意的,还得自己动 ...
DS对第二问的分析其实还是到位的,它终于找到问题出在哪(为什么出现辣么多的子锥体) 花飞飞 发表于 2025-5-13 18:36
不仅自转,还绕着中间块公转,这立体的效果一次比一次惊艳
这些都是利用 three.js 封装的功能 花飞飞 发表于 2025-5-13 18:34
真的跟水晶一样,半透明的设定,让它跟钻石一样看上去绝美。。
绝美好,我差一点看成绝症了{:4_170:}