战争神曲 Novera(诺维拉)
<style>#papa { margin: 30px 0; left: calc(50% - 81px); transform: translateX(-50%); width: clamp(600px, 90vw, 1400px); height: auto; aspect-ratio: 16/9; background: #333 url('https://638183.freep.cn/638183/t24/6/war.jpg') no-repeat center/cover; box-shadow: 2px 2px 8px #000; display: grid; place-items: center; perspective: 2000px; z-index: 1; position: relative; --state: running; --hh: 190px; }
.son { position: absolute; width: 30px; height: 30px; border-radius: 50%; background: url('https://638183.freep.cn/638183/small/2025/bl01.png') no-repeat center/cover; opacity: 0.5; cursor: pointer; transform: rotateZ(var(--a)) translate3d(var(--x), var(--y), var(--z)); --x: 0; --y: 0; --z: 0; --a: 0; }
#btnFs { bottom: 20px; color: silver; border-color: silver !important; }
#vid {position: absolute; width: 100%; height: 100%; object-fit: cover; mask: radial-gradient(transparent 20%, red); -webkit-mask: radial-gradient(transparent 20%, red); opacity: .6; pointer-events: none; }
#player { position: absolute; width: 200px; height: 200px; background: url('https://638183.freep.cn/638183/small/hxdo.png') no-repeat center/cover; transform: rotateX(45deg) rotateZ(0); cursor: pointer; animation: rot3d 6s linear infinite var(--state); filter: drop-shadow(0 0 4px gray) opacity(0.8); display: grid; place-items: center; }
#player::after { position: absolute; content: ''; width: 30%; height: 30%; background: url('https://638183.freep.cn/638183/small/2025/bl01.png') no-repeat center/cover; }
@keyframes rot3d { to { transform: rotateX(45deg) rotateZ(360deg); } }
</style>
<div id="papa">
<audio id="aud" src="https://music.163.com/song/media/outer/url?id=2599491071" autoplay loop></audio>
<video id="vid" src="https://bpic.588ku.com/video_listen/588ku_video/24/12/30/16/16/25/video677256d96857e.mp4" autoplay loop muted></video>
<div id="player"></div>
</div>
<script type="module">
import TWEEN from 'https://638183.freep.cn/638183/3dev/examples/jsm/libs/tween.module.js';
import { FS } from 'https://638183.freep.cn/638183/web/ku/FS.js';
const total = 60, sons = [], tweens = [];
Array.from({length: total}).forEach( (son, idx) => {
son = document.createElement('div');
son.className = 'son';
son.title = 'Alt+X';
son.onclick = () => player.click();
sons.push(son);
papa.appendChild(son);
const begin = {
x: papa.clientWidth / 4 - Math.random() * papa.clientWidth / 2,
y: 0,
z: -5000,
a: 0
};
const end = {
x: 0,
y: [-papa.clientHeight, papa.clientHeight / 3],
z: [-3000, 560],
a:
};
const tween = new TWEEN.Tween(begin)
.to(end, Math.random() * 5000 + 8000)
.onUpdate( () => {
son.style.setProperty('--x', begin.x + 'px');
son.style.setProperty('--y', begin.y + 'px');
son.style.setProperty('--z', begin.z + 'px');
son.style.setProperty('--a', begin.a + 'deg');
})
.delay(Math.random() * 5000)
.repeat(Infinity)
.repeatDelay(Math.random() * 5000)
.easing(TWEEN.Easing.Exponential.Out)
.yoyo(Math.random() * 30000 + 2000)
.start();
tweens.push(tween);
});
const animate = () => {
TWEEN.update();
requestAnimationFrame(animate);
};
aud.onplaying = aud.onpause = () => {
tweens.forEach(tween => aud.paused ? tween.pause() : tween.resume());
};
animate();
FS(papa, player);
</script> 帖子代码:
<style>
#papa { margin: 30px 0; left: calc(50% - 81px); transform: translateX(-50%); width: clamp(600px, 90vw, 1400px); height: auto; aspect-ratio: 16/9; background: #333 url('https://638183.freep.cn/638183/t24/6/war.jpg') no-repeat center/cover; box-shadow: 2px 2px 8px #000; display: grid; place-items: center; perspective: 2000px; z-index: 1; position: relative; --state: running; --hh: 190px; }
.son { position: absolute; width: 30px; height: 30px; border-radius: 50%; background: url('https://638183.freep.cn/638183/small/2025/bl01.png') no-repeat center/cover; opacity: 0.5; cursor: pointer; transform: rotateZ(var(--a)) translate3d(var(--x), var(--y), var(--z)); --x: 0; --y: 0; --z: 0; --a: 0; }
#btnFs { bottom: 20px; color: silver; border-color: silver !important; }
#vid {position: absolute; width: 100%; height: 100%; object-fit: cover; mask: radial-gradient(transparent 20%, red); -webkit-mask: radial-gradient(transparent 20%, red); opacity: .6; pointer-events: none; }
#player { position: absolute; width: 200px; height: 200px; background: url('https://638183.freep.cn/638183/small/hxdo.png') no-repeat center/cover; transform: rotateX(45deg) rotateZ(0); cursor: pointer; animation: rot3d 6s linear infinite var(--state); filter: drop-shadow(0 0 4px gray) opacity(0.8); display: grid; place-items: center; }
#player::after { position: absolute; content: ''; width: 30%; height: 30%; background: url('https://638183.freep.cn/638183/small/2025/bl01.png') no-repeat center/cover; }
@keyframes rot3d { to { transform: rotateX(45deg) rotateZ(360deg); } }
</style>
<div id="papa">
<audio id="aud" src="https://music.163.com/song/media/outer/url?id=2599491071" autoplay loop></audio>
<video id="vid" src="https://bpic.588ku.com/video_listen/588ku_video/24/12/30/16/16/25/video677256d96857e.mp4" autoplay loop muted></video>
<div id="player"></div>
</div>
<script type="module">
import TWEEN from 'https://638183.freep.cn/638183/3dev/examples/jsm/libs/tween.module.js';
import { FS } from 'https://638183.freep.cn/638183/web/ku/FS.js';
const total = 60, sons = [], tweens = [];
Array.from({length: total}).forEach( (son, idx) => {
son = document.createElement('div');
son.className = 'son';
son.title = 'Alt+X';
son.onclick = () => player.click();
sons.push(son);
papa.appendChild(son);
const begin = {
x: papa.clientWidth / 4 - Math.random() * papa.clientWidth / 2,
y: 0,
z: -5000,
a: 0
};
const end = {
x: 0,
y: [-papa.clientHeight, papa.clientHeight / 3],
z: [-3000, 560],
a:
};
const tween = new TWEEN.Tween(begin)
.to(end, Math.random() * 5000 + 8000)
.onUpdate( () => {
son.style.setProperty('--x', begin.x + 'px');
son.style.setProperty('--y', begin.y + 'px');
son.style.setProperty('--z', begin.z + 'px');
son.style.setProperty('--a', begin.a + 'deg');
})
.delay(Math.random() * 5000)
.repeat(Infinity)
.repeatDelay(Math.random() * 5000)
.easing(TWEEN.Easing.Exponential.Out)
.yoyo(Math.random() * 30000 + 2000)
.start();
tweens.push(tween);
});
const animate = () => {
TWEEN.update();
requestAnimationFrame(animate);
};
aud.onplaying = aud.onpause = () => {
tweens.forEach(tween => aud.paused ? tween.pause() : tween.resume());
};
animate();
FS(papa, player);
</script>
动画设计请参阅:
CSS+TWEEN创意3d粒子 - 马黑黑教程专版 - 花潮论坛 - Powered by Discuz!
本帖最后由 马黑黑 于 2025-7-8 18:34 编辑
帖子未设置 transform-style 属性,缺省默认使用 flat 转换形态,这意味着小球之间以及小球和相同场景下的其它3d元素的遮挡关系依赖于元素在代码流中的先后次序+z-index的设置。transform-style: flat; 的默认属性设置依然可以渲染3d效果,例如帖子中小球近大远小的效果存在。
当设置 transform-style: preserve-3d; 属性的容器同时又设置了防溢出的 overflow: hidden; 时,preserve-3d 将自动回退到 flat 渲染形态。
控制小球活动范围的因素是综合的,其中,在小球相关其它设置不改变的情况下,仅修改帖子容器元素的 perspective 属性值,就可以改变小球的活动区域。 马黑黑 发表于 2025-7-8 18:24
动画设计请参阅:
这个里面的小球运作还是和讲义里的不完全一样的呢{:4_173:} “当设置 transform-style: preserve-3d; 属性的容器同时又设置了防溢出的 overflow: hidden; 时,preserve-3d 将自动回退到 flat 渲染形态。”
防溢出居然还能影响到3d,这个完全没想到。 音乐很激昂,有磅礴大气的史诗感,配合着这样的背景,特别有感染力{:4_199:} 奇特的效果,谢谢马老师经典讲授与示范{:4_190:} 杨帆 发表于 2025-7-8 19:16
奇特的效果,谢谢马老师经典讲授与示范
{:4_191:} 红影 发表于 2025-7-8 19:01
这个里面的小球运作还是和讲义里的不完全一样的呢
原理不变 红影 发表于 2025-7-8 19:08
音乐很激昂,有磅礴大气的史诗感,配合着这样的背景,特别有感染力
嗯,打起来啦的意思 红影 发表于 2025-7-8 19:02
“当设置 transform-style: preserve-3d; 属性的容器同时又设置了防溢出的 overflow: hidden; 时,preserve ...
我带过的一位学生,曾使用 css+HTML 做了一个三维圆球,运行完好。有一天,出于某种需要,给圆球的容器元素添加了 overflow: hidden;,结果坍塌成了差不多是平面的东东{:4_170:} 马黑黑 发表于 2025-7-8 19:34
原理不变
虽然原理一样,表现效果不同。这个里面播放器也被贴了同样的小图,和例子相互呼应,非常漂亮{:4_199:} 马黑黑 发表于 2025-7-8 19:34
嗯,打起来啦的意思
打起来应该比这更血腥,黑黑很善良,用这样的形态就表示了。 马黑黑 发表于 2025-7-8 19:37
我带过的一位学生,曾使用 css+HTML 做了一个三维圆球,运行完好。有一天,出于某种需要,给圆球的容器元 ...
防溢出肯定有需要的啊,比如想让视频的logo不出现,结果设置这个就会让三维坍塌啊,这个想不到呢。 红影 发表于 2025-7-8 21:22
防溢出肯定有需要的啊,比如想让视频的logo不出现,结果设置这个就会让三维坍塌啊,这个想不到呢。
所以需要设计好逻辑关系 红影 发表于 2025-7-8 21:20
打起来应该比这更血腥,黑黑很善良,用这样的形态就表示了。
{:4_172:} 红影 发表于 2025-7-8 21:19
虽然原理一样,表现效果不同。这个里面播放器也被贴了同样的小图,和例子相互呼应,非常漂亮
这个简单的。开始的时候,我是让 .son 的其中一个去填充那个位置,后来想想还不如用个伪元素,旋转呀什么的都免了,更重要的是,旋转不用专门设计 马黑黑 发表于 2025-7-8 21:37
所以需要设计好逻辑关系
是不是它把 z 轴的延伸也当做是溢出了? 马黑黑 发表于 2025-7-8 21:38
这样的制作只看到了美感,没看到血腥。