鸿蒙那个开场动画挺帅的 试着用 CSS 也整一个这样的花活
前言
大家知道有一阵子鸿蒙系统很有名,朋友圈里经常会出现有关鸿蒙的新闻以及大家对它的看法评价之类的。就在某天下午摸鱼的时候突然刷到了一条视频:
突然就有一种眼前一亮的感觉!哇!好酷啊!突然职业病就犯了!
前端还有职业病?那当然了!前端其实也是个大方向,也可以细分为很多种的,比方说有的前端专做中后台管理系统、有的前端是数据可视化方向、有的前端走的是架构方向、有的是GIS
地图、有的是WebGL
、ThreeJS
、有的则是小程序、小游戏之类的…
太多太多了根本数不过来,而我呢,(以前)是属于炫酷交互类方向的。这也是我当初选择前端的重要原因之一。大学时上网页课的时候,老师就给我们展示了许多国外非常有创意的网页,尤其是看到了一些非常炫酷的交互动画后我的眼睛不知怎的就移不开了(请原谅我这条颜狗)并且非常想要学会如何制作这么酷炫的效果,总之那时我就爱上了前端。
但毕业后我发现前端跟我想的不太一样啊,哪来那么多炫酷特效?干的都是非常无聊非常枯燥的事情(尤其涉及到浏览器兼容性的时候)此时我觉得这并不是我想要的,如果那么枯燥无聊的话当初还不如选Java
好了,工资还能高点。而且非专业人员看不懂不会对你指手画脚的,不像前端谁都能来看一眼提点意见:这个按钮颜色浅了、那个效果不好看了、诶?你这IE
上怎么白屏了?
于是乎我跳了,想要炫酷特效方向的工作就得对症下药啊!不再是看见前端的岗位就投简历,而是看公司方向,找广告类、智慧城市类的投,结果果然如我所愿。
不过那都是之前的事了,现在的这家公司是比较专业的互联网企业,没那么多炫酷特效。但我也已经习惯了,特效做多了也觉得没意思,甚至会怀疑自己是不是只会做特效?如果是互联网企业的需求自己能不能胜任?
好在还是没问题的!
我的职业病就是当看见一个特别炫酷的效果时,也甭管它到底是不是网页上的特效,我都会试着用前端的技术去实现一遍。
拥有类似职业病的可不止我一个,所有那些以炫酷特效主题为主的博主,好多文章都是在哪哪哪看到了怎么样的一个特效然后实现一版供大家敬仰学习。
这样的职业病对我们来说很有多好处:比方说大家熟知的尤雨溪
,在他那个年代网页的CSS
以及JS
能力都很垃圾,比不过安卓、IOS
它们。但尤大却用web
技术复刻了当时比较风靡的一款有着炫酷交互效果Todo
App然后发到了网上写了篇文章来宣传,他一下就出名了。科技巨头Google
向他抛来了橄榄枝,从此便踏上了前端之旅~
而对于我等普通人来说,有时候接私活,对面通常会甩过来几个国外网站的链接,说想要把这几个网站的效果合并到一起去。一点进去就会出现特别炫酷的效果,但实际上好多效果我们见多识广早就实现过类似的,无压力~
不过呢,我们还是会装作一副非常为难的样子:哎呀这个实在是太复杂了!倒也不是做不了,得加钱!
所以建议有想接特效类私活的老铁们没事多看看这类文章,小程序类、管理系统类私活卷的要死根本不赚钱。反而是特效类竞争少要得上价。不要动不动就冷嘲热讽:一个Gif
就搞定了,至于这么麻烦吗?每当看到这类评论时我都忍不住想吐槽:前端那么多种,你觉得没意义不代表对所有人都没意义呀!既然看标题就知道这是篇特效类文章,而你平时又不做特效,那何必还要点进来看一篇对自己帮助不大的文章呢?就是想指点一下江山呗?
就像是内科医生非要点进外科医生的论文链接然后在底下极尽嘲讽一样。
分析动画
扯远了哈!咱们本来讨论的是鸿蒙的开场动画,那么我们就要开始对这个动画进行分析了。第一次看的时候感觉挺惊艳的,因为感觉就像是一轮空心的明月在水面上升起,水面倒映着月影,让我想起了唐朝著名浪漫主义诗人李白的那句:举杯邀明月,对影成三人
:
前半秒我居然没看出来这到底是个什么,等这个空心的满月全部升起的时候我才知道原来这是一个字母:〇
既然是倒映,那么就证明上下的显示是一致的,只不过是倒了过来,那么我们有(包括但不仅限于
)如下几种选择:
-webkit-box-reflect
(专门做倒影或侧影的一个属性)(火狐和IE不支持
)-moz-element()
(可以将DOM的某部分当作图片渲染)(只有火狐支持
)- 复制一遍DOM 然后用
transform: rotate(180deg);
把它给倒过来
想了一下虽然最后那个最麻烦,但最合适的还是它,不仅仅因为它的兼容性最好,而是因为仔细观察了一下鸿蒙的开场动画,那个倒影是有一定的模糊程度的。-webkit-box-reflect
只能控制方向及透明度或渐变透明度,但无法添加模糊效果。-moz-element()
虽然非常强大,但只有火狐支持那是肯定不行的,要知道目前火狐在浏览器市场的占比已经非常低了,所以只好用不那么优雅的第三个方式了,首先我们需要绘制一个半圆形的圆环,你能想到几种方式?
第一种:
一大一小两个半圆,小半圆的背景色保持与页面背景色一致的颜色,然后盖在大半圆上(就像日环食那样
),这样看起来就像是个圆环啦(原理示意图
):
第二种:
先写出来个半圆,不给加背景色,只给加边框,最后把下边框去掉
,于是看起来就是个半圆环啦(原理示意图
):
第三种:
直接写个圆,然后写上边框,圆环外套个容器,外层容器高度为圆的一半,最后overflow: hidden;
隐藏掉露在外面那半部分(原理示意图
):
第四种:
把第三种的overflow: hidden;
换成clip-path
(原理示意图
):
第五种:
直接用SVG
或Canvas
来进行绘制(原理示意图
):
最终还是选择了overflow: hidden;
,因为用它来做圆环升起的效果很合适,把露在外面的那部分圆隐藏掉,然后控制圆的位置,看起来就像是一轮空心的明月从海面上升起来了一样(原理示意图
):
接下来再把两个半圆环拼接到一起去就可以了(原理示意图
):
去掉为了向大家展示原理的那些杂七杂八的动画之后,显示出来的最终效果如下:
是不是有那么一点点神似了呢?不过在细节上跟鸿蒙的那个开场动画比起来还是差了许多,比方说后来我又找到了一版鸿蒙开场动画,如果跟以前的动画比起来的话,现在这版本在细节上的处理就更加的游刃有余了:
首先我们可以看到这个〇
有一个外发光的效果,在黑色背景的衬托下显得格外明亮,鸿蒙第一版的那个动画其实也有外发光,大家可以翻上去仔细对比一下,那一版的外发光没有这一版明显,而且细节处理的也没有这版好。那版是在〇
全部显现之后立刻消除掉外发光,有些略显生硬。而仔细看这版的话可以发现外发光是在不知不觉的过程中消失的。CSS的外发光效果其实很好做,就是在黑色背景下用box-shadow
给元素添加一个适当模糊的白色阴影,然后求阴影部分面积:
此时溢出隐藏(overflow: hidden;
)这个方案的缺点就会被暴露出来,由于我们的阴影部分面积
在上下左右四个方向已经超出了外面盒子的宽高,所以被隐藏掉了,我们只好为外面的盒子加入内边距padding
来解决掉这个缺陷:
我们也把我们的〇
变白变粗,但仔细看又会发现新的问题:那就是box-shadow
默认只会在元素的外部添加阴影,我们〇
这个圆圈的内部却没有阴影,好在box-shadow
是支持多重阴影和内阴影的:
而且这种效果用filter: drop-shadow();
也同样可以实现,不过由于在谷歌内核的浏览器中,filter: drop-shadow();
在动态变化的元素上渲染效果并不如box-shadow
那样理想:
所以我们决定还是采用box-shadow
内外双阴影的方案,现在看起来已经不错了,但还是少了点什么,少的就是圆环倒映在水面上的模糊效果。要知道在日常生活中,倒映在水面上的图案通常会比真正的视图稍稍模糊一点:
这是因为水面其实并不是一个完全平整的平面,哪怕再小的风也会导致水面上产生一定的水波:
正是这些水波导致了倒映在水面上的图案会产生一定的模糊度,水波的波纹越细,模糊程度就会越精致。正如上面那张图一样,水波的波纹不够细,就会导致我们就能够看到水波的纹理,就像鸿蒙的效果图那样:
他这水波的纹理搞得跟指纹一样… 如果要咱们写出这样的一个滤镜的话还是非常困难的,但好在鸿蒙的开场动画并没有能够看到水波的纹理,所以咱们就可以用模糊效果(filter: blur(2px);
)来写:
这个效果跟鸿蒙的开场效果比起来差距可就不是一星半点了,所以说鸿蒙那个动画虽然看起来简单,好像就是一个圆环从水面上升起来的效果,但实际上蕴含的细节只有亲自动手试一遍才会知道。
模糊细节
我们来把咱们做的圆环升起时的效果和鸿蒙圆环升起时的效果截张图放在一起对比一下:
发现没有?咱们用的CSS模糊,模糊方向是上下左右东南西北等各个方向的,而鸿蒙的模糊方向是沿着Y轴
也就是上下方向的模糊,如果还是看着不太明显的话那咱们再来截一张图看看:
这个是圆环未完全升起时的效果,这回应该能比较明显的看出来,水面下的圆环越靠下模糊程度就越高,并且它的模糊主要是沿着上下两个方向来进行模糊的。而且在向下方向的模糊程度要比在向上方向时的模糊程度要高上许多,这样看起来就会比较真实,才能给用户一种在水面上升起的错觉。
如果不分青红皂白的按照各个方向一顿模糊的话,那么圆环看起来的效果就怎么也不像是在水面上的感觉了:
对于这种带着方向带着渐变带着不同程度的模糊效果,我们就不能指望CSS了。这种场景下需要用到的是更为底层也更加复杂的SVG滤镜
!
其实好多CSS属性都是从
SVG
那里获得的灵感,比如说我们较为常用的pointer-events
、filter
等,还有一些不常用的clip-path
、mask
等…
由于CSS提供给我们的模糊只能各个方向都模糊,而在目前这种情况下我们需要的是沿着Y轴模糊
,那么SVG
的代码就可以写成这样:
<svg>
<filter id="blur">
<feGaussianBlur in="SourceGraphic" stdDeviation="0 5"/>
</filter>
</svg>
看不懂没关系啊,大家只需要记住stdDeviation
这个属性是控制模糊的就可以了,如果只给一个数字的话,就相当于全方位模糊,跟CSS的filter: blur();
效果是差不多的。但如果给了两个数字,那么第一个数字就代表X轴
模糊程度,第二个数字就代表Y轴
模糊程度。在这里我们让X轴的模糊程度为0
、Y轴的模糊程度为5
,注意不要像写CSS的时候给加单位(px
),这里只写数字就好了,不要带单位。
由于CSS的滤镜属性
filter
本来就是从SVG
那边吸收过来的,所以在CSS中可以使用SVG滤镜
!用法如下:
filter: url(#blur);
我们之前不是在SVG的<filter>
标签上加了一个id
属性么,这个id
就可以写在CSS滤镜
的url
里。但也不知是谷歌浏览器的filter
在动态变化的元素上渲染不好还是怎么着,总之在Chrome
浏览器里显示效果是这样的:
而在Safari
浏览器里是这样的:
在火狐
浏览器里效果最为完美:
那这可不行啊,谷歌浏览器可是市场占有率最高的浏览器了,产品那边肯定通不过的!不过也不是没办法解决啦。在谷歌浏览器那显示的问题不就是一开始会有个缝嘛!那咱们就margin-top: -2px;
来让这两个半圆先负距离接触
,对于它俩来说也不用-18px
,-2px
就够啦:
最后一步,就是把下半圆的模糊效果去掉,让它真正的变成一个字母〇
:
没想到用了SVG
的CSS filter
居然没有任何的过渡效果,那就只好用requestAnimationFrame
来动态改变SVG
里的<feGaussianBlur>
上的stdDeviation
属性啦:
完整代码
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Harmony</title>
<style>
* {
padding: 0;
margin: 0;
}
html, body { height: 100% }
body {
background: black;
display: flex;
align-items: center;
justify-content: center
}
.ul {
position: relative;
width: 100px;
height: 50px;
padding: 10px;
list-style: none;
overflow: hidden
}
.ul:first-of-type {
padding-bottom: 0
}
.ul:last-of-type {
padding-top: 0;
/* margin-top: -2px; */
/* animation: container-move .1s 1.2s forwards */
}
.harmony {
position: absolute;
top: 10px;
left: 10px;
width: 70px;
height: 70px;
border: 15px solid white;
border-radius: 50%;
transform: translateY(50%);
box-shadow: 0 0 6px white, inset 0 0 6px white;
animation: move 1.2s forwards
}
.ul:last-of-type > .harmony {
top: auto;
bottom: 10px;
transform: translateY(-50%);
filter: url(#blur)
}
svg {
width: 0;
height: 0
}
@keyframes move {
to { transform: none }
}
/* @keyframes container-move {
to { margin-top: 0 }
} */
</style>
</head>
<body>
<div class="container">
<ul class="ul">
<li class="harmony"></li>
</ul>
<ul class="ul">
<li class="harmony"></li>
</ul>
</div>
<svg>
<filter id="blur">
<feGaussianBlur in="SourceGraphic" stdDeviation="0 6"/>
</filter>
</svg>
<script>
const filter = document.querySelector('feGaussianBlur')
const clearFilter = () => {
const value = parseFloat(filter.getAttribute('stdDeviation').split(' ')[1]) - 0.06
if (value > 0) {
filter.setAttribute('stdDeviation', `0 ${value}`)
requestAnimationFrame(clearFilter)
} else {
return
}
}
setTimeout(clearFilter, 1200)
</script>
</body>
</html>
注释的那部分代码就是为了解决谷歌浏览器有缝隙的代码,可以解开注释对比一下在谷歌浏览器里的效果。不解开注释的话拿火狐浏览器打开效果是最好的
。最重要的一点是,我们可以通过修改代码里的数字来改变这个动画的效果:
大家觉得这仨哪个更好看呢?当然如果像鸿蒙那样作为开机动画来说,肯定是越快越好。因为这个动画可能也就前两次看着能有点新鲜感。但每次开机都看这么个动画,很快就会审美疲劳了,用户只希望能够快点开机少整点那些花里胡哨的。
不过如果抛开这些应用场景的话,大家觉得是第一张那样让模糊慢慢消失好看,还是最后那张一边升起一边就把模糊度给擦除掉了好看呢?
往期精彩文章
转载自:https://juejin.cn/post/6979042510400126983