原生JS实现DOM粒子爆炸效果

iiichigo 2019-06-28

爆炸动效分享

前言

此次分享是一次自我组件开发的总结,还是有很多不足之处,望各位大大多提宝贵意见,互相学习交流。

分享内容介绍

通过原生js代码,实现粒子爆炸效果组件
组件开发过程中,使用到了公司内部十分高效的工程化环境,特此打个广告: 新浪移动诚招各种技术大大!可以私聊投简历哦!

效果预览

原生JS实现DOM粒子爆炸效果

效果分析

  • 点击作为动画开始的起点,自动结束
  • 每次效果产生多个抛物线粒子运动的元素,方向随机,展示内容不一样,有空间上Z轴的大小变化
  • 需求上可以无间隔点击,即第一组动画未结束可播放第二组动画
  • 动画基本执行时长一致
由以上四点分析后,动画实现有哪些实现方案呢?
  • css操作态变换(如focus)使子元素执行动画

不可取,效果可多次连点,css状态变换与需求不符

  • Js 控制动画开始,事先写好css动画预置,通过class 包含选择器切换动画 例如: .active .items{animation:xxx ...;}

    不可取,单次执行动画没有问题,但是存在效果的固定,以及无法连续执行动画

  • 事先写好大量动画,隐藏大量dom元素,动画开始随机选取dom元素执行自己唯一的动画keyframes

    实现层面来说,行得通,但是评论列表长的时候,dom数量巨大,且css大量动画造成代码量沉重、无随机性

  • 抛弃css动画,使用canvas 绘制动画

    可行,但是canvas维护成本略高,且自定义功能难设计,屏幕适配也有一定成本

  • js做dom创建,生成随机css @keyframes

    可行,但是创建style样式表,引发css重新渲染页面,会导致页面的性能下降,且抛物线css的复杂度不低,暂不作为首选

  • js 刷帧 做dom渲染

    可行,但是刷帧操作会造成性能压力

结论

canvas虽说可行,但由于其开发弊端 本次分享不以canvas为分享内容,而是使用最后一种 js刷帧的dom操作

组件结构

由截图分享,动画可以分为两个模块,首先,随机发散的粒子具有共性:抛物线动画,淡出,渲染表情

而例子数量变多之后则为截图中的效果

但是,由于性能原因,我们需要做到粒子的掌控,实现资源再利用,那么还需要第二个模块,作为粒子的管控组件

所以: 此功能可使用两个模块进行开发: partical.js 粒子功能 与 boom.js 粒子管理

实现 Partical.js

  1. 前置资源:抛物线运动的物理曲线需要使用Tween.js提供的速度函数

    若不想引入Tween.js 可以使用以下代码

    * Tween.js
          * t: current time(当前时间);
          * b: beginning value(初始值);
          * c: change in value(变化量);
          * d: duration(持续时间)。
          * you can visit 'http://easings.net/zh-cn' to get effect
*
    
        const Quad = {
            easeIn: function(t, b, c, d) {
                return c * (t /= d) * t + b;
            },
            easeOut: function(t, b, c, d) {
                return -c *(t /= d)*(t-2) + b;  
            },
            easeInOut: function(t, b, c, d) {
                if ((t /= d / 2) < 1) return c / 2 * t * t + b;
                return -c / 2 * ((--t) * (t-2) - 1) + b;
            }
        }
        const Linear = function(t, b, c, d) { 
            return c * t / d + b; 
        }
  1. 粒子实现
    实现思路:
    希望在粒子管控组件时,使用new partical的方式创建粒子,每个粒子存在自己的动画开始方法,动画结束回调。
    由于评论列表可能存在数量巨大的情况,我们希望只全局创建有限个数的粒子,那么则提供呢容器移除粒子功能以及容器添加粒子的功能,实现粒子的复用

    partical_style.css

    //粒子充满粒子容器,需要容器存在尺寸以及relative定位
         .Boom-Partical_Holder{
             position: absolute;
             left:0;
             right:0;
             top:0;
             bottom:0;
             margin:auto;
         }

    particle.js

    import "partical_style.css";
     
     class Partical{
         // dom为装载动画元素的容器 用于设置位置等样式
         dom = null;
         // 动画开始时间
         StartTime = -1;
         // 当前粒子的动画方向,区别上抛运动与下抛运动
         direction = "UP";
         // 动画延迟
         delay = 0;
         // 三方向位移值
         targetZ = 0;
         targetY = 0;
         targetX = 0;
         // 缩放倍率
         scaleNum = 1;
         // 是否正在执行动画
         animating = false;
         // 粒子的父容器,标识此粒子被渲染到那个元素内
         parent = null;
         // 动画结束的回调函数列表
         animEndCBList = [];
         // 粒子渲染的内容容器 slot
         con = null;
         
         constructor(){
             //创建动画粒子dom
             this.dom = document.createElement("div");
             this.dom.classList.add("Boom-Partical_Holder");
             this.dom.innerHTML = `
                 <div class="Boom-Partical_con">
                     Boom
                 </div>
             `;
         }
         
         // 在哪里渲染
         renderIn(parent) {
             // dom判断此处省略
             parent.appendChild(this.dom);
             this.parent = parent;
             // 此处为初始化 slot 容器
             !this.con && ( this.con = this.dom.querySelector(".Boom-Partical_con"));
         }
         
         // 用于父容器移除当前粒子
         deleteEl(){
             // dom判断此处省略
             this.parent.removeChild(this.dom);
         }
         
         // 执行动画,需要此粒子执行动画的角度,动画的力度,以及延迟时间
         animate({ deg, pow, delay } = {}){
             // 后续补全
         }
         
         // 动画结束回调存储
         onAnimationEnd(cb) {
             if (typeof cb !== 'function') return;
             this.animEndCBList.push(cb);
         }
         
         // 动画结束回调执行
         emitEndCB() {
             this.dom.style.cssText += `;-webkit-transform:translate3d(0,0,0);opacity:1;`;
             this.animating = false;
             try {
                 for (let cb  of this.animEndCBList) {
                     cb();
                 }
             } catch (error) {
                 console.warn("回调报错:",cb);
             }
         }
         
         // 简易实现slot功能,向粒子容器内添加元素
         insertChild(child){
             this.con.innerHTML = '';
             this.con.appendChild(child);
         }
     }

致此,我们先创建了一个粒子对象的构造函数,现在考虑一下我们实现了我们的设计思路吗?

  • 使用构造函数new Partical( )粒子
  • 粒子实力对象存在 animate 执行动画方法
  • 有动画结束回调函数的存储和执行
  • 设置粒子的父元素: renderIn 方法
  • 父元素删除粒子: deleteEl 方法

为了更好的展示粒子内容,我们特意在constructor里创建了一个 Boom-Partical_con 元素用于模拟slot功能: insertChild方法,用于使用者展示不同的内容进行爆炸

相关推荐