解读网易云的页面布局,构建独属动态

1,280 阅读10分钟

简言:

网易的年度听歌报告生动而有趣,独属的动画LOGO飘逸洒脱,字幕的渐变插入,按键的巧妙布局,背景的颜色变化,中心的点缀,以及懂你的歌曲联播...今天就设计一个浅简的网易页面布局。

HTML

根据页面所示:构建页面中的身体部落,定义各个部位名

<audio id="j-bgm" src="./assert/bgm.mp3"></audio>
    <div class="music-btn off"></div>

    <div class="view special">
        <div class="sun"></div>
        <!-- 荡秋千 -->
        <div class="art">
            <div class="swing j-anim03 z-anim">
                <!-- 腿 -->
                <div class="leg2">
                    <div class="jiojio"></div>
                </div>
                <!-- 腿 -->
                <div class="leg1">
                    <div class="jiojio"></div>
                </div>
                <!-- 脖子 -->
                <div class="neck"></div>
                <!-- 头 -->
                <div class="head">
                    <div class="part"></div>
                </div>
            </div>
        </div>
  1. <audio id="j-bgm" src="./assert/bgm.mp3"></audio>:这是一个音频元素,通过id属性为其指定了一个标识符"j-bgm"。该音频元素引用了名为"bgm.mp3"的音频文件,路径为"./assert/bgm.mp3"。这个音频元素用于在网页上播放背景音乐。
  2. <div class="music-btn off"></div>:这是一个div元素,具有"class"属性,其中包含了两个类名"music-btn"和"off"。这个div元素可能是用于控制音乐播放的按钮,"off"类可能表示按钮当前处于关闭状态。
  3. <div class="view special">...</div>:这是一个div元素,具有"class"属性,并包含了一些子元素,用于创建动画效果。其中可能包含了一些CSS类或JavaScript类来实现动画的效果。在这个div内部可能是一个场景的描述,类名"special"可能表示这个场景具有特殊的特征。
  4. <div class="view special">...</div>内部包含了一些div元素,用于创建动画效果。这些div元素可能是动画的组成部分,通过CSS或JavaScript来控制它们的动态效果。其中可能包含了"荡秋千"场景的描述,以及一些用于描述人物动作的div元素,如"swing"、"leg1"、"leg2"、"neck"和"head"等。

其次,插入报告总结,在HTML中用P标签输入对应的内容即可

<div class="paras">
            <p class="para f-animLineUp" style="transition-delay: 0.2s;">
                <em class="s-fcRed">1111日</em>
            </p>
            <p class="para f-animLineUp" style="transition-delay: 0.3s;">
                大概是很特别的一天
            </p>
            <p class="para f-animLineUp" style="transition-delay: 0.4s;">
                这一天里
            </p>
            <p class="para f-animLineUp" style="transition-delay: 0.5s;">
                你把郑源的
                <em class="s-fcRed">《被伤过的心还可以爱谁》</em>
            </p>
            <p class="para f-animLineUp" style="transition-delay: 0.6s;">
                反复听了
                <em class="s-fcRed">10次</em>
            </p>
        </div>

独属于个人的年度总结。

  1. <div class="paras">...</div>:这是一个div元素,具有"class"属性为"paras",用于包裹一系列段落文本。

  2. <p class="para f-animLineUp" style="transition-delay: 0.2s;">...</p>:这是一个段落元素,具有"class"属性为"para"和"f-animLineUp",这两个类可能用于指定段落的样式和动画效果。在内联样式中使用了"transition-delay"属性,设置了动画延迟时间为0.2秒。段落内部包含了一个<em>元素,用于强调文本内容,并且具有"class"属性为"s-fcRed",可能用于设置文本颜色为红色。这个段落可能用于显示日期信息,"11月11日"。

  3. 接下来的几个段落元素<p class="para f-animLineUp" style="transition-delay: ...">...</p>也具有相似的结构,它们显示了一些文本内容,并应用了相同的CSS动画效果"f-animLineUp"。每个段落的动画延迟时间逐渐增加,分别为0.3秒、0.4秒、0.5秒和0.6秒,以创建一个逐行渐现的效果。

  4. 在其中的某个段落中包含了一个<em>元素,用于强调文本内容,具有"class"属性为"s-fcRed",可能用于设置文本颜色为红色。这个<em>元素内部包含了一些歌曲标题或数字,如"《被伤过的心还可以爱谁》"和"10次"。

单独构建JS进行引用,在HTML中用scipt标签来摄取

<script>
            const viewSpecial = document.querySelector('.view .paras');
            const musicBtn = document.querySelector('.music-btn');
            let defaultMusicPlay = true;
            const bgMusic = document.querySelector('#j-bgm');

            musicBtn.addEventListener('click', () => {
                if (defaultMusicPlay) {
                    bgMusic.play();
                } else {
                    bgMusic.pause();
                }
                defaultMusicPlay = !defaultMusicPlay;
                musicBtn.classList.toggle('off');
            });

            setTimeout(() => {
                viewSpecial.classList.add('z-enter')
            }, 1000)
        </script>

根据各个组件的命名进行调用,进行页面的实操和动态效果。

  1. const viewSpecial = document.querySelector('.view .paras');:这行代码使用了document.querySelector方法,通过CSS选择器获取了页面中具有"class"属性为"view"的元素下的"class"属性为"paras"的元素,并将其赋值给变量viewSpecial

  2. const musicBtn = document.querySelector('.music-btn');:这行代码同样使用了document.querySelector方法,获取了页面中具有"class"属性为"music-btn"的元素,并将其赋值给变量musicBtn

  3. let defaultMusicPlay = true;:这行代码定义了一个变量defaultMusicPlay,用于记录音乐是否处于播放状态,默认为true,表示音乐初始状态是播放的。

  4. const bgMusic = document.querySelector('#j-bgm');:这行代码获取了页面中具有"id"属性为"j-bgm"的音频元素,并将其赋值给变量bgMusic

  5. musicBtn.addEventListener('click', () => { ... });:这是一个事件监听器,用于监听音乐按钮(musicBtn)的点击事件。当音乐按钮被点击时,会执行后面的箭头函数中的代码。

  6. if (defaultMusicPlay) { bgMusic.play(); } else { bgMusic.pause(); }:在点击事件中,根据defaultMusicPlay的值判断音乐当前是否正在播放。如果正在播放,则暂停音乐;如果未播放,则开始播放音乐。

  7. defaultMusicPlay = !defaultMusicPlay;:在点击事件中,将defaultMusicPlay的值取反,以记录音乐的当前播放状态。

  8. musicBtn.classList.toggle('off');:在点击事件中,切换音乐按钮的类名,如果原先有"off"类,则移除,如果原先没有"off"类,则添加,以改变音乐按钮的样式或状态。

  9. setTimeout(() => { viewSpecial.classList.add('z-enter') }, 1000):这行代码使用了setTimeout函数,在页面加载后延迟1秒执行,将给定元素viewSpecial添加类名"z-enter"。这可能是用于触发某种动画效果,如渐现或滑入效果。

CSS

实现对应的动态效果

* {
    margin: 0;
    padding: 0;
}

html,
body {
    width: 100%;
    height: 100%;
}

布置前先构建全局的样式。

  1. * { margin: 0; padding: 0; }:这是一个通用选择器("*"),用于选择页面中的所有元素。设置了所有元素的marginpadding属性值为0,这意味着去除了所有元素的外边距和内边距,使页面内容与浏览器窗口边缘之间没有任何空白间隙。
  2. html, body { width: 100%; height: 100%; }:这行代码选择了html元素和body元素,并为它们设置了宽度和高度均为100%。这意味着网页的html根元素和body元素将会占据整个浏览器窗口的宽度和高度,确保页面的内容占据整个可视区域。这通常用于确保页面布局的响应性和一致性。
/* music 按钮 */
.music-btn {
    top: 25px;
    left: 25px;
    z-index: 3;
    position: absolute;
    width: 40px;
    height: 40px;
    background: url(./assert/close.png) no-repeat center / cover;
}

.music-btn.off {
    background-image: url(./assert/music.png);
}

先拿按钮进行举例

这段CSS代码定义了一个名为.music-btn的样式规则,用于设置页面中音乐按钮的外观和位置。

  1. .music-btn { ... }:这是一个类选择器,用于选择具有"class"属性为"music-btn"的元素,并设置其样式。

    • top: 25px; left: 25px;:将音乐按钮的位置设置在距离父元素顶部和左侧各25像素的位置,这使得音乐按钮位于页面的左上角位置。

    • z-index: 3;:设置音乐按钮的层级为3,确保它位于其他元素之上,以便在页面上可见。

    • position: absolute;:将音乐按钮的定位方式设置为绝对定位,使其相对于最近的已定位祖先元素(通常是body元素)进行定位。

    • width: 40px; height: 40px;:设置音乐按钮的宽度和高度分别为40像素,这通常是一个合适的尺寸用于按钮。

    • background: url(./assert/close.png) no-repeat center / cover;:设置音乐按钮的背景样式为从"./assert/close.png"路径引用的图像,设置背景图像不重复(no-repeat),并且将其居中(center)显示,并让背景图像覆盖整个按钮区域(cover)。

  2. .music-btn.off { ... }:这是另一个类选择器,用于选择具有"class"属性为"music-btn"和"off"的元素,并设置其样式。通常是在音乐按钮被关闭时应用的样式。

    • background-image: url(./assert/music.png);:将音乐按钮的背景图像更改为从"./assert/music.png"路径引用的图像,这可能是一个表示音乐开关的图标,用于在音乐暂停或关闭时显示。

基于此,进行其他部位的类推,例如leg,head,swing。

对于动画效果的展示:使用@keyframes 加上类名进行创建

@keyframes ani4_qiuqian {
    0% {
        transform: rotateZ(0deg);
    }

    50% {
        transform: rotateZ(31.99359208deg);
    }

    100% {
        transform: rotateZ(0deg);
    }
}

这段代码定义了一个名为ani4_qiuqian的关键帧动画(Keyframes Animation),用于创建一个旋转动画效果。

  1. @keyframes ani4_qiuqian { ... }:这是关键帧动画的声明,ani4_qiuqian是动画的名称。

  2. 0% { transform: rotateZ(0deg); }:在动画的起始状态(0%)时,元素没有旋转,transform属性的rotateZ(0deg)将元素绕着Z轴旋转0度,即保持不动。

  3. 50% { transform: rotateZ(31.99359208deg); }:在动画进行到50%时,元素绕着Z轴旋转31.99359208度。这表示元素在旋转过程中达到了一半的旋转角度。

  4. 100% { transform: rotateZ(0deg); }:在动画结束状态(100%)时,元素回到原始状态,绕着Z轴旋转0度,即保持不动,完成了一次完整的旋转。

通过定义这些关键帧,可以创建一个周期性的旋转动画效果,元素将会从起始状态逐渐旋转到一定角度,然后再回到起始状态,形成一种往复的动画效果。

基于此依次进行构建布局,对于每段代码的解读这里就不再缀续了,直接附上全部代码

* {
    margin: 0;
    padding: 0;
}

html,
body {
    width: 100%;
    height: 100%;
}

/* music 按钮 */
.music-btn {
    top: 25px;
    left: 25px;
    z-index: 3;
    position: absolute;
    width: 40px;
    height: 40px;
    background: url(./assert/close.png) no-repeat center / cover;
}

.music-btn.off {
    background-image: url(./assert/music.png);
}

.view {
    width: 100%;
    height: 100%;
    overflow: hidden;
}

/* 第二屏 特殊 view special*/
.view.special {
    position: absolute;
    background-image:
        linear-gradient(60deg, #f8ddd1, #faece5 73%, #fad2c0);
}

/* 中间太阳 */
.sun {
    position: absolute;
    top: 45%;
    width: 283px;
    height: 283px;
    background: url(https://s5.music.126.net/static_public/5c21db8d4684556c72180904/sun.a3f575ae2fef2cfdae15011e6081a094.png) no-repeat;
    background-size: 100%;
    left: 50%;
    transform: translate(-50%, -50%);
}

/* 秋千 外层压缩 */
.art {
    transform: scale(.5);
    position: absolute;
    top: -140px;
    right: 0;
    width: 750px;
    height: 1334px;
    transform: scale(.5);
    transform-origin: top right;
}

/* 秋千动画 */
.swing {
    display: block;
    position: absolute;
    left: 226px;
    top: -180.25px;
    width: 478px;
    height: 1038px;
    background: url(https://s5.music.126.net/static_public/5c21db8d4684556c72180904/swing.88545d6c8e1ac798465e367f8e5357ab.png) no-repeat;
    transform-origin: -16.10878661% -29.76878613%;
    animation: ani4_qiuqian 6s cubic-bezier(.455, .03, .515, .955) infinite;
}

@keyframes ani4_qiuqian {
    0% {
        transform: rotateZ(0deg);
    }

    50% {
        transform: rotateZ(31.99359208deg);
    }

    100% {
        transform: rotateZ(0deg);
    }
}

/* 腿动画 */
.swing .leg2 {
    display: block;
    position: absolute;
    left: 185.375px;
    top: 958px;
    width: 130px;
    height: 32px;
    background: url(https://s5.music.126.net/static_public/5c21db8d4684556c72180904/leg2.d7bc44a91b6974450f2ccc430846c63d.png) no-repeat;
    transform-origin: 91.1538461538462% 33.59375%;
    animation: ani7_leg2 8s ease infinite;
}

@keyframes ani7_leg2 {
    0% {
        transform: rotate(0deg);
    }

    25% {
        transform: rotate(-86.98199658deg);
    }

    50% {
        transform: rotate(0deg);
    }

    75% {
        transform: rotate(-86.98199658deg);
    }

    100% {
        transform: rotate(0deg);
    }
}

.swing .leg2 .jiojio {
    display: block;
    position: absolute;
    background: url(https://s5.music.126.net/static_public/5c21db8d4684556c72180904/leg2-part.8f70bb7fc789a70bc78c48aa7718a765.png) no-repeat;
    left: -27.75px;
    top: -10.5px;
    width: 57px;
    height: 44px;
}

.swing .leg1 {
    display: block;
    position: absolute;
    left: 290.375px;
    top: 955.25px;
    width: 63px;
    height: 130px;
    background: url(https://s5.music.126.net/static_public/5c21db8d4684556c72180904/leg1.b1df6a7d1a794d36fbd0e1277733e1cf.png) no-repeat;
    transform-origin: 17.8571428571429% 13.3653846153846%;
    animation: ani5_leg1 8s ease infinite;
}

@keyframes ani5_leg1 {
    0% {
        transform: rotate(0deg);
    }

    25% {
        transform: rotate(108.97744399deg);
    }

    50% {
        transform: rotate(0deg);
    }

    75% {
        transform: rotate(108.97744399deg);
    }

    100% {
        transform: rotate(0deg);
    }
}

/* 腿的一部分 */
.leg1 .jiojio {
    display: block;
    position: absolute;
    left: 26.25px;
    top: 102.5px;
    width: 39px;
    height: 62px;
    background: url(https://s5.music.126.net/static_public/5c21db8d4684556c72180904/leg1-part.f2f17703a6af8fd2af5e0f5a9f320623.png) no-repeat;
}

.swing .neck {
    position: absolute;
    left: 451.125px;
    top: 855.5px;
    width: 51px;
    height: 42px;
    background: url(https://s5.music.126.net/static_public/5c21db8d4684556c72180904/neck.07a0013beff9796ed79c2cea542e5af2.png) no-repeat;
}

/* 头 */
.swing .neck,
.swing .head {
    display: block;
    position: absolute;
    left: 451.125px;
    top: 855.5px;
    width: 51px;
    height: 42px;
}

/* 脖子 */
.swing .neck {
    background: url(https://s5.music.126.net/static_public/5c21db8d4684556c72180904/neck.07a0013beff9796ed79c2cea542e5af2.png) no-repeat;
}

/* 头 */
.swing .head {
    background: url(https://s5.music.126.net/static_public/5c21db8d4684556c72180904/head.90bf892023d7df0522a4b53fc07e38df.png) no-repeat;
    animation: ani2_head 8s ease infinite;
}

/* 头发 */
.swing .head .part {
    background: url(https://s5.music.126.net/static_public/5c21db8d4684556c72180904/head-part.22d4381c4bd6cb1c3afd2b1bfcfe22f1.png) no-repeat;
    left: 20px;
    top: 2px;
    width: 40px;
    height: 47px;
    position: absolute;
}

@keyframes ani2_head {
    0% {
        transform: rotate(0deg);
    }

    25% {
        transform: rotate(-55deg);
    }

    62.5% {
        transform: rotate(-55deg);
    }

    87.92% {
        transform: rotate(0deg);
    }

    100% {
        transform: rotate(0deg);
    }
}

/* 特殊的内容 */
.paras {
    bottom: 110px;
    left: 10.67%;
    position: absolute;
    line-height: 1.6667;
    letter-spacing: 1px;
    color: #333;
}

.s-fcRed {
    color: #df493a;
}

.z-enter .f-animLineUp {
    opacity: 1;
    transform: translateY(0);
    transition: opacity 1.2s, transform 1s;
}

.f-animLineUp {
    opacity: 0;
    transform: translateY(6px);
}

em,
i {
    font-style: normal;
    text-align: left;
}

以上便能实现全部动态效果的实现了。

结尾

这里总结几点要素:

对于部位的要素位置,要学会灵活使用定位的布局,防止按键的失联。

动画记得循环的时间节点,可以尽可能详细。

按钮页面的布局,防止失效。