外面的宇宙
<style>#papa { margin: 30px 0; left: calc(50% - 81px); transform: translateX(-50%); width: clamp(600px, 90vw, 1400px); height: auto; aspect-ratio: 16/9; background: url('https://638183.freep.cn/638183/t24/6/outspace.jpg') no-repeat center/cover; box-shadow: 2px 2px 8px #000; z-index: 1; overflow: hidden; display: grid; place-items: center; position: relative; }
#player { position: absolute; bottom: 120px; aspect-ratio: 1/1; width: 15%; display: grid; place-items: center; animation: rot 12s linear infinite var(--state); transition: 0.6s; cursor: pointer; }
li-zi { position: absolute; aspect-ratio: 1/1; width: 15%; border-radius: 50%; background: radial-gradient(circle at 10%, var(--c1), var(--c2)); box-shadow: inset 4px 4px 16px gray; transform: rotate(var(--a)) translate(var(--r)); transition: 0.7s; }
#btnFs { bottom: 20px; color: #eee; }
.vid {position: absolute; width: 100%; height: 100%; object-fit: cover; mask: radial-gradient(transparent 20%, red); -webkit-mask: radial-gradient(transparent 20%, red); opacity: .8; pointer-events: none; }
@keyframes rot { to { transform: rotate(1turn); } }
</style>
<div id="papa">
<audio id="aud" src="https://music.163.com/song/media/outer/url?id=2164895832" autoplay loop></audio>
<video class="vid" src="https://bpic.588ku.com/video_listen/588ku_video/22/11/04/04/03/07/video63641e7be0306.mp4" autoplay loop muted></video>
<div id="player"></div>
</div>
<script type="module">
import { FS } from 'https://638183.freep.cn/638183/web/js/fullscreen.js';
FS(papa, player);
var posdata = , lzAr = [];
posdata.forEach( (pos, posIdx) => {
Array.from({ length: pos }).forEach( (lz, lzIdx) => {
lz = document.createElement('li-zi');
const a = lzIdx * 360 / pos, r = posIdx * player.clientWidth / (posdata.length + 1);
lz.style.cssText += `
--c1: #${Math.random().toString(16).substring(2, 8)};
--c2: #${Math.random().toString(16).substring(2, 8)};
--r: ${r}px;
--a: ${a}deg;
`;
lzAr.push({elm: lz, r: r, idx: posIdx});
player.appendChild(lz);
});
});
aud.ontimeupdate = () => flash();
window.onresize = () => {
lzAr.forEach(lz => lz.elm.style.setProperty('--r', `${lz.idx * player.clientWidth / (posdata.length + 1)}px`));
};
function flash() {
player.style.setProperty('filter', `hue-rotate(${360 * Math.random()}deg)`);
}
</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: url('https://638183.freep.cn/638183/t24/6/outspace.jpg') no-repeat center/cover; box-shadow: 2px 2px 8px #000; z-index: 1; overflow: hidden; display: grid; place-items: center; position: relative; }
#player { position: absolute; bottom: 120px; aspect-ratio: 1/1; width: 15%; display: grid; place-items: center; animation: rot 12s linear infinite var(--state); transition: 0.6s; cursor: pointer; }
li-zi { position: absolute; aspect-ratio: 1/1; width: 15%; border-radius: 50%; background: radial-gradient(circle at 10%, var(--c1), var(--c2)); box-shadow: inset 4px 4px 16px gray; transform: rotate(var(--a)) translate(var(--r)); transition: 0.7s; }
#btnFs { bottom: 20px; color: #eee; }
.vid {position: absolute; width: 100%; height: 100%; object-fit: cover; mask: radial-gradient(transparent 20%, red); -webkit-mask: radial-gradient(transparent 20%, red); opacity: .8; pointer-events: none; }
@keyframes rot { to { transform: rotate(1turn); } }
</style>
<div id="papa">
<audio id="aud" src="https://music.163.com/song/media/outer/url?id=2164895832" autoplay loop></audio>
<video class="vid" src="https://bpic.588ku.com/video_listen/588ku_video/22/11/04/04/03/07/video63641e7be0306.mp4" autoplay loop muted></video>
<div id="player"></div>
</div>
<script type="module">
import { FS } from 'https://638183.freep.cn/638183/web/js/fullscreen.js';
FS(papa, player);
var posdata = , lzAr = [];
posdata.forEach( (pos, posIdx) => {
Array.from({ length: pos }).forEach( (lz, lzIdx) => {
lz = document.createElement('li-zi');
const a = lzIdx * 360 / pos, r = posIdx * player.clientWidth / (posdata.length + 1);
lz.style.cssText += `
--c1: #${Math.random().toString(16).substring(2, 8)};
--c2: #${Math.random().toString(16).substring(2, 8)};
--r: ${r}px;
--a: ${a}deg;
`;
lzAr.push({elm: lz, r: r, idx: posIdx});
player.appendChild(lz);
});
});
aud.ontimeupdate = () => flash();
window.onresize = () => {
lzAr.forEach(lz => lz.elm.style.setProperty('--r', `${lz.idx * player.clientWidth / (posdata.length + 1)}px`));
};
function flash() {
player.style.setProperty('filter', `hue-rotate(${360 * Math.random()}deg)`);
}
</script>
播放器适用容器元素尺寸机制:
播放器 #player 选择器宽度设置为 15%,这将令其尺寸是动态变化的——依据访问者的显示设备宽度而定,因为帖子容器的尺寸采用了自适应显示设备尺寸。
然后,借助CSS的 aspect-ratio 属性,强制小播宽高比一致(都是一比一)。
酱紫,其内的子元素 li-zi ,15%的宽度 + 一比一的 aspect-ratio 配套设置,小球也是自适应尺寸的。
一系列的自适应宽高,在不同分辨率下,呈现的小播及小播内的粒子(小球)大小不尽一致,但与帖子的宽高应该总是般配的,而同一显示设备下,浏览器窗口的尺寸变化小播会有过渡性的实时响应。
浏览器窗口的变更,需要重新计算各层粒子的 --r 变量,它规范粒子层之间的间隔(gap,其实就是最大范围半径),这个变量不好用相对单位表达,其计算依据是粒子容器即 player 的动态宽度和粒子所在的圈层(第39~41行代码)。
粒子分层呈现,最里层一个,然后第二、三、四层分别为5、10、15个,用数组 posdata 记录,代码在20行,可以给不同的层定义不同的粒子数,以及建立更多的层,但数量应恰当。 小球的闪烁通过 flash 函数实现,借用的是 CSS 的滤镜属性,通过audio标签的 ontimeupdate 事件驱动。 <p>音频:秋刀电音·外面的宇宙</p>
<script>
aud.src = 'https://music.163.com/song/media/outer/url?id=2726392829';
</script> 欣赏老师的精彩{:4_180:} 马黑黑 发表于 2025-7-18 14:50
小球的闪烁通过 flash 函数实现,借用的是 CSS 的滤镜属性,通过audio标签的 ontimeupdate 事件驱动。
一进来先注意的是小球的闪烁,那么玄妙,和背景十分协调{:4_199:} 马黑黑 发表于 2025-7-18 14:48
播放器适用容器元素尺寸机制:
播放器 #player 选择器宽度设置为 15%,这将令其尺寸是动态变化的——依 ...
原来这里面还有这么多的自适应设置,可以保证不同分辨率下,小球的分布于帖子的框相匹配,特地去试了一下,改变分辨率,小球果然有过渡性相应呢{:4_199:} 漂亮!谢谢马老师经典讲授和精彩示范{:4_190:} 杨帆 发表于 2025-7-18 22:24
漂亮!谢谢马老师经典讲授和精彩示范
{:4_190:} 空灵,精彩{:4_178:} 老师好,学生已交作业,请您指正{:4_190:}
页:
[1]