前言
最近刷抖音刷微博啥的经常能看到鸿蒙
的消息,看到中国终于出现了一个有可能会在未来成气候的操作系统也是很开心。感觉谷歌被啪啪打脸,刚刚宣布安卓12不给华为用
,完了转头人家就拿出来个鸿蒙OS
,直接对标谷歌还在襁褓中的Fuchsia OS
。谷歌这就很尴尬,跟吧?安卓咋整?这可刚官宣安卓12啊!总不能像华为似的直接提示用户升级新系统吧,那就证明要淘汰掉安卓了!不跟吧?现在华为的万物互联操作系统已经能用了,如果不发布Fushsia
的话那就会被快速的抢占市场份额,尤其是当鸿蒙的生态成气候了之后,再想要夺回市场份额那可就是难上加难了!
我个人甚至认为反而是美国的制裁帮助了鸿蒙
系统,为什么这么说呢?首先,虽然鸿蒙之前在一直在开发中,但华为其实并没有押宝在这个系统上,也就是说给予该项目了一定的支持,但支持的程度并不特别大。后来美国一顿制裁,大家都感觉到了迟早会有一天安卓也不给华为用了,于是华为大大增加了对鸿蒙
这个项目的支持,开发这个项目的开发者们也铆足了劲势要突破美国的技术封锁,鸿蒙也就大大加快了它前进的脚步。后来全世界都知道美国是怎么欺负华为的了,尤其是中国人,觉得这也太欺负人了吧!既然我们出了新系统,很多人还是想要尽自己的一份力支持一下的。
设想一下,如果川建国(Trump)
同志没有高举贸易保护的大旗来痛下杀手的话、华为也能照常采购芯片等各种半导体产品、谷歌的GMS
华为也用的好好的、手机市场也没有萎靡不振、每年赚它个百八十亿的、安卓12
发布会也邀请华为一起参加…
那么此时华为推出一个 Harmony OS
,大家还会和现在这种态度一样吗?会不会觉得这个系统只是华为出于自己的商业利益而开发出来的系统?安卓系统好好的瞎折腾啥?华为手机的用户还会有多少人愿意升级到鸿蒙系统?而且大家对国货普遍存在质疑态度,在相差不大甚至小幅度领先的情况下大家都宁愿多花钱购买外国货。OPPO
如果还是和前阵子那样喷一下鸿蒙,大家还会说:如果OPPO
不适配鸿蒙以后就不买OPPO
了吗?抖音快手等各大短视频平台以及央视各种新闻还会给鸿蒙
这么大的曝光度吗?就算给了这么大的曝光度,大家不会反感吗?
听了他的演讲我还以为这是要让我们开发鸿蒙应用了,还好我做过功课,看了一下语法,感觉跟小程序有些类似,这下前端又要开始卷了… 以后面试的时候估计还要问鸿蒙应用的知识…
就在这时产品从他的口袋里掏出了一台搭载了鸿蒙操作系统的华为mate40
,问:大家都升鸿蒙系统了么?答:我们都用iPhone…
当然哈,我自己用的是安卓,不过也不是华为,而是便宜的realme(留下了没钱的泪水
),所以也没法升级。
产品:来让你们看看我的开机动画:
我希望这个字母〇
的动画能够移植到咱们的网站上面去,因为咱们的好多产品字母里同样也有个〇
!
分析动画
既然需求明确了下来,那么我们就要开始对这个动画进行分析了。第一次看的时候感觉挺惊艳的,因为感觉就像是一轮空心的明月在水面上升起,水面倒映着月影,让我想起了唐朝著名浪漫主义诗人李白的那句:举杯邀明月,对影成三人
:
前半秒我居然没看出来这到底是个什么,等这个空心的满月全部升起的时候我才知道原来这是一个字母:〇
既然是倒映,那么就证明上下的显示是一致的,只不过是倒了过来,那么我们有(包括但不仅限于
)如下几种选择:
-
-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>公众号:前端学不动</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>
注释的那部分代码就是为了解决谷歌浏览器有缝隙的代码,可以解开注释对比一下在谷歌浏览器里的效果。不解开注释的话拿火狐浏览器打开效果是最好的
。最重要的一点是,我们可以通过修改代码里的数字来改变这个动画的效果:
大家觉得这仨哪个更好看呢?当然如果像鸿蒙那样作为开机动画来说,肯定是越快越好。因为这个动画可能也就前两次看着能有点新鲜感。但每次开机都看这么个动画,很快就会审美疲劳了,只希望能够快点开机。
不过如果抛开这些应用场景的话,大家觉得是第一张那样让模糊慢慢消失好看,还是最后那张一边升起一边就把模糊度给擦除掉了好看呢?
本文首发于公众号:前端学不动
网友评论