scss神奇魔法完成一个手机充电动画

1,991 阅读4分钟

这是我参与11月更文挑战的第26天,活动详情查看:2021最后一次更文挑战

介绍

很早之前看过一个大神写的华为手机充电,感觉非常惊艳,一直记忆犹新,最近仿照着也动手实现了一个。本期也作为充电水滴动画的一次扩展延伸,我们将带大家一起通过使用scss从零开始完成这个手机充电动画的案例,里面包含了一些css小技巧,希望大家喜欢。

接下来,我们先来一睹为快吧:

VID_20211125_211135.gif

正文

1.基础结构

<div id="app">
    <div class="loading">
        <h5>60%</h5>
        <div class="loading-warrper">
            <div class="loading-circle"></div>
            <div class="loading-drop">
                <span></span>
                <span></span>
                <span></span>
                <span></span>
                <span></span>
                <span></span>
                <span></span>
                <span></span>
                <span></span>
                <span></span>
                <span></span>
                <span></span>
            </div>
        </div>
    </div>
</div>
@use "sass:math";
@import url(https://fonts.googleapis.com/css?family=Source+Code+Pro:400);
$color:#79f77f;
$num:12;

#app{
    width: 100%;
    height: 100vh;
    background-color: black;
    overflow: hidden;
    display: flex;
    justify-content: center;
    align-items: center;
    position: relative;
}

.loading{
    width: 360px;
    height: 360px;
    position: relative; 
    h5{
        width: 180px;
        height: 180px;
        position: absolute;
        top: 50%;
        left: 50%;
        transform: translate(-50%, -50%);
        font-family: 'Source Code Pro', monospace;
        font-size: 48px;
        font-weight: normal;
        color: white;
        z-index: 9;
        display: flex;
        align-items: center;
        justify-content: center;
    }
    .loading-warrper{
        position: fixed;
        top: 0;
        left: 0;
        width: 100%;
        height: 100vh;
        background-color: black;
        display: flex;
        align-items: center;
        justify-content: center;
        filter: contrast(10) hue-rotate(0);
    }
}

一开始我们会引入sass:math和字体,字体是为了电量文字使用,而引入sass:math是因为这个项目用了vite搭建使用了最新sass,而且最新的sass已经不支持“/”符号做除法了,取而代之的是Math.div(a,b),即a/b。然后我们定义了colorcolor与num分别代表了主色与span的数量。

div#app作为主屏,用黑色铺满全屏,h5主要为了显示当前电量,用绝对定位压住下面的div.loading-warrper目的是为了不被div.loading-warrper的filter属性影响,而且div.loading-warrper我们用fixed定位,铺满全屏,他的目的是后面的div.loading-drop一直置于底部,不管多长的屏幕都可以适配,div.loading-circle则就是后面马上要说的了,用他去做旋转。

微信截图_20211125221404.png

2.圆环旋转

.loading-circle{
    position: relative;
    width: 300px;
    height: 300px;
    box-sizing: border-box;
    filter: blur(8px);
    &::before {
        content: "";
        position: absolute;
        width: 180px;
        height: 180px;
        top: 50%;
        left: 50%;
        transform: translate(-50%, -50%);
        border-radius: 50%;
        background-color:black;
        z-index: 10;
    }
    &::after {
        content: "";
        position: absolute;
        top: 50%;
        left: 50%;
        width: 205px;
        height: 205px;
        transform: translate(-50%, -50%);
        background-color: $color;
        border-radius:42% 37% 55% 48% / 47% 43%;
        animation: rotate 10s infinite linear;
    }
}
@keyframes rotate {
    50% {
        border-radius:47% 43% / 42% 37% 55% 48%;
    }
    100% {
        transform: translate(-50%, -50%) rotate(720deg);
    }
}

这里要说明的就是div.loading-circle一定要带上filter: blur属性,他和上层的filter: contrast(10)配合,就是完成粘连效果两个必要属性。而其内部则是两个伪类,伪类after是绘制了一个椭圆,我们通过animation去完成一次旋转并改变其椭圆形状。伪类before则是绘制一个黑色圆形专门去遮盖伪类after,其实只显示边缘位置。

微信截图_20211125225759.png

3.充电动画

.loading-drop{
    position: absolute;
    bottom: -90px;
    left: 50%;
    margin-left: -50px;
    width: 120px;
    height: 120px;
    border-radius: 50%;
    background-color: $color;
    filter: blur(8px);
    span{
        width: 25px;
        height: 25px;
        display: block;
        position: absolute;
        border-radius: 50%;
        background: $color;
        @for $i from 0 through $num { 
            &:nth-child(#{$i}) {
                $size:25 + random(7) + px;
                left: 20 + random(80) + px;
                top: 50%;
                transform: translate(-50%, -50%);
                width: $size;
                height: $size;
                animation: up #{random(5) + 5}s ease-in-out infinite;
                animation-delay: -#{math.div(random(10000),1000)}s;
            }
        }
    }
}

@keyframes up {
    90% {
        opacity: 0.7;
    }
    100% {
        opacity: .5;
        transform: translate(-50%, calc(-50vh + 60px));
    }
}

这里在div.loading-drop依然要给他一个filter: blur属性,也是粘连效果。我们要画一个小圆用定位给他置于最下方,这样多高的屏幕他都会在最下面使他漏出一个小半圆来,然后我们给其下的span用过scss的@for方法去遍历他们,致使他们通过@random去随机获得大小、偏移还有动画的执行周期和延时。接下来动画就很简单了,我们最终目的是要让他往上飘,所以要改变其translateY的位置,我这里使用了css的计算方法,让往上飘到一边屏幕然后在加上一个圆的外围距离,这样就会兼容到所有高度了。

微信截图_20211125225823.png

4.变色动画

.loading-warrper{
    position: fixed;
    top: 0;
    left: 0;
    width: 100%;
    height: 100vh;
    filter: contrast(10) hue-rotate(0);
    animation: changeColor 5s infinite linear;
    background-color: black;
    display: flex;
    align-items: center;
    justify-content: center;
}
@keyframes changeColor {
    100% {
        filter: contrast(50) hue-rotate(360deg);
    }
}

我们期望颜色会随着时间不断发生变化,之前我们用了hsl去改变其第一个值H即灰度,这里就不用这么麻烦了,这里可以直接使用了filter: hue-rotate去改变,让其旋转360度,颜色就会随着时间推移而改变,这样是不是很方便。

微信截图_20211125230004.png

结语

不知道你从中有收获了么,你可以看到粘连效果的又一次实践,也看到颜色变化除了hsl还有filter: hue-rotate方案。也可以了解到scss中@for,@random,math等的一些用法。

另外,本次在线演示案例中我用了pug模板语法,让整个html结构更加干净高效,大家也可以去了解一下。