⚠️本文为掘金社区首发签约文章,未获受权禁止转载javascript
有一天开会,产品经理问:你们都升鸿蒙系统了么?紧接着一群人答:咱们都用iPhone…
固然哈,我本身用的是安卓,不过也不是华为(留下了没钱的泪水
)…css
听了他这么一问我还觉得这是要让咱们开发鸿蒙应用了,还好我作过功课,有提早了解过它的语法,感受跟小程序有些相似,这下前端又要开始卷了… 之后面试的时候估计还要问鸿蒙应用的知识…html
就在这时产品从他的口袋里掏出了一台搭载了鸿蒙操做系统的华为mate40
:来让大家看看个人开机动画:前端
我但愿这个字母〇
的动画可以移植到我们的网站上面去,由于我们的好多产品字母里一样也有个〇
!vue
害!原来不是要开发鸿蒙应用呀!java
既然需求明确了下来,那么咱们就要开始对这个动画进行分析了。第一次看的时候感受挺惊艳的,由于感受就像是一轮空心的明月在水面上升起,水面倒映着月影,让我想起了唐朝著名浪漫主义诗人李白的那句:举杯邀明月,对影成三人
:web
前半秒我竟然没看出来这究竟是个什么,等这个空心的满月所有升起的时候我才知道原来这是一个字母:〇
面试
既然是倒映,那么就证实上下的显示是一致的,只不过是倒了过来,那么咱们有(包括但不只限于
)以下几种选择:vue-router
-webkit-box-reflect
(专门作倒影或侧影的一个属性)(火狐和IE不支持
)-moz-element()
(能够将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>公众号:前端学不动</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>
复制代码
注释的那部分代码就是为了解决谷歌浏览器有缝隙的代码,能够解开注释对比一下在谷歌浏览器里的效果。不解开注释的话拿火狐浏览器打开效果是最好的
。最重要的一点是,咱们能够经过修改代码里的数字来改变这个动画的效果:
你们以为这仨哪一个更好看呢?固然若是像鸿蒙那样做为开机动画来讲,确定是越快越好。由于这个动画可能也就前两次看着能有点新鲜感。但每次开机都看这么个动画,很快就会审美疲劳了,用户只但愿可以快点开机少整点那些花里胡哨的。
不过若是抛开这些应用场景的话,你们以为是第一张那样让模糊慢慢消失好看,仍是最后那张一边升起一边就把模糊度给擦除掉了好看呢?