快过年了,我写了一个放鞭炮动画🔥

3,585 阅读4分钟

PK创意闹新春,我正在参加「春节创意投稿大赛」,详情请看:春节创意投稿大赛

效果展示

首先放上效果图:

11.gif

线上预览地址:鞭炮动画

ps:动画是一次性的,每次刷新才能看到效果,毕竟鞭炮燃放后就没了(偷懒了-_-)

实现

参考图片

微信截图_20220111162430.png

本文鞭炮静态展示页面是参考上面图片实现的,技术上用的vue + scss,动画效果就是css动画,没有用到js。

实现单个鞭炮

单个鞭炮其实就是一个圆柱体,所以我们先画个圆柱体再完善细节。

image.png

  1. 先画一个圆柱体的外表面,用linear-gradient属性设置为渐变背景色
  .item{
    margin: 10px;
    position: relative;
    width: 40px;
    height: 100px;
    background: linear-gradient(90deg,#6e0009,#e64743,#6e0009);
  }
  1. 利用伪元素画底和顶,为了底部有点层次感,给border设置为一层深色,再用outline设置为浅色:
  .item{
    // ...
    &::before,&::after{
      content: '';
      position: absolute;
      display: inline-block;
      width: 38px;
      height: 20px;
      border-radius: 50%;
    }

    &::before{
      width: 40px;
      top: -10px;
      background: linear-gradient(90deg,#6e0009,#e64743,#6e0009);
    }

    &::after{
      left: 1px;
      bottom: -9px;
      background: #9f6f2d;
      border: 1px solid #58301b;
      outline: 1px solid #9f6f2d;
    }
  }
  1. 最后画上下的黄色线条 image.png

黄色的线条其实是两个椭圆叠起来的效果,然后利用定位放在圆柱体上,因为和背景色是一样的所以才有这样的效果:

 .cicle{
    position: absolute;
    top: -6px;
    width: 100%;
    height: 20px;
    background: linear-gradient(90deg,#9f6f2d,#dfa351,#9f6f2d);
    border-radius: 50%;

    &::before{
      content: '';
      position: absolute;
      display: inline-block;
      top: 3px;
      width: 100%;
      height: 20px;
      border-radius: 50%;
      background: linear-gradient(90deg,#6e0009,#e64743,#6e0009);
    }

    &.cicle1{
      top: 82px;
    }
  }
  1. 细节调整 颜色我是吸的图片上的颜色,但是效果看起来有点黯淡,于是加了一层滤镜:
.container{
  filter: contrast(120%);
}

画单个的时候为了方便调整效果画的有点大,组合成一串鞭炮的时候适当缩小就行了。

单个鞭炮代码的完整代码:one.vue

组合成一串鞭炮

  1. 编写容器和线 这应该很好理解,所有的鞭炮排放位置是相对于线的位置排放的,所以线设置属性position: relative
  <div class="bianpao">
    <div class="main">
    </div>
  </div>
  

对应css:

.bianpao {
  position: relative;
  width: 400px;
  height: 380px;
  margin: 0 auto;
  transform-origin: top;
  padding: 20px;
}
.main {
  width: 2px;
  height: 52.6%;
  background: black;
  margin: 40px auto;
  position: relative;
  box-shadow: 0 0 1px black;
}
  1. 引入鞭炮组件
<template>
  <div class="bianpao">
    <div class="main">
      <One v-for="item in 40" :key="item" />
      <One class="last-one" />
    </div>
  </div>
</template>

<script>
import One from './one.vue'
export default {
  components: {
    One
  }
}
</script>

  1. 排放鞭炮 单数排一边,双数排一边,类似这样:

image.png

  1. 旋转鞭炮 从图片上可以看出,鞭炮的旋转角度0-90度的样子,我们也让鞭炮随机旋转一个角度即可。单数旋转正角度,双数旋转负角度:

image.png

第3,4步用到了scss的遍历和随机函数,相关scss代码如下:

  .container:nth-child(2n) {
    left: -28px;
  }
  .container:nth-child(2n + 1) {
    right: -28px;
  }

  .last-one {
    transform: scale(0.4) rotate(10deg);
    bottom: -120px;
  }

  @for $i from 1 through 20 {
    $angle: #{random(70) + 20}deg;
    @if ($i % 2 == 0) {
      $angle: -#{random(70) + 20}deg;
    }

    .container:nth-child(#{$i}) {
      top: 14px + $i * 8px;
      transform: scale(0.4) rotate($angle);
    }
  }

  @for $i from 20 through 40 {
    $angle: #{random(70) + 7}deg;
    @if ($i % 2 == 0) {
      $angle: -#{random(70) + 7}deg;
    }

    .container:nth-child(#{$i}) {
      top: 14px + ($i - 20) * 8px;
      transform: scale(0.4) rotate($angle);
    }
  }

可以注意到我把40个鞭炮分成了两组去写,因为我发现这样做随机出来的效果更好看一点。

  1. 最后一个鞭炮 由于每次改动代码,随机函数生成的值都不一样,每次生成的鞭炮旋转角度也不一样,形状也就不一样,我们有可能会出现这样的效果:

image.png

下面分开太大不太好看,于是加上最后一个鞭炮来协调一下,位置看着调就行了

image.png

实现光斑

光斑是燃放鞭炮的时候产生的一个效果,单个光斑是用渐变背景和box-shadow来实现的。

$yellow: #fdf410;

background: radial-gradient(rgba($yellow, 0.6), rgba($yellow, 0.1));
box-shadow: 0 0 20px rgba($yellow, 0.6);

光斑的有以下特点:大小随机,位置随机。又可以用到上面提到过的random()函数了,完整代码如下:

  <div class="spot-wrap">
    <div v-for="item in 50" :key="item" class="spot" />
  </div>
$yellow: #fdf410;
.spot-wrap {
  position: absolute;
  bottom: -160px;
  left: 50%;
  transform: translateX(-50%);
  width: 200px;
  height: 200px;
  .spot {
    position: absolute;
    background: radial-gradient(rgba($yellow, 0.6), rgba($yellow, 0.1));
    box-shadow: 0 0 20px rgba($yellow, 0.6);
    border-radius: 50%;
    animation: twikle 1.2s infinite alternate;
  }

  @for $i from 1 through 50 {
    .spot:nth-child(#{$i}) {
      width: #{random($limit: 20) + 20}px;
      height: #{random($limit: 20) + 16}px;
      top: #{random($limit: 150) + 10}px;
      left: #{random($limit: 160) + 10}px;
      animation-delay: -#{random($limit: 4)}s;
    }
  }

  @keyframes twikle {
    0% {
      opacity: 1;
    }
    100% {
      opacity: 0.2;
    }
  }
}

给每个光斑加了闪烁的动画,看起来更生动一点。

效果是这样的

20220113_150755.gif

实现爆炸后的碎片

和光斑类似,但不一样的地方在于,每个碎片除了大小位置不一样外,颜色和形状也不一样。大小随机和位置随机前面我们已经说过怎么实现了,下面主要将其他不一样的地方

  1. 不同颜色的碎片 颜色我们用rgba函数来取随机数,但是要限制每个值的范围,因为我们只想用红色系的颜色:
  background: rgba(random(100) + 155, random(100), random(100), 1);
  1. 不同形状 形状是用clip-path属性实现的,polygon()函数让我们可以实现各种形状。

我们给每个div分组,分为3n,4n,10n和其他,每组设置成不一样的形状,这样保证了碎片形状的随机性,相关scss代码如下:

@for $i from 1 through 50 {
    .suipian:nth-child(#{$i}) {
      width: #{random($limit: 6) + 8}px;
      height: #{random($limit: 6) + 8}px;
      top: #{random($limit: 120) + 6}px;
      left: #{random($limit: 120) + 6}px;
      /* animation-delay: -#{random($limit: 4)}s; */
      background: rgba(random(100) + 155, random(100), random(100), 1);

      @if ($i % 3 == 0) {
        clip-path: polygon(#{random($limit: 100)}% 0%, #{random($limit: 100)}% 10%, #{random($limit: 100)}% 80%, 0% 60%);
      } @else if($i % 4 == 0) {
        clip-path: polygon(#{random($limit: 100)}% 0%, #{random($limit: 100)}% 10%, #{random($limit: 100)}% 40%, 0% 20%);
      } @else if($i % 10 == 0) {
        clip-path: circle(24%);
      } @else {
        clip-path: polygon(#{random($limit: 40)}% 0%, #{random($limit: 100)}% 10%, #{random($limit: 100)}% 80%, #{random($limit: 80)}% 60%, 0% 20%);
      }
    }
  }
  1. 碎片的动画 碎片的动画是加在每个碎片上的,运动轨迹类似下图这样:

image.png

单数的先向左上运动再向下运动,双数的先向右上运动再向下运动,每个动画的延迟时间随机,移动的距离也是随机的。

 @for $i from 1 through 50 {
    .suipian:nth-child(#{$i}) {
      // ...
      animation: move#{$i} .8s infinite;
      animation-delay: -#{random($limit: 4)}s;

      $der: #{random($limit: 100)}px;
      @if ($i % 2 == 0) {
        $der: -#{random($limit: 100)}px;
      }
      @keyframes move#{$i} {
        30%{
          transform: translate($der,-#{random($limit: 100)}px);
          opacity: 0.8;
        }
        100%{
          transform: translate($der,#{random($limit: 200) + 10}px);
          opacity: 0;
        }
      }
    }
  }

效果是这样的

20220113_150602.gif

实现动画

至此静态的效果可以完成了:

微信截图_20220113151405.png

整体的动画包括两个方面,一个是鞭炮的掉落,一个是线的燃烧

  1. 鞭炮的掉落
  .container {
    // ...
    animation: drop 1s forwards;
  }
  
  @keyframes drop{
    80%,100%{
      transform: scale(0.3) rotate(0deg) translateY(1600px);
      opacity: 0;
    }
  }

设置每个鞭炮的动画延时不一样,注意下面的鞭炮应该先掉落,所以应该是这样的:

  @for $i from 1 through 20 {
      animation-delay: #{(20 - $i)*0.1}s;
  }
  
  
  @for $i from 20 through 40 {
      animation-delay: #{(40 - $i)*0.1}s;
  }
  1. 线的燃烧 只需要减小线的高度就行了,因为光斑和碎片都是相对线的bottom定位的,所以就自动往上移动。
.main {
  // ...
  animation: ranshao 2s forwards;

  @keyframes ranshao {
    84%{
      opacity: 1;
      height: 80px;
    }
    100%{
      opacity: 0;
      height: 80px;
    }
  }
}

以上完整代码:bianpao.vue

结语

第一版的效果就这样吧,从有这个想法开发到实现已经比较满意了~~

其他动画: css 实现伪3D魔方动态效果