网易云年度报告如何实现?

950 阅读6分钟

先来看看效果吧

事实上左上角这个位置还有一个图标,它可以用来控制音乐的开关以及点一下音乐图标会变成×

image.png

image.png

那么眼前这个荡秋千的小帅是如何实现的呢?让我们开动脑筋来想一想。

思路:

  • 众所周知,打开这个页面会有音乐,因此需要一个audio标签
  • 其次左上角有个控制开关,因此可以放一个div盒子,这里类名我们叫做:music-btn和off(多类名)
    <audio src="./assets/bgm.mp3" id="j-bgm"></audio>
    <div class="music-btn off"></div>
  • 这个小图标永远都在这里,既如此我们不妨直接固定定位把他确定在这个位置
  • z-index,设置层级,它需要永远定在这,不能让别人的层级大于他,默认层级为0,因此我们只需要将他设置为大于0的任意值就可以了,这里我们直接设置为999
.music-btn {
    position: fixed;
    top: 25px;
    left: 25px;
    width: 40px;
    height: 40px;
    background: url(./assets/close.png);
    background-size: cover;
    z-index: 999;
}
  • 为什么写成多类名的形式?因为这里点击一下会变成×,需要准备两张照片,一开始我们让他显示音乐图标,点击之后变成×。
.music-btn.off {
    background-image: url(./assets/music.png);
}
  • 可以看到由于优先级的关系,这里先显示音乐图标,它同时选中两个类名选择器,上面那个只有一个,权重更大。

说到这个地方,先补充一个小知识点

.music-btn.off和.music-btn .off有什么区别?

  • 前者是选中同时具备这两个类名的标签,而后者是前后代的关系。

接下来继续思考准备工作

image.png 可以看见,中间有一个大太阳,因此我们接着搭框架 -用一个类名为view的div存放类名为sun的div、类名为art的div(秋千)、以及类名为paras的div用于存放文字,那么至此,整个html的框架部分就出来了,当然这里的(art)div中还需要存放秋千部分,并且它包含着腿1(包含脚)、腿2(包含脚)、脖子、头。

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

    <div class="view">
        <div class="sun"></div>
        <!-- 秋千 -->
        <div class="art">
            <div class="swing">
                <div class="leg2">
                    <div class="jiojio"></div>
                </div>

                <!-- neck -->
                <div class="neck"></div>
                <!-- head -->
                <div class="head">
                    <div class="part"></div>
                </div>

                <div class="leg1">
                    <div class="jiojio"></div>
                </div>
            </div>
        </div>
        
        <!-- 特殊内容 -->
        <div class="paras">
            <p class="para f-animLineUp" style="transition-delay: 0.2s;">
                <em class="s-fcRed">11月11日</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>
    </div>

搭建完成,渲染框架(CSS)

  • html与body标签继承屏幕的宽高100%,view标签便继承body的宽高百分百
  • 设置超出则隐藏overflow: hidden;
  • 设置好相对定位,后续要基于view标签做定位
  • 可以看见这个背景是一个渐变色linear-gradient
  • 太阳基于view做定位因此太阳做一个绝对定位,当然图片大小要设置为覆盖整个容器,不然图片就不全了background-size: cover;

那么这种动画效果到底是如何实现的呢?

  1. 自定义动画:@keyframes name{}
  2. 调用:animation: name time way xxx
  • 具体如下:动画中可以自己设置,在百分之0的时候旋转多少,百分之25的时候旋转多少,数据越细腻,动画就越丝滑。
.swing {
    position: absolute;
    left: 226px;
    top: -180px;
    width: 478px;
    height: 1038px;
    background: url(https://s5.music.126.net/static_public/5c21db8d4684556c72180904/swing.88545d6c8e1ac798465e367f8e5357ab.png);
    transform-origin: -16% -30%;
    animation: ani_qiuqian 6s cubic-bezier(0.455, 0.03, 0.515, 0.955) infinite;
}

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

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

    100% {
        transform: rotateZ(0deg);
    }
}
  • 其他的部分也大致相同,附带代码:
* {
    margin: 0;
    padding: 0;
}

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

.music-btn {
    position: fixed;
    top: 25px;
    left: 25px;
    width: 40px;
    height: 40px;
    background: url(./assets/close.png);
    background-size: cover;
    z-index: 999;
}

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

.view {
    width: 100%;
    height: 100%;
    overflow: hidden;
    position: relative;
    background-image: linear-gradient(60deg, #f8ddd1, #faece5 73%, #fad2c0);
}

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

.art {
    position: absolute;
    top: -140px;
    right: 0;
    width: 375px;
    height: 667px;
    transform: scale(0.5);
    transform-origin: top right;
}

.swing {
    position: absolute;
    left: 226px;
    top: -180px;
    width: 478px;
    height: 1038px;
    background: url(https://s5.music.126.net/static_public/5c21db8d4684556c72180904/swing.88545d6c8e1ac798465e367f8e5357ab.png);
    transform-origin: -16% -30%;
    animation: ani_qiuqian 6s cubic-bezier(0.455, 0.03, 0.515, 0.955) infinite;
}

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

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

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

.leg2 {
    position: absolute;
    left: 185.375px;
    top: 958px;
    width: 130px;
    height: 32px;
    background: url(https://s5.music.126.net/static_public/5c21db8d4684556c72180904/leg2.d7bc44a91b6974450f2ccc430846c63d.png);
    transform-origin: 91.15% 33.59%;
    animation: ani_leg2 8s ease infinite;
}

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

    25% {
        transform: rotateZ(-87deg);
    }

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

    75% {
        transform: rotateZ(-87deg);
    }

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

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

.leg1 {
    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);
    transform-origin: 17.857% 13.365%;
    animation: ani_leg1 8s ease infinite;
}

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

    25% {
        transform: rotateZ(109deg);
    }

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

    75% {
        transform: rotateZ(109deg);
    }

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

.leg1 .jiojio {
    position: absolute;
    width: 39px;
    height: 62px;
    background: url(https://s5.music.126.net/static_public/5c21db8d4684556c72180904/leg1-part.f2f17703a6af8fd2af5e0f5a9f320623.png);
    left: 26.25px;
    top: 102.5px;
}

.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;
}

最后js部分,实现点击音乐图标变为×,以及音乐的播放与暂停。思路如下:

  1. 设置状态变量,默认状态为true,点击以后变为false
  2. play、pause控制播放与暂停
  3. 图标的转换:前面我们用了一个单类名和多类名选择器插入背景图的方式,这里我们只需要删除掉那个多类名选择器就可以变换图片了classList.add\classList.remove
    <script>
        //播放音乐
        var viewSpecial = document.querySelector('.view .paras');
        var musicBtn = document.querySelector('.music-btn');
        var bgMusic = document.getElementById('j-bgm');
        var defaultMusicPlay = true;

        musicBtn.addEventListener('click', function () {
            if (defaultMusicPlay) {
                bgMusic.play();
                // musicBtn.classList.remove('off');
            } else {
                bgMusic.pause();
                // musicBtn.classList.add('off');
            }
            musicBtn.classList.toggle('off');//如果有自动移除;如果没有自动添加

            defaultMusicPlay = !defaultMusicPlay;
        })
        setTimeout(() => {
            viewSpecial.classList.add('z-enter')
        }, 1000)

    </script>
  • 可以看到,这里我们并没有采用classList.add\classList.remove的方式,而是为了代码更加优雅,直接通过classList.toggle()方法能够取代那两步操作
  • 通过 defaultMusicPlay = !defaultMusicPlay;,也取代了小白同学再ifelse中重新复制为false、true的情况,代码更加简洁哦

如有帮助请大力支持~