I. Description
This document introduces how to achieve the effect of time fireworks. The final effect is as follows:
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:
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.
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.