Mess
<style>#tz { margin: 30px 0; left: calc(50% - 81px); transform: translateX(-50%); width: clamp(600px, 90vw, 1400px); height: auto; aspect-ratio: 16/9; background: #eee url('https://638183.freep.cn/638183/t24/w4/mess.webp') 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; }
#player { position: absolute; left: -1000px; }
#tz canvas { position: absolute; }
</style>
<div id="tz">
<audio id="aud" src="https://music.163.com/song/media/outer/url?id=1328537196" autoplay loop></audio>
<div id="player" title="播放/暂停"></div>
</div>
<script type="module">
import * as THREE from 'https://638183.freep.cn/638183/3dev/build/three.module.min.js';
import { FS } from 'https://638183.freep.cn/638183/web/ku/FS.js';
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(45, tz.offsetWidth/tz.offsetHeight, 0.1, 1000);
camera.position.set(0, 0, 10);
const renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
renderer.setSize(tz.offsetWidth, tz.offsetHeight);
const clock = new THREE.Clock();
tz.appendChild(renderer.domElement);
const playerTexture = new THREE.TextureLoader().load('https://638183.freep.cn/638183/web/svg/sunfl-1.svg');
const spriteTexture = new THREE.TextureLoader().load('https://638183.freep.cn/638183/web/svg/sunfl-2.svg');
const mesh = new THREE.Mesh(
new THREE.SphereGeometry(1, 64, 64),
new THREE.MeshBasicMaterial({ map: playerTexture, transparent: true })
);
mesh.position.set(-2.15, 2.5, 0);
mesh.rotateZ(-Math.PI / 2);
scene.add(mesh);
const group = new THREE.Group();
for (let i = 0; i < 200; i++) {
const sprite = new THREE.Sprite(
new THREE.SpriteMaterial({ color: Math.random() * 0xffffff, map: spriteTexture })
);
sprite.scale.set(0.5, 0.5, 0.5);
sprite.position.set(
THREE.MathUtils.randFloatSpread(15),
THREE.MathUtils.randFloatSpread(15),
THREE.MathUtils.randFloatSpread(10)
);
group.add(sprite);
}
scene.add(group);
const animate = () => {
requestAnimationFrame(animate);
const delta = clock.getDelta();
group.rotation.x += delta / 10;
group.rotation.y += delta / 10;
group.rotation.z += delta / 10;
mesh.rotation.y += delta;
group.children.forEach(sprite => {
sprite.material.rotation -= delta;
});
renderer.render(scene, camera);
};
const isMess = (event) => {
const raycaster = new THREE.Raycaster();
const pointer = new THREE.Vector2();
let intersects = [];
pointer.x = (event.offsetX / tz.offsetWidth) * 2 - 1;
pointer.y = -(event.offsetY / tz.offsetHeight) * 2 + 1;
raycaster.setFromCamera(pointer, camera);
intersects = raycaster.intersectObjects(, true);
return intersects.length > 0;
}
tz.onmousemove = (e) => {
isMess(e)
? (tz.style.cursor = 'pointer', tz.title = '播放/暂停 Alt+X')
: (tz.style.cursor = 'default', tz.title = '');
};
tz.onclick = (e) => {
if (isMess(e)) player.click();
};
window.onresize = () => {
camera.aspect = tz.offsetWidth / tz.offsetHeight;
camera.updateProjectionMatrix();
renderer.setSize(tz.offsetWidth, tz.offsetHeight);
};
document.onvisibilitychange = () => {
if (aud.paused) return;
document.visibilityState === 'hidden' ? clock.stop() : clock.start();
};
aud.onplaying = aud.onpause = () => aud.paused ? clock.stop() : clock.start();
animate();
FS(tz, player);
</script> 帖子代码
<style>
#tz { margin: 30px 0; left: calc(50% - 81px); transform: translateX(-50%); width: clamp(600px, 90vw, 1400px); height: auto; aspect-ratio: 16/9; background: #eee url('https://638183.freep.cn/638183/t24/w4/mess.webp') 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; }
#player { position: absolute; left: -1000px; }
#tz canvas { position: absolute; }
</style>
<div id="tz">
<audio id="aud" src="https://music.163.com/song/media/outer/url?id=1328537196" autoplay loop></audio>
<div id="player" title="播放/暂停"></div>
</div>
<script type="module">
import * as THREE from 'https://638183.freep.cn/638183/3dev/build/three.module.min.js';
import { FS } from 'https://638183.freep.cn/638183/web/ku/FS.js';
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(45, tz.offsetWidth/tz.offsetHeight, 0.1, 1000);
camera.position.set(0, 0, 10);
const renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
renderer.setSize(tz.offsetWidth, tz.offsetHeight);
const clock = new THREE.Clock();
tz.appendChild(renderer.domElement);
const playerTexture = new THREE.TextureLoader().load('https://638183.freep.cn/638183/web/svg/sunfl-1.svg');
const spriteTexture = new THREE.TextureLoader().load('https://638183.freep.cn/638183/web/svg/sunfl-2.svg');
const mesh = new THREE.Mesh(
new THREE.SphereGeometry(1, 64, 64),
new THREE.MeshBasicMaterial({ map: playerTexture, transparent: true })
);
mesh.position.set(-2.15, 2.5, 0);
mesh.rotateZ(-Math.PI / 2);
scene.add(mesh);
const group = new THREE.Group();
for (let i = 0; i < 200; i++) {
const sprite = new THREE.Sprite(
new THREE.SpriteMaterial({ color: Math.random() * 0xffffff, map: spriteTexture })
);
sprite.scale.set(0.5, 0.5, 0.5);
sprite.position.set(
THREE.MathUtils.randFloatSpread(15),
THREE.MathUtils.randFloatSpread(15),
THREE.MathUtils.randFloatSpread(10)
);
group.add(sprite);
}
scene.add(group);
const animate = () => {
requestAnimationFrame(animate);
const delta = clock.getDelta();
group.rotation.x += delta / 10;
group.rotation.y += delta / 10;
group.rotation.z += delta / 10;
mesh.rotation.y += delta;
group.children.forEach(sprite => {
sprite.material.rotation -= delta;
});
renderer.render(scene, camera);
};
const isMess = (event) => {
const raycaster = new THREE.Raycaster();
const pointer = new THREE.Vector2();
let intersects = [];
pointer.x = (event.offsetX / tz.offsetWidth) * 2 - 1;
pointer.y = -(event.offsetY / tz.offsetHeight) * 2 + 1;
raycaster.setFromCamera(pointer, camera);
intersects = raycaster.intersectObjects(, true);
return intersects.length > 0;
}
tz.onmousemove = (e) => {
isMess(e)
? (tz.style.cursor = 'pointer', tz.title = '播放/暂停 Alt+X')
: (tz.style.cursor = 'default', tz.title = '');
};
tz.onclick = (e) => {
if (isMess(e)) player.click();
};
window.onresize = () => {
camera.aspect = tz.offsetWidth / tz.offsetHeight;
camera.updateProjectionMatrix();
renderer.setSize(tz.offsetWidth, tz.offsetHeight);
};
document.onvisibilitychange = () => {
if (aud.paused) return;
document.visibilityState === 'hidden' ? clock.stop() : clock.start();
};
aud.onplaying = aud.onpause = () => aud.paused ? clock.stop() : clock.start();
animate();
FS(tz, player);
</script>
本帖最后由 马黑黑 于 2025-6-11 22:37 编辑
本帖:
一、播放控制器
简单的球体几何+基础材质搭建的Mesh王哥图像:
const mesh = new THREE.Mesh(
new THREE.SphereGeometry(1, 64, 64),
new THREE.MeshBasicMaterial({ map: playerTexture, transparent: true })
);
mesh.position.set(-2.15, 2.5, 0);
mesh.rotateZ(-Math.PI / 2);
scene.add(mesh);
位置设置 position.set() 基于帖子背景图像的特点,效果正如大家所看到的。-2.15 表示从中心向左移动的距离单位、2.5 是从中心向上移动的距离单位,距离单位基于经验,当然也结合相机在 Z轴 上的定位(camera.position.set(0, 0, 10););Z轴上的翻转 rotateZ() 出于构图需求,也可以通过翻转 texture 贴图实现。
二、粒子系统
粒子采用精灵 Sprite 做成,它永远面朝相机,就是说我们总是看到它的正面、无论它怎么翻转——如果它可以翻转。精灵放在一个 ThreeJS 组(Group)里,使用 ThreeJS 封装的工具集合 MathUtils 的方法 randFloatSpread() 进行xyz三个方向的快速随机定位。相机在 Z轴 的 10 个距离单位上,以此为参照,XY轴 都取 15 个距离单位以便令精灵存在略微超越边界的可能,Z轴 上则取 10 个距离单位。精灵在动画中随 group 进行xyz三个方向的旋转,也可以考虑仅在一个或两个方向上旋转,不论如何,理论上都会随机出现个体较大的情形。
粒子的大致个体尺寸可以通过 spirte.scale.set(0.5, 0.5, 0.5); 进行设置,但运动中的精灵粒子还是会在特定角度下出现较大的情况。另外,由于场景xyz轴的尺寸并不一致,运动过程中产生的效果非常迷人。
这球体贴图有意思,用这样的svg图图竟然能贴出半个球体的感觉,这个没想到{:4_187:} 精彩之作,赞一个{:4_178:} 这个粒子飞舞的方式不仅是上下,而是四面八方~有的上有的下,且前后空间感立体感超强~特别好看~ 漂亮,谢谢马老师经典分享{:4_191:} 这个漂亮的太阳花红色和绿色,
半球体效果像是燃烧的火焰一样,又特别又好看~
绿色的也被随机渲染成各种色彩,这个怎么做到的~
异域音乐神秘 红影 发表于 2025-6-11 21:41
这球体贴图有意思,用这样的svg图图竟然能贴出半个球体的感觉,这个没想到
图片有透明部分。开启 transparent: true 属性之后,透明部分就会透明,不开启的话透明部分是黑色 花飞飞 发表于 2025-6-11 22:30
这个漂亮的太阳花红色和绿色,
半球体效果像是燃烧的火焰一样,又特别又好看~
绿色的也被随机渲染成各种色 ...
当材质设置有颜色和贴图,贴图的颜色和所设置的颜色会互相叠加。随机颜色中可以进行有限的控制,比方只需要红色参与对贴图的影响:
new THREE.SpriteMaterial({ color: Math.random() * 0xff0000, map: spriteTexture })
这样,材质中贴图随机以红色系叠加到自身 杨帆 发表于 2025-6-11 22:29
漂亮,谢谢马老师经典分享
{:4_190:} 花飞飞 发表于 2025-6-11 22:24
这个粒子飞舞的方式不仅是上下,而是四面八方~有的上有的下,且前后空间感立体感超强~特别好看~
精灵全部加入都组 group 中。就像微信群一样,加入了,群里的活动大家共同参与。group 在 xyz 三个方向都有旋转,精灵跟群旋转。 朵拉 发表于 2025-6-11 22:11
精彩之作,赞一个
{:4_191:} 美!简直美得要让你滴们学生把你爱S{:4_172:} 樵歌 发表于 2025-6-12 07:05
美!简直美得要让你滴们学生把你爱S
{:4_196:}{:4_203:} 马黑黑 发表于 2025-6-11 22:39
图片有透明部分。开启 transparent: true 属性之后,透明部分就会透明,不开启的话透明部分是黑色
这个效果非常奇妙,不是帖上得到效果,空想很难想得到是这样{:4_173:} 马黑黑 发表于 2025-6-12 08:20
瞧把把都乐成猪头了{:4_172:} 樵歌 发表于 2025-6-12 16:39
瞧把把都乐成猪头了
相当于捡了块大号的玉石 红影 发表于 2025-6-12 14:58
这个效果非常奇妙,不是帖上得到效果,空想很难想得到是这样
我花了不少闲暇时间去读文档 马黑黑 发表于 2025-6-11 22:42
当材质设置有颜色和贴图,贴图的颜色和所设置的颜色会互相叠加。随机颜色中可以进行有限的控制,比方只需 ...
原来如此,今天那个花型的却有不同层次的色彩。。内圈和外圈不同。。
看了源码是本身花型就是这样的,你找的图还真是经典
页:
[1]
2