JS Time Fireworks

  • Last update:  2020-12-17
  • I. Description

    This document introduces how to achieve the effect of time fireworks. The final effect is as follows:

    1606380567742602.gif

    II. Steps

    First create a new dashboard, add a report block, enter the following code in the cell of the report block, and then check Display by HTML, the effect is as follows:

    1606380588340360.png

    The HTML code entered is as follows

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

    Next, click on the report block and add the an After Initialization event.

    1606380621767786.png

    The JS code in the event is as follows:

    setTimeout(
    function(){
    let dt = 0.1;//The time interval represented by each frame
    //Ball
    class Ball{
        constructor(x,y,r,id){
            this.pos = new Vector2(x,y);//Record current coordinates
            this.id = id;
            this.color = '';
            this.r = r;
            this.velocity = null;
            this.life = true;
        }
        //Update particle coordinates
        update(){
            //Gravity acceleration affects speed
            this.velocity = this.velocity.add(g.multiply(dt));
            //Speed affects displacement
            this.pos = this.pos.add(this.velocity.multiply(dt));
            //If the particle is out of bounds, mark life out
            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);
        }
    }
    //Two-dimensional vector definition
    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 formatting tool
    Date.prototype.Format = function(fmt)   
    { //author: meizz   
      var o = {   
        "M+" : this.getMonth()+1,                 //Month
        "d+" : this.getDate(),                    //Date
        "h+" : this.getHours(),                   //Hour
        "m+" : this.getMinutes(),                 //Minute
        "s+" : this.getSeconds(),                 //Second
        "q+" : Math.floor((this.getMonth()+3)/3), //Quarter
        "S"  : this.getMilliseconds()             //Millisecond
      };   
      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; //The width of the text area
    let ys = 120; //The height of the area occupied by the text
    let imgData =[];//Pixel information of the rectangular area that covers the text
    let checkPercent = 0.4;//Detect the proportion of non-white pixels in the detection area, if less than the limit, fireworks will be generated
    let textLeft = 180; //Distance between text and left side of canvas
    let textTop = 80;//The distance between the text and the upper side of the canvas
    let g = new Vector2(0,9.8);//Acceleration of gravity
    start();
    function start() {
        init();
        requestAnimationFrame(startAnim);
    }
    //Initial setting
    function init(){
        // Set font
        context.font = "80px bold impact";
        // Set color
        context.fillStyle = "black";
        // Set horizontal alignment
        context.textAlign = "left";
        // Set vertical alignment
        context.textBaseline = "top";
    }
    //Draw background
    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);
    }
    //Frame animation
    function startAnim() {
        //Redraw the background
        paintBg();
        //Update timer status
        timer.update();
        timer.paint(context);
        //When the time changes, a new ball is generated and pushed into the array
        handleTimerChange();
        //Update the status of each ball
        balls = balls.map(ball=>{
          ball.update();
          ball.paint();//Draw lines but not draw on canvas
          return ball;
        }); 
        //Plot the position of each ball
    requestAnimationFrame(startAnim);
    }
    //Blasting when time changes
    function handleTimerChange() {
      let position;
      //If time changes, a new blasting ball is generated
      if (!timer.shouldAnim) {
          //Filter out life-depleted particles
          balls = balls.filter(ball=>ball.life);
          //Generate new text fireworks
          for(let y = 0; y < ys / gap; y++){
            for(let x= 0; x < xs / gap ; x++){
               position = shouldAddBall(x, y);
               if (position) {
                   addBall(position);
               }
            }
          }
      }
    }
    //Check whether the specified block needs to generate fireworks
    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;
    }
    //Add a new ball at regular intervals
    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);

     

    III. View the effect

    3.1 PC effect

    The effect is as shown at the beginning.


    3.2 Mobile effect

    Note: Mobile terminal is not supported.

    IV. Completed template

    Attachment List


    Theme: Secondary Development
    • Helpful
    • Not helpful
    • Only read

    滑鼠選中內容,快速回饋問題

    滑鼠選中存在疑惑的內容,即可快速回饋問題,我們將會跟進處理。

    不再提示

    10s後關閉

    Get
    Help
    Online Support
    Professional technical support is provided to quickly help you solve problems.
    Online support is available from 9:00-12:00 and 13:30-17:30 on weekdays.
    Page Feedback
    You can provide suggestions and feedback for the current web page.
    Pre-Sales Consultation
    Business Consultation
    Business: international@fanruan.com
    Support: support@fanruan.com
    Page Feedback
    *Problem Type
    Cannot be empty
    Problem Description
    0/1000
    Cannot be empty

    Submitted successfully

    Network busy