反馈已提交

网络繁忙

当前为10.0版本文档,只有最新版本的文档支持在线编辑修改,如果想创建/编辑文档,请移步至 最新版帮助文档

JS实现时间烟花特效

  • 文档创建者:Kevin-s
  • 历史版本:5
  • 最近更新:RosieY 于 2021-02-02
  • 1. 概述

    1.1 预期效果

    有时候需要在决策报表集成一些特效,如下图,集成一个时间烟花效果。

    时间烟花.gif

    1.2 实现思路

    通过 HTIML 和 JS 可以实现时间获取以及添加粒子烟花效果。

    2. 示例

    2.1 准备模板

    新建决策报表,添加一个报表块,在报表单元格中输入 HTML 代码,单元格显示方式选择「用 HTML 显示内容」,如下图所示:

    HTML 代码如下:

    <canvas id="canvas" height="800" width="1200"></canvas>

    2.2 添加事件

    点击报表块,为报表块添加「初始化后」事件。如下图所示:

    JavaScript 代码如下:

    setTimeout(
    function(){
    let dt = 0.1;//每帧表示的时间间隔
    //球体
    class Ball{
        constructor(x,y,r,id){
            this.pos = new Vector2(x,y);//记录当前坐标
            this.id = id;
            this.color = '';
            this.r = r;
            this.velocity = null;
            this.life = true;
        }
        //更新粒子坐标
        update(){
            //重力加速度影响速度
            this.velocity = this.velocity.add(g.multiply(dt));
            //速度影响位移
            this.pos = this.pos.add(this.velocity.multiply(dt));
            //如果粒子已经出界则标记生命耗尽
            if(this.pos.y > 280){
                this.life = false;
            }
        }
        paint(){
            if(!this.life)return;
            context.beginPath();
            context.fillStyle = this.color;
            context.arc(this.pos.x ,this.pos.y, this.r, 0, Math.PI * 2 , false);
            context.fill();
        }
    }
    class Timer{
        constructor(){
            this.lastTime = Date.now();
            this.label = new Date(this.lastTime).Format('hh:mm:ss');
            this.step = 0;
            this.shouldAnim = 0;
        }
        update(){
           this.step = (this.step + 1) % 60;
           this.shouldAnim = (this.shouldAnim + 1) % 120;
           if (!this.step) {
              this.lastTime = Date.now();
              this.label = new Date(this.lastTime).Format('hh:mm:ss');
           }
        }
        paint(ctx){
            context.fillStyle = "#353535";
            ctx.fillText(this.label, 200, 100);
        }
    }
    //二维向量定义
    Vector2 = function(x, y) { this.x = x; this.y = y; };
    Vector2.prototype = {
        copy: function() { return new Vector2(this.x, this.y); },
        length: function() { return Math.sqrt(this.x * this.x + this.y * this.y); },
        sqrLength: function() { return this.x * this.x + this.y * this.y; },
        normalize: function() { var inv = 1 / this.length(); return new Vector2(this.x * inv, this.y * inv); },
        negate: function() { return new Vector2(-this.x, -this.y); },
        add: function(v) { return new Vector2(this.x + v.x, this.y + v.y); },
        subtract: function(v) { return new Vector2(this.x - v.x, this.y - v.y); },
        multiply: function(f) { return new Vector2(this.x * f, this.y * f); },
        divide: function(f) { var invf = 1 / f; return new Vector2(this.x * invf, this.y * invf); },
        dot: function(v) { return this.x * v.x + this.y * v.y; }
    };
        
       
         const canvas = document.getElementById('canvas');
    const context = canvas.getContext('2d');
    // 日期格式化工具
    Date.prototype.Format = function(fmt)   
    { //author: meizz   
      var o = {   
        "M+" : this.getMonth()+1,                 //月份   
        "d+" : this.getDate(),                    //日   
        "h+" : this.getHours(),                   //小时   
        "m+" : this.getMinutes(),                 //分   
        "s+" : this.getSeconds(),                 //秒   
        "q+" : Math.floor((this.getMonth()+3)/3), //季度   
        "S"  : this.getMilliseconds()             //毫秒   
      };   
      if(/(y+)/.test(fmt))   
        fmt=fmt.replace(RegExp.$1, (this.getFullYear()+"").substr(4 - RegExp.$1.length));   
      for(var k in o)   
        if(new RegExp("("+ k +")").test(fmt))   
      fmt = fmt.replace(RegExp.$1, (RegExp.$1.length==1) ? (o[k]) : (("00"+ o[k]).substr((""+ o[k]).length)));   
      return fmt;   
    }  
    var colorPalette = [
        "#2ec7c9",
        "#b6a2de",
        "#5ab1ef",
        "#ffb980",
        "#d87a80",
        "#8d98b3",
        "#e5cf0d",
        "#97b552",
        "#95706d",
        "#dc69aa",
        "#07a2a4",
        "#9a7fd1",
        "#588dd5",
        "#f5994e",
        "#c05050",
        "#59678c",
        "#c9ab00",
        "#7eb00a",
        "#6f5553",
        "#c14089"
    ];
    let balls = [];
    let timer = new Timer();
    let rightBorder = 800;
    let bottomBorder = 300;
    let gap = 5;
    let xs = 380; //文字所占区域宽度
    let ys = 120; //文字所占区域高度
    let imgData =[];//覆盖文字的矩形区域的像素点信息
    let checkPercent = 0.4;//检测区域中非白色像素点的比例小于限制时需要生成烟花
    let textLeft = 180; //文字距离画布左侧距离
    let textTop = 80;//文字距离画布上侧距离
    let g = new Vector2(0,9.8);//重力加速度
    start();
    function start() {
        init();
        requestAnimationFrame(startAnim);
    }
    //初始化设置
    function init(){
        // 设置字体
        context.font = "80px bold impact";
        // 设置颜色
        context.fillStyle = "black";
        // 设置水平对齐方式
        context.textAlign = "left";
        // 设置垂直对齐方式
        context.textBaseline = "top";
    }
    //绘制背景
    function paintBg() {
       context.strokeStyle = "#353535";
       context.lineWidth = 1;
       context.fillStyle = "white";
       context.fillRect(1,1,rightBorder - 1, bottomBorder - 1);
       context.strokeRect(0,0,rightBorder,bottomBorder);
    }
    //帧动画
    function startAnim() {
        //重绘背景
        paintBg();
        //更新计时器状态
        timer.update();
        timer.paint(context);
        //时间发生变化时,生成新的小球并推入数组
        handleTimerChange();
        //更新每个小球的状态
        balls = balls.map(ball=>{
          ball.update();
          ball.paint();//描线但不在画布上绘制
          return ball;
        }); 
        //绘制每个小球位置
    requestAnimationFrame(startAnim);
    }
    //时间变化时生成爆破
    function handleTimerChange() {
      let position;
      //如果时间变化,则生成新的爆破小球
      if (!timer.shouldAnim) {
          //过滤掉生命耗尽的粒子
          balls = balls.filter(ball=>ball.life);
          //生成新的文字烟花
          for(let y = 0; y < ys / gap; y++){
            for(let x= 0; x < xs / gap ; x++){
               position = shouldAddBall(x, y);
               if (position) {
                   addBall(position);
               }
            }
          }
      }
    }
    //检测指定区块是否需要生成烟花圆
    function shouldAddBall(x, y) {
       let pixels = context.getImageData(textLeft + x*gap, textTop + y*gap, 5, 5);
       if(!count(pixels)) return
       return {
          x:textLeft + x*gap,
          y:textTop + y*gap
       }
    }
    function count(pixels) {
       let data = pixels.data;
       let total = data.length;
       let num = data.filter(item=>item !== 255).length;
       return (num / total) > checkPercent;
    }
    //每隔一定时间增加一个新的球体
    function addBall(pos) {
       let {x,y} = pos;
       let r = Math.ceil(Math.random() * 4+1);
       let i = Math.ceil(Math.random() * 16);
       let left = pos.x < (textLeft + xs / 2) ? -1 : 1;
       let ball = new Ball(x,y,r,balls.length);
           ball.color = colorPalette[i];
           ball.velocity = new Vector2(left * 10 *Math.random(), -20 * Math.random());
           balls.push(ball);
    }
    },1000);

    2.3 效果预览

    保存模板,点击「分页预览」,效果如下图所示:

    2021-02-02_13-29-48.gif

    注:不支持移动端

    3. 模板下载

    点击可下载模板:时间烟花效果.frm

    附件列表


    主题: 决策报表应用
    已经是第一篇
    已经是最后一篇
    • 有帮助
    • 没帮助
    • 只是浏览

    售前咨询电话

    400-811-8890转1

    在线技术支持

    请前往「服务平台」,选择「在线支持」

    热线电话:400-811-8890转2

    总裁办24H投诉

    热线电话:173-1278-1526