遥遥领先~
前言
最近在掘金上看到一位大佬用CSS写了鸿蒙OS的开机动画(原文在这里),不过只是字母O的出场特效,看完觉得挺有意思,就想着能不能把整个动画效果给实现出来,说干就干,也不管能不能实现,拿起键盘就是一顿敲......
当然了,开始敲之前肯定是先在网上搜一波,看有没有现成的,如果有人已经写了并且还原度很高,那我直接copy过来看一下不就得了😅, 还敲个锤子......
然后我网上搜到的答案只有这两个:
-
纯css实现华为鸿蒙开机动画:这个是css实现的,但是最终效果差了点意思,只是做了个大概,与预期有点差距
-
codepen:这个是codepen上搜到的,看着还原度更高了一点,有了聚光灯模糊渐变的效果,但是用的是canvas
最后就是不想搞这么麻烦,只想用css一把梭,能用css实现的动效,我就不想用js🤔......
分析动画
仔细看了好几遍鸿蒙OS的开机动画,大致分成以下几个阶段:
- 字母O的睁眼效果,直接把上面大佬写的copy下来
- HarmonyOS 除了O之外的其他字母显现,并且整体缩小向左平移
- 平移的同时会有一个聚光灯效果,像一个手电筒照亮在O上面,然后扩散至全部字母显现
- 字母O下方的下划线动效
开敲
屏幕兼容问题
首先需要考虑的一个问题是兼容,当然这里说的不是兼容IE😥......只是需要简单兼容一下不同屏幕大小的显示,确保在PC和手机上能正常显示
简单用媒体查询rem布局写了一下不同分辨率屏幕下的显示大小,主要为了自己方便计算,这里贴一下代码,不做太多解释了,不熟悉的可以去了解一下rem布局的原理
css
html {
font-size: 10px;
}
@media screen and (max-width: 767px) {
html {
font-size: 6px;
}
}
@media screen and (min-width: 768px) and (max-width: 1023px) {
html {
font-size: 8px;
}
}
@media screen and (min-width: 1024px) and (max-width: 1199px) {
html {
font-size: 10px;
}
}
@media screen and (min-width: 1200px) {
html {
font-size: 12px;
}
}
字母O的睁眼动画就不做解释了,直接去看原文,我在此基础上做了修改
HarmonyOS 除了O之外的其他字母显现,并且整体缩小向左平移
html结构如下,做了一些修改
html
<div class="container">
<span class="delay">Harmony</span>
<div class="letter">
<ul class="ul">
<li class="harmony"></li>
</ul>
<ul class="ul">
<li class="harmony"></li>
</ul>
<svg>
<filter id="blur">
<feGaussianBlur in="SourceGraphic" stdDeviation="0 6" />
</filter>
</svg>
</div>
<span class="delay">S</span>
</div>
除了O之外其他字母显现
这个很容易,用opacity
就可以实现,等2秒是让字母O动画结束后执行,加这个动画的目的主要是先让字母O执行完动画,配合聚光灯效果
css
.delay{
opacity: 0;
animation: fade_in 2s 0.5s forwards;
}
@keyframes fade_in {
to {
opacity: 1;
}
}
整体缩小向左平移
首先想到的是缩放然后向左平移,最后发现平移都没必要了,直接默认整体放大,原点设置在左侧中间位置,这样缩小动画的时候会整体以左边为基点恢复到原位
chrome在放大后动画感觉不是那么丝滑,没关系,不用在意这些细节......用火狐浏览器效果不错
scale(2.5)
大概试了个值,这个时候字母O刚好在中间,差不多就行,不用在意这些细节......
cubic-bezier(0.1, 0.6, 0, 1)
这个贝塞尔曲线是自己试出来的,感觉效果比较类似
css
.container {
position: relative;
display: flex;
justify-content: center;
font-size: 10rem;
color: white;
transform: scale(2.5);
transform-origin: 100% 50%;
animation: move 0.8s 1.5s cubic-bezier(0.1, 0.6, 0, 1) forwards;
}
@keyframes move {
to {
transform: none;
}
}
聚光灯效果
html结构新增<div class="shadow__spot"></div>
与class="container"
同级
这个其实我首先想到的就是类似聚光灯效果的蒙版扩散,所以在codepen上搜了一下,有个效果比较满意:XWdRwrm,但是这个css属性值不能使用动画过渡,想实现的话应该需要js,所以我又想到了用渐变色做蒙版好像也可以,但是渐变色的属性值也是不支持动画过渡的,我改成红色先看一下效果:
css
.shadow__spot {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: radial-gradient(circle, transparent 6rem, red 20rem);
animation: fade_spot 1s 1.5s forwards;
}
@keyframes fade_spot {
to {
background: radial-gradient(circle, transparent 30rem, red 60rem);
}
}
额......难道还得用js?
不,总有简单粗暴的方法,既然不支持过渡,那就直接放大,放大到全部显示为止......
css
.shadow__spot {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: radial-gradient(circle, transparent 6rem, red 20rem);
animation: fade_spot 1s 1.5s forwards;
}
@keyframes fade_spot {
to {
transform: translateX(10rem) scale(5);
}
}
效果好像还不错,把颜色改成黑色就大功告成了
注意这里加了个translateX(10rem)
让蒙版向右偏移了一点,主要目的是为了尽量让焦点聚焦在字母O上,更接近官方动画效果,也是个大概值......
注意这里scale(5)
放大5倍后是会出现滚动条的,所以在body上设置了overflow: hidden
字母O下方的下划线动效
这个比较简单就不多做解释了,直接flex布局,水平居中改变宽度就实现了
html结构中svg
标签下方添加<div class="line"></div>
css
.letter .line{
position: absolute;
bottom: 0;
width: 100%;
height: 1rem;
display: flex;
justify-content: center;
}
.letter .line::after{
content: '';
width: 0%;
background-color: #146df7;
animation: line 0.5s 1.7s forwards;
}
@keyframes line {
to {
width: 60%;
}
}
最后看一下最终效果,火狐浏览器比较丝滑,我录个火狐的运行gif图
是不是有内味了?
其实最后还有个扫光效果,懒得搞了,也是比较简单,类似于骨架屏的扫光
全部代码
html
<!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 {
height: 100%;
font-size: 10px;
}
body {
height: 100%;
display: flex;
align-items: center;
justify-content: center;
font-family: Arial;
background-color: black;
overflow: hidden;
}
@media screen and (max-width: 767px) {
html {
font-size: 6px;
}
}
@media screen and (min-width: 768px) and (max-width: 1023px) {
html {
font-size: 8px;
}
}
@media screen and (min-width: 1024px) and (max-width: 1199px) {
html {
font-size: 10px;
}
}
@media screen and (min-width: 1200px) {
html {
font-size: 12px;
}
}
.container {
position: relative;
display: flex;
justify-content: center;
font-size: 10rem;
color: white;
transform: scale(2.5);
transform-origin: 100% 50%;
animation: move 0.8s 1.5s cubic-bezier(0.1, 0.6, 0, 1) forwards;
}
.delay{
opacity: 0;
animation: fade_in 2s 0.5s forwards;
}
.letter {
position: relative;
display: flex;
flex-direction: column;
justify-content: center;
}
.letter .ul {
position: relative;
width: 7rem;
height: 3.5rem;
padding: 1rem;
list-style: none;
overflow: hidden;
}
.letter .ul:first-of-type {
padding-bottom: 0;
}
.letter .ul:last-of-type {
padding-top: 0;
/* margin-top: -2px; */
/* animation: letter-move .1s 1.2s forwards; */
}
.letter .ul .harmony {
position: absolute;
top: 1rem;
left: 1rem;
width: 5rem;
height: 5rem;
border: 1rem solid white;
border-radius: 50%;
transform: translateY(50%);
box-shadow: 0px 0px 1.5rem 0px white, inset 0 0 1rem white;
animation: move 1.2s forwards, shadow 1.5s 1.2s forwards;
}
.letter .ul:last-of-type>.harmony {
top: auto;
bottom: 1rem;
transform: translateY(-50%);
filter: url(#blur);
}
.letter svg {
position: absolute;
width: 0;
height: 0;
}
.letter .line{
position: absolute;
bottom: 0;
width: 100%;
height: 1rem;
display: flex;
justify-content: center;
}
.letter .line::after{
content: '';
width: 0%;
background-color: #146df7;
animation: line 0.5s 1.7s forwards;
}
.shadow__spot {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: radial-gradient(circle, transparent 6rem, black 20rem);
animation: fade_spot 1s 1.5s forwards;
}
@keyframes move {
to {
transform: none;
}
}
@keyframes shadow {
to {
box-shadow: none;
}
}
@keyframes fade_spot {
to {
transform: translateX(10rem) scale(5);
}
}
@keyframes fade_in {
to {
opacity: 1;
}
}
@keyframes line {
to {
width: 60%;
}
}
/* @keyframes letter-move {
to { margin-top: 0 }
} */
</style>
</head>
<body>
<div class="container">
<span class="delay">Harmony</span>
<div class="letter">
<ul class="ul">
<li class="harmony"></li>
</ul>
<ul class="ul">
<li class="harmony"></li>
</ul>
<svg>
<filter id="blur">
<feGaussianBlur in="SourceGraphic" stdDeviation="0 6" />
</filter>
</svg>
<div class="line"></div>
</div>
<span class="delay">S</span>
</div>
<div class="shadow__spot"></div>
<script>
const filterElem = document.querySelector('feGaussianBlur')
const clearFilter = () => {
const value = parseFloat(filterElem.getAttribute('stdDeviation').split(' ')[1]) - 0.06
if (value > 0) {
filterElem.setAttribute('stdDeviation', `0 ${value}`)
requestAnimationFrame(clearFilter)
} else {
return
}
}
setTimeout(clearFilter, 300)
</script>
</body>
</html>