HarmonyOS - 纯CSS实现吹灭蜡烛动画
Posted 开源基础软件社区官方
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了HarmonyOS - 纯CSS实现吹灭蜡烛动画相关的知识,希望对你有一定的参考价值。
作者:炒栗子
前言
前段时间HarmonyOS 3发布了,吸引了不少的眼球,为了体验鸿蒙应用开发,决定动手实现一个案例——通过css动画实现吹灭蜡烛动画,看了一下鸿蒙应用开发文档,有js和ets两种开发方式,综合考量了一下,决定采用js方式实现。
效果展示
实现思路
-
通过变换translate()的X轴位置,实现最右边蜡烛眼睛移动效果;
-
通过改变height、width和left属性实现最右边蜡烛的嘴巴吹起动画;
-
通过scale()和translate()动画函数实现最右侧蜡烛吹起过程的身体变化;
-
通过改变left属性实现最左侧蜡烛的水平方向的烟雾;
-
通过改变left和top属性实现最左侧蜡烛的竖直方向的烟雾;
-
通过变换translate()的Y轴方向、scale()、background-color、box-shadow和border-color实现蜡烛被吹灭时左移效果和蜡烛摩擦生火体色的变化;
-
通过translate()和rotateZ()函数实现蜡烛被吹灭时的左右抖动以及烛芯摩擦生火动画;
-
通过border透明度、translate()和scale()实现烛光点燃时产生的光晕;
-
通过backgroun-color、left、width和height实现火焰闪烁效果,通过translate-origin、rotate()和translate()实现眨眼效果;
- 通过background-color变化实现背景明暗变化。
组件实现
1. hml代码
分为三部分:最左边的蜡烛、最右边的蜡烛和底座;最右边蜡烛由烛芯、火焰、眼睛、嘴巴和烛体构成,最左边蜡烛由烛芯、眼睛、烛体、火焰和烟雾组成。
<div class="page">
<div class="wrapper">
<div class="candles">
<!-- 背景光 -->
<div class="light_wave"></div>
<!-- 最右边蜡烛 -->
<div class="candle1">
<div class="candle1_body">
<div class="candle1_eyes">
<div class="candle1_eyes-left"></div>
<div class="candle1_eyes-right"></div>
</div>
<div class="candle1_mouth"></div>
</div>
<div class="candle1_stick"></div>
</div>
<!-- 最左边蜡烛 -->
<div class="candle2">
<div class="candle2_body">
<div class="candle2_eyes">
<div class="candle2_eyes-left"></div>
<div class="candle2_eyes-right"></div>
</div>
</div>
<div class="candle2_stick"></div>
<div class="candle2_fire"></div>
</div>
<!-- 烟雾 -->
<div class="candle_smoke1"></div>
<div class="candle_smoke2"></div>
</div>
<!-- 底座 -->
<div class="floor"></div>
</div>
</div>
2. css代码
实现了最右边蜡烛眼睛向左看、嘴巴对最左边蜡烛的火焰吹气的动画和吸气形体变化的动画;
实现了最左边蜡烛的火焰闪烁、火焰被吹灭时产生的烟雾动效、烛芯扰动和蜡烛向左偏移的效果,蜡烛摩擦生火的效果。
.page
height: 100%;
width: 100%;
background-color: #FFF;
animation: change-background 3s infinite linear;
.floor
position: absolute;
left: 50%;
top: 50%;
width: 100%;
height: 9px;
margin: 0px 80px;
background-color: #673C63;
transform: translate(-50%, -50%);
box-shadow: 0px 2px 5px #111;
z-index: 2;
.candles
position: absolute;
left: 50%;
top: 50%;
width: 250px;
height: 150px;
transform: translate(-50%, -100%);
z-index: 1;
.candle1
flex-direction: column;
position: absolute;
left: 50%;
top: 50%;
z-index: 100;
width: 35px;
height: 100px;
background-color: #ffffff;
border: 3px solid #673C63;
border-bottom: 0px;
border-radius: 3px;
transform-origin: right center;
transform: translate(60%, -25%);
box-shadow: -2px 0px 0px #95c6f2 inset;
animation: expand-body 3s infinite linear;
.candle1_body
flex-direction: column;
.candle1_stick, .candle2_stick
position: absolute;
left: 50%;
top: 0%;
width: 3px;
height: 15px;
background-color: #673C63;
border-radius: 8px;
transform: translate(-50%, -100%);
.candle2_stick
height: 12px;
transform-origin: center bottom;
animation: stick-animation 3s infinite linear;
.candle1_eyes, .candle2_eyes
position: absolute;
z-index: 1000;
left: 50%;
top: 0%;
width: 35px;
height: 30px;
transform: translate(-50%, 0%);
.candle1_eyes-left
position: absolute;
z-index: 1000;
left: 40%;
top: 20%;
width: 5px;
height: 5px;
border-radius: 2px;
background-color: #673C63;
transform: translate(-70%, 0%);
animation: blink-eyes 3s infinite linear;
.candle1_eyes-right
position: absolute;
z-index: 1000;
left: 80%;
top: 20%;
width: 5px;
height: 5px;
border-radius: 5px;
background-color: #673C63;
transform: translate(-70%, 0%);
animation: blink-eyes 3s infinite linear;
.candle1_mouth
position: absolute;
left: 40%;
top: 20%;
width: 0px;
height: 0px;
border-radius: 20px;
background-color: #673C63;
transform: translate(-50%, -50%);
animation: uff 3s infinite linear;
.candle_smoke1
position: absolute;
left: 30%;
top: 50%;
width: 30px;
height: 3px;
background-color: grey;
transform: translate(-50%, -50%);
animation: move-left 3s infinite linear;
.candle_smoke2
position: absolute;
left: 30%;
top: 40%;
width: 10px;
height: 10px;
border-radius: 10px;
background-color: grey;
transform: translate(-50%, -50%);
animation: move-top 3s infinite linear;
.candle2
position: absolute;
left: 20%;
top: 65%;
z-index: 100;
width: 42px;
height: 60px;
background-color: #FFFFFF;
border: 3px solid #673C63;
border-bottom: 0px;
border-radius: 3px;
transform: translate(60%, -15%);
transform-origin: right center;
box-shadow: -2px 0px 0px #95c6f2 inset;
animation: shake-left 3s infinite linear;
.candle2_eyes
flex-direction: column;
.candle2_eyes-left
position: absolute;
left: 40%;
top: 50%;
width: 5px;
height: 5px;
display: flex;
border: 0px solid #673C63;
border-radius: 5px;
background-color: #673C63;
transform: translate(-80%, 0%);
animation: changeto-lower 3s infinite linear;
.candle2_eyes-right
position: absolute;
left: 80%;
top: 50%;
width: 5px;
height: 5px;
display: flex;
border: 0px solid #673C63;
border-radius: 5px;
background-color: #673C63;
transform: translate(-80%, 0%);
animation: changeto-greater 3s infinite linear;
.light_wave
flex-direction: column;
position: absolute;
top: 50%;
left: 50%;
width: 75px;
height: 75px;
border-radius: 75px;
z-index: 0;
transform: translate(0%, 70%) scale(2.5, 2.5);
border: 2px solid #FFFFFF;
opacity: 0.2;
animation: expand-light 3s infinite linear;
.candle2_fire
position: absolute;
z-index: 1000;
top: -20px;
left: 50%;
display: flex;
width: 16px;
height: 20px;
background-color: red;
border-radius: 20px;
background-color: #ff9800;
transform: translate(-40%, -50%);
animation: dance-fire 3s infinite linear;
/* 最右侧蜡烛眼睛闪烁 */
@keyframes blink-eyes
0%
opacity: 1;
transform: translate(-70%, 0%);
35%
opacity: 1;
transform: translate(-70%, 0%);
36%
opacity: 0;
transform: translate(-70%, 0%);
39%
opacity: 0;
transform: translate(-70%, 0%);
40%
opacity: 1;
transform: translate(-70%, 0%);
50%
transform: translate(-140%, 0%);
65%
transform: translate(-140%, 0%);
66%
transform: translate(-70%, 0%);
/* 最右侧蜡烛吸气形体变化 */
@keyframes expand-body
0%
transform: scale(1, 1) translate(60%, 0%);
40%
transform: scale(1, 1) translate(60%, 0%);
45%
transform: scale(1.1, 1.1) translate(60%, 0%);
55%
transform: scale(1.1, 1.1) translate(60%, 0%);
60%
transform: scale(0.89, 0.89) translate(60%, 0%);
65%
transform: scale(1, 1) translate(60%, 0%);
70%
transform: scale(0.95, 0.95) translate(60%, 0%);
75%
transform: scale(1, 1) translate(60%, 0%);
/* 最右侧蜡烛口型变化 */
@keyframes uff
0%
width: 0px;
height: 0px;
40%
width: 0px;
height: 0px;
50%
width: 15px;
height: 15px;
left: 30%;
54%
width: 15px;
height: 15px;
left: 30%;
59%
width: 5px;
height: 5px;
left: 20%;
62%
width: 2px;
height: 2px;
left: 20%;
67%
width: 0px;
height: 0px;
left: 30%;
/* 光影背景变化 */
@keyframes change-background
0%
background-color: #fff;
59%
background-color: #fff;
61%
background-color: #000;
97%
background-color: #000;
98%
background-color: #fff;
100%
background-color: #fff;
/* 最左侧蜡烛熄灭的水平方向烟雾 */
@keyframes move-left
0%
width: 0px;
left: 40%;
59%
width: 0px;
left: 40%;
60%
width: 30px;
left: 30%;
68%
width: 0px;
left: 20%;
100%
width: 0px;
left: 40%;
/* 最左侧蜡烛熄灭的竖直方向的烟雾 */
@keyframes move-top
0%
width: 0px;
height: 0px;
top: 0%;
64%
width: 0px;
height: 0px;
top: 0%;
65%
width: 10px;
height: 10px;
top: 40%;
left: 40%;
80%
width: 0px;
height: 0px;
top: 20%;
100%
width: 0px;
height: 0px;
top: 0%;
/* 最左侧蜡烛熄灭的向左偏移及蜡烛摩擦生火体色的变化 */
@keyframes shake-left
0%
left: 20%;
background-color: #FFFFFF;
border: 3px solid #673C63;
border-bottom: 0px;
border-radius: 3px;
40%
left: 20%;
background-color: #FFFFFF;
border: 3px solid #673C63;
border-bottom: 0px;
border-radius: 3px;
50%
left: 20%;
transform: translate(60%, 5%);
54%
left: 20%;
transform: translate(60%, 5%);
59%
left: 20%;
transform: translate(60%, 5%);
62%
left: 18%;
transform: translate(60%, 5%);
67%
left: 20%;
transform: translate(60%, 5%);
75%
left: 20%;
transform: scale(1.15, 0.85) translate(60%, 5%);
background-color: #fff;
border-color: #673C63;
91%
left: 20%;
transform: scale(1.18, 0.82) translate(60%, 5%);
background-color: #f44336;
border-color: #f44336;
box-shadow: -2px 0px 0px #f44336 inset;
92%
left: 20%;
transform: scale(0.85, 0.95) translate(60%, 5%);
95%
left: 20%;
transform: scale(1.05, 0.95) translate(60%, 5%);
97%
left: 20%;
transform: scale(1, 1) translate(60%, 5%);
/* 最左侧蜡烛熄灭时烛芯扰动和摩擦生火 */
@keyframes stick-animation
0%
left: 50%;
top: 0%;
transform: translate(-50%, 0%);
40%
left: 50%;
top: 0%;
transform: translate(-50%, 0%);
50%
left: 50%;
top: 0%;
transform: translate(-50%, 0%);
54%
left: 50%;
top: 0%;
transform: translate(-50%, 0%);
59%
left: 50%;
top: 0%;
transform: translate(-50%, 0%);
62%
left: 50%;
top: 0%;
transform: rotateZ(-15deg) translate(-50%, 0%);
65%
left: 50%;
top: 0%;
transform: rotateZ(15deg) translate(-50%, 0%);
70%
left: 50%;
top: 0%;
transform: rotateZ(-5deg) translate(-50%, 0%);
72%
left: 50%;
top: 0%;
transform: rotateZ(5deg) translate(-50%, 0%);
74%
left: 50%;
top: 0%;
transform: rotateZ(0deg) translate(-50%, 0%);
84%
left: 50%;
top: 0%;
transform: rotateZ(0deg) translate(-50%, 0%);
85%
transform: rotateZ(180deg) translate(-50%, 0%);
92%
left: 50%;
top: 0%;
transform: rotateZ(0deg) translate(-50%, 0%);
93%
left: 50%;
top: 0%;
transform: rotateZ(0deg) translate(-50%, 0%);
/* 最左侧蜡烛火焰光圈变化 */
@keyframes expand-light
0%
transform: translate(-25%, -50%) scale(2.5, 2.5);
border: 2px solid #FFFFFF;
opacity: 0.2;
10%
transform: translate(-25%, -50%) scale(0, 0);
border: 2px solid #FFFFFF;
opacity: 0;
20%
transform: translate(-25%, -50%) scale(1, 1);
26%
transform: translate(-25%, -50%) scale(2, 2);
border: 2px solid #FFFFFF;
opacity: 0.5;
27%
transform: translate(-25%, -50%) scale(2, 2);
border: 2px solid #FFFFFF;
opacity: 0.5;
28%
transform: translate(-25%, -50%) scale(2.5, 2.5);
border: 2px solid #FFFFFF;
opacity: 0.2;
29%
transform: translate(-25%, -50%) scale(0, 0);
border: 2px solid #FFFFFF;
opacity: 0;
58%
transform: translate(-25%, -50%) scale(2.5, 2.5);
border: 2px solid #FFFFFF;
opacity: 0.2;
50%
transform: translate(-25%, -50%) scale(1, 1);
56%
transform: translate(-25%, -50%) scale(2, 2);
border: 2px solid #FFFFFF;
opacity: 0.5;
57%
transform: translate(-25%, -50%) scale(2, 2);
border: 2px solid #FFFFFF;
opacity: 0.5;
59%
transform: translate(-25%, -50%) scale(0, 0);
border: 2px solid #FFFFFF;
opacity: 0;
90%
transform: translate(-25%, -50%) scale(1, 1);
95%
transform: translate(-25%, -50%) scale(2, 2);
border: 2px solid #FFFFFF;
opacity: 0.5;
96%
transform: translate(-25%, -50%) scale(2, 2);
border: 2px solid #FFFFFF;
opacity: 0.5;
89%
transform: translate(-25%, -50%) scale(0, 0);
border: 2px solid #FFFFFF;
opacity: 0;
100%
transform: translate(-25%, -50%) scale(2.5, 2.5);
border: 2px solid #FFFFFF;
opacity: 0.2;
/* 最左侧蜡烛火焰跳动 */
@keyframes dance-fire
0%
left: 40.8%;
width: 16px;
height: 20px;
background-color: #FFC107;
3%
left: 41.2%;
width: 16px;
height: 20px;
background-color: #FF9800;
7%
left: 40.8%;
width: 16px;
height: 20px;
background-color: #FFC107;
11%
left: 41.2%;
width: 16px;
height: 20px;
background-color: #FF9800;
15%
left: 40.8%;
width: 16px;
height: 20px;
background-color: #FFC107;
19%
left: 41.2%;
width: 16px;
height: 20px;
background-color: #FF9800;
23%
left: 40.8%;
width: 16px;
height: 20px;
background-color: #FFC107;
27%
left: 41.2%;
width: 16px;
height: 20px;
background-color: #FF9800;
31%
left: 40.8%;
width: 16px;
height: 20px;
background-color: #FFC107;
35%
left: 41.2%;
width: 16px;
height: 20px;
background-color: #FF9800;
39%
left: 40.8%;
width: 16px;
height: 20px;
background-color: #FFC107;
43%
left: 41.2%;
width: 16px;
height: 20px;
background-color: #FF9800;
47%
left: 40.8%;
width: 16px;
height: 20px;
background-color: #FFC107;
51%
left: 41.2%;
width: 16px;
height: 20px;
background-color: #FF9800;
55%
left: 40.8%;
width: 16px;
height: 20px;
background-color: #FFC107;
58%
left: 41.2%;
width: 16px;
height: 20px;
background-color: #FF9800;
59%
left: 40%;
width: 0px;
height: 0px;
89%
left: 40%;
width: 0px;
height: 0px;
90%
left: 40.8%;
width: 16px;
height: 20px;
background-color: #FFC107;
94%
left: 41.2%;
width: 16px;
height: 20px;
background-color: #FF9800;
/* 最左侧蜡烛摩擦生火的眼睛变小 */
@keyframes changeto-lower
0%
padding: 0px;
display: flex;
border-radius: 5px;
background-color: #673C63;
border-width: 0 0 0 0;
border: 0px solid #673C63;
transform: translate(-90%, 0%);
70%
padding: 0px;
display: flex;
border-radius: 5px;
background-color: #673C63;
border-width: 0 0 0 0;
border: 0px solid #673C63;
transform: translate(-90%, 0%);
89%
background-color: rgba(0, 0,0,0);
border: solid #673C63;
border-radius: 0px;
border-width: 0 2px 2px 0;
display: flex;
padding: 1px;
float: left;
transform-origin: 100% 0%;
transform: rotate(-45deg) translate(-50%, -65%);
90%
padding: 0px;
display: flex;
border-radius: 5px;
background-color: #673C63;
border-width: 0 0 0 0;
border: 0px solid #673C63;
transform: translate(-90%, 0%);
71%
background-color: rgba(0, 0,0,0);
border: solid #673C63;
border-radius: 0px;
border-width: 0 2px 2px 0;
display: flex;
padding: 1px;
float: left;
transform-origin: 100% 0%;
transform: rotate(-45deg) translate(-50%, -65%);
/* 最左侧蜡烛摩擦生火的眼睛变大 */
@keyframes changeto-greater
0%
top: 50%;
padding: 0px;
display: flex;
border-radius: 10px;
background-color: #673C63;
border-width: 0 0 0 0;
border: 0px solid #673C63;
transform: translate(-80%, 0%);
70%
top: 50%;
padding: 0px;
display: flex;
border-radius: 10px;
background-color: #673C63;
border-width: 0 0 0 0;
border: 0px solid #673C63;
transform: translate(-80%, 0%);
89%
top: 30%;
background-color: rgba(0, 0,0,0);
border: solid #673C63;
border-radius: 0px;
border-width: 0 2px 2px 0;
display: flex;
padding: 1px;
float: left;
transform-origin: 100% 0%;
transform: rotate(135deg) translate(-80%, 20%);
90%
top: 50%;
padding: 0px;
display: flex;
border-radius: 10px;
background-color: #673C63;
border-width: 0 0 0 0;
border: 0px solid #673C63;
transform: translate(-80%, 0%);
71%
top: 30%;
background-color: rgba(0, 0,0,0);
border: solid #673C63;
border-radius: 0px;
border-width: 0 2px 2px 0;
display: flex;
padding: 1px;
float: left;
transform-origin: 100% 0%;
transform: rotate(135deg) translate(-80%, 20%);
总结
基于js扩展的类web范式开发的css样式和标准的css有所不同,个人最直观感受有以下几点:
1)它不支持border属性里使用rgba()函数。
2)不支持background合并属性书写。
3)hml的元素display默认值为flex,容器内元素默认横向排列。
4)position的absolute定位相对的是父组件,z-index决定的是元素的渲染顺序,未设置position也能够使用。
此外,动画的火焰有点瑕疵,由于js扩展的类web范式开发的css样式不支持border-radius使用/分隔多组属性,蜡烛火焰没能实现宝塔形,只实现了坚果形。
如有错漏,请大家指正,每天进步一小点。
更多原创内容请关注:中软国际 HarmonyOS 技术团队
入门到精通、技巧到案例,系统化分享HarmonyOS开发技术,欢迎投稿和订阅,让我们一起携手前行共建鸿蒙生态。
以上是关于HarmonyOS - 纯CSS实现吹灭蜡烛动画的主要内容,如果未能解决你的问题,请参考以下文章
只需2张照片就能2D变3D,这个AI能自己脑补蜡烛吹灭过程,一作二作均为华人 | CVPR 2022...