HarmonyOS - 纯CSS实现吹灭蜡烛动画

Posted 开源基础软件社区官方

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了HarmonyOS - 纯CSS实现吹灭蜡烛动画相关的知识,希望对你有一定的参考价值。

作者:炒栗子

前言

前段时间HarmonyOS 3发布了,吸引了不少的眼球,为了体验鸿蒙应用开发,决定动手实现一个案例——通过css动画实现吹灭蜡烛动画,看了一下鸿蒙应用开发文档,有js和ets两种开发方式,综合考量了一下,决定采用js方式实现。

效果展示

实现思路

  1. 通过变换translate()的X轴位置,实现最右边蜡烛眼睛移动效果;

  2. 通过改变height、width和left属性实现最右边蜡烛的嘴巴吹起动画;

  3. 通过scale()和translate()动画函数实现最右侧蜡烛吹起过程的身体变化;

  4. 通过改变left属性实现最左侧蜡烛的水平方向的烟雾;

  5. 通过改变left和top属性实现最左侧蜡烛的竖直方向的烟雾;

  6. 通过变换translate()的Y轴方向、scale()、background-color、box-shadow和border-color实现蜡烛被吹灭时左移效果和蜡烛摩擦生火体色的变化;

  7. 通过translate()和rotateZ()函数实现蜡烛被吹灭时的左右抖动以及烛芯摩擦生火动画;

  8. 通过border透明度、translate()和scale()实现烛光点燃时产生的光晕;

  9. 通过backgroun-color、left、width和height实现火焰闪烁效果,通过translate-origin、rotate()和translate()实现眨眼效果;

  10. 通过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开发技术,欢迎投稿和订阅,让我们一起携手前行共建鸿蒙生态。

想了解更多关于开源的内容,请访问:

51CTO 开源基础软件社区

https://ost.51cto.com/#bkwz

以上是关于HarmonyOS - 纯CSS实现吹灭蜡烛动画的主要内容,如果未能解决你的问题,请参考以下文章

只需2张照片就能2D变3D,这个AI能自己脑补蜡烛吹灭过程,一作二作均为华人 | CVPR 2022...

HarmonyOS 纯css3版冰墩墩

吹灭蛋糕上蜡烛的节能小车

HarmonyOS- 基于ArkUI(eTs)实现猫头鹰动画

纯css实现文字特效动画

GitHub精选 使用纯CSS实现动画加载效果