CSS 高度还原鸿蒙OS开机动画 HarmonyOS

遥遥领先~

前言

最近在掘金上看到一位大佬用CSS写了鸿蒙OS的开机动画(原文在这里),不过只是字母O的出场特效,看完觉得挺有意思,就想着能不能把整个动画效果给实现出来,说干就干,也不管能不能实现,拿起键盘就是一顿敲......

当然了,开始敲之前肯定是先在网上搜一波,看有没有现成的,如果有人已经写了并且还原度很高,那我直接copy过来看一下不就得了😅, 还敲个锤子......

然后我网上搜到的答案只有这两个:

  • 纯css实现华为鸿蒙开机动画:这个是css实现的,但是最终效果差了点意思,只是做了个大概,与预期有点差距

  • codepen:这个是codepen上搜到的,看着还原度更高了一点,有了聚光灯模糊渐变的效果,但是用的是canvas

最后就是不想搞这么麻烦,只想用css一把梭,能用css实现的动效,我就不想用js🤔......

分析动画

仔细看了好几遍鸿蒙OS的开机动画,大致分成以下几个阶段:

  1. 字母O的睁眼效果,直接把上面大佬写的copy下来
  2. HarmonyOS 除了O之外的其他字母显现,并且整体缩小向左平移
  3. 平移的同时会有一个聚光灯效果,像一个手电筒照亮在O上面,然后扩散至全部字母显现
  4. 字母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>
相关推荐
魏大帅。3 分钟前
Axios 的 responseType 属性详解及 Blob 与 ArrayBuffer 解析
前端·javascript·ajax
花花鱼9 分钟前
vue3 基于element-plus进行的一个可拖动改变导航与内容区域大小的简单方法
前端·javascript·elementui
k093313 分钟前
sourceTree回滚版本到某次提交
开发语言·前端·javascript
EricWang135834 分钟前
[OS] 项目三-2-proc.c: exit(int status)
服务器·c语言·前端
September_ning34 分钟前
React.lazy() 懒加载
前端·react.js·前端框架
web行路人44 分钟前
React中类组件和函数组件的理解和区别
前端·javascript·react.js·前端框架
超雄代码狂1 小时前
ajax关于axios库的运用小案例
前端·javascript·ajax
长弓三石1 小时前
鸿蒙网络编程系列44-仓颉版HttpRequest上传文件示例
前端·网络·华为·harmonyos·鸿蒙
小马哥编程1 小时前
【前端基础】CSS基础
前端·css
嚣张农民2 小时前
推荐3个实用的760°全景框架
前端·vue.js·程序员