preface

Since I have met canvas repeatedly in the interview process, and I only know and learn the theoretical knowledge of Canvas, I will make a whole demo!

The corresponding files have been organized and uploaded to their own warehouse.

Gitee warehouse

Making the warehouse

followImooc seriesStudy summary, if there is not clear place, also suggest with me from zero to watch learning oh

Canvas based

Create a Canvas

HTML tags

	<canvas id="canvas" width="1024" height="768" style="border: 1px solid #aaa; display: block; margin: auto auto">
		<! -- Compatible error reduction -->The current browser does not support Canvas. Please change your browser and try again</canvas>
Copy the code

Block-level elements, by default, are 350px wide and 150px high

W3C specifies that width and height should be used to specify the size (canvas size and pixel size)

Javascript

const canvas = document.getElementById('canvas')
// Draw context 2D drawing
const context = canvas.getContext('2d')
Copy the code

Canvas to draw

Draw a straight line

// State Settings - with top left corner as vertex (0, 0)
context.moveTo(100.100)	// The nib moves
context.lineTo(700.700)	// Move the brush to this point

// Common attributes
/ / line
context.lineWidth = 5	/ / line
context.strokeStyle = '# 005588'	/ / thread color

context.stroke()	// Start drawing
Copy the code

Draw a polygon

/ / triangle
context.moveTo(100.100)	// The nib moves
context.lineTo(700.700)	// Move the brush to this point
context.lineTo(100.700)	// Move the brush to this point
context.lineTo(100.100)	// Move the brush to this point

// Common attributes
/ / line
context.lineWidth = 5	/ / line
context.strokeStyle = '# 005588'	/ / thread color
/ / fill
context.fillStyle = 'rgb(2, 100, 30)'	// Fill the color
context.fill()	/ / fill

context.stroke()	// Start drawing
Copy the code

Draw a tangram

// Tangram line data
const tanGram = [
  {p: [{x: 0.y: 0}, {x: 800.y: 0}, {x: 400.y: 400}].color: '#caff67'},
  {p: [{x: 0.y: 0}, {x: 400.y: 400}, {x: 0.y: 800}].color: '#67beef'},
  {p: [{x: 800.y: 0}, {x: 800.y: 400}, {x: 600.y: 600}, {x: 600.y:200}].color: '#ef3d61'},
  {p: [{x: 600.y: 200}s, {x: 600.y: 600}, {x: 400.y: 400}].color: '#f9f51a'},
  {p: [{x: 400.y: 400}, {x: 600.y: 600}, {x: 400.y: 800}, {x: 200.y: 600}].color: '#a594c0'},
  {p: [{x: 200.y: 600}, {x: 400.y: 800}, {x: 0.y: 800}].color: '#fa8ecc'},
  {p: [{x: 800.y: 400}, {x: 800.y: 800}, {x: 400.y: 800}].color: '#f6ca29'},]window.onload = function() {
  / / jigsaw puzzle
  const canvas = document.getElementById('canvas')
  // Draw context 2D drawing
  const context = canvas.getContext('2d')
  const len = tanGram.length
  // Draw each polygon
  for (let i = 0; i < len; i++) {
    draw(tanGram[i], context)
  }
}

function draw(piece, cxt) {
  // Start drawing
  cxt.beginPath();
  cxt.moveTo(piece.p[0].x, piece.p[0].y)
  const len = piece.p.length
  for (let i = 1; i < len; i++) {
    cxt.lineTo(piece.p[i].x, piece.p[i].y)
  }
  // Finish drawing
  cxt.closePath()
  cxt.fillStyle = piece.color
  cxt.fill()
}
Copy the code

Draw arcs and circles

The method attributes

constext.arc(
	centerx, centery, radius,	// The center of the circle origin coordinates and the radius values
  startingAngle, endingAngle,	// The range of radians
  anticlockwise = false	// Whether to turn clockwise (optional) - Default false to turn clockwise
)
Copy the code

arc

context.lineWidth = 5
context.strokeStyle = '# 005588'
context.arc(300.300.200.0.1.5 + Math.PI)
context.stroke()
Copy the code

Continuous arc

context.lineWidth = 5
context.strokeStyle = '# 005588'

// Discontinuous arc
for (let i = 0; i < 10; i++) {
  context.beginPath()
  context.arc(50 + i*100.60.40.0.2*Math.PI*(i+1) /10)
  context.closePath()
  context.stroke()
}

/ / continuous arc
for (let i = 0; i < 10; i++) {
  context.beginPath()
  context.arc(50 + i*100.180.40.0.2*Math.PI*(i+1) /10)
  context.stroke()
}
Copy the code

round

context.lineWidth = 5
context.strokeStyle = '# 005588'

for (let i = 0; i < 10; i++) {
  context.beginPath()
  context.arc(50 + i*100.60.40.0.2*Math.PI*(i+1) /10)
  context.closePath()
	context.fill()	/ / fill
}

for (let i = 0; i < 10; i++) {
  context.beginPath()
  context.arc(50 + i*100.180.40.0.2*Math.PI*(i+1) /10)
	context.fill()	/ / fill
}
Copy the code

Realized electronic clock

Drawing electronic clock

The directory structure

Digin.js // A two-dimensional lattice that stores numbers

Countdown.js // Concrete logic

Index.html // The specific page

2d lattice – digin.js

Because there’s a lot of data, let’s take a little example

// indicates that 0,1 indicates that the circle needs to be drawn[[0.0.1.1.1.0.0],
  [0.1.1.0.1.1.0],
  [1.1.0.0.0.1.1],
  [1.1.0.0.0.1.1],
  [1.1.0.0.0.1.1],
  [1.1.0.0.0.1.1],
  [1.1.0.0.0.1.1],
  [1.1.0.0.0.1.1],
  [0.1.1.0.1.1.0],
  [0.0.1.1.1.0.0]]Copy the code

Concrete logic – Countdown.js

Initialize the

/ / constant
const WINDOW_WIDTH = 1024;
const WINDOW_HEIGHT = 768;
const RADIUS = 8;
const MARGIN_TOP = 60;
const MARGIN_LEFT = 30;

window.onload = function(){
		// Initialize the canvas
    const canvas = document.getElementById('canvas');
    const context = canvas.getContext("2d");

    canvas.width = WINDOW_WIDTH;
    canvas.height = WINDOW_HEIGHT;

    // Draw method
    render( context )
}
Copy the code

Draw the dot

// Draw the dots
function renderDigit( x , y , num , cxt ){
    cxt.fillStyle = "RGB (0102153)";

    for(let i = 0 ; i < digit[num].length ; i ++ )
        for(let j = 0 ; j < digit[num][i].length ; j ++ )
            if( digit[num][i][j] == 1 ){
                cxt.beginPath();
              	// The center of the (I, j) circle:
              	// j*2*(R+1) => JTH column box
              	// x: x+j*2*(R+1)+(R+1)
               	// y: y+i*2*(R+1)+(R+1)
                cxt.arc( x+j*2*(RADIUS+1)+(RADIUS+1) , y+i*2*(RADIUS+1)+(RADIUS+1) , RADIUS , 0 , 2*Math.PI )
                cxt.closePath()

                cxt.fill()
            }
}

Copy the code

Draw a number dot

// Draw each dot of the number
function render( cxt ){
    let hours = 12
		let minutes = 34
		let seconds = 56

    / / 1
    renderDigit( MARGIN_LEFT , MARGIN_TOP , parseInt(hours/10) , cxt )
  	/ / 2
    renderDigit( MARGIN_LEFT + 15*(RADIUS+1) , MARGIN_TOP , parseInt(hours%10) , cxt )
  	/ / :
    renderDigit( MARGIN_LEFT + 30*(RADIUS + 1) , MARGIN_TOP , 10 , cxt )
  	/ / 3
    renderDigit( MARGIN_LEFT + 39*(RADIUS+1) , MARGIN_TOP , parseInt(minutes/10) , cxt);
  	/ / 4
    renderDigit( MARGIN_LEFT + 54*(RADIUS+1) , MARGIN_TOP , parseInt(minutes%10) , cxt);
  	/ / :
    renderDigit( MARGIN_LEFT + 69*(RADIUS+1) , MARGIN_TOP , 10 , cxt);
  	/ / 5
    renderDigit( MARGIN_LEFT + 78*(RADIUS+1) , MARGIN_TOP , parseInt(seconds/10) , cxt);
  	/ / 6
    renderDigit( MARGIN_LEFT + 93*(RADIUS+1) , MARGIN_TOP , parseInt(seconds%10) , cxt);
}
Copy the code

Time to calculate

Initialize the

// Remember to dynamically change your deadlines
const endTime = new Date(2021.3.7.0.0.0);
let curShowTimeSeconds = 0
Copy the code

draw

window.onload = function(){
		// Initialize the canvas
    const canvas = document.getElementById('canvas');
    const context = canvas.getContext("2d");

    canvas.width = WINDOW_WIDTH;
    canvas.height = WINDOW_HEIGHT;
		curShowTimeSeconds = getCurrentShowTimeSeconds()
		render( context )
}
Copy the code

Get time number

// Get the current time and expiration date
function getCurrentShowTimeSeconds() {
	var curTime = new Date(a);var ret = endTime.getTime() - curTime.getTime();
	ret = Math.round( ret/1000 )

	return ret >= 0 ? ret : 0;
}
Copy the code

Draw a number dot

/ / change the value
let hours = parseInt( curShowTimeSeconds / 3600);
let minutes = parseInt( (curShowTimeSeconds - hours * 3600) /60 )
let seconds = curShowTimeSeconds % 60
Copy the code

Refresh to get the current dynamic time number

Thus the electronic clock was realized

Animation effect

Clock digital animation

The directory structure

Digin.js // A two-dimensional lattice that stores numbers

Countdown.js // Concrete logic

Index.html // The specific page

Timing rendering

Use setInterval() to render the animation

// countdown.js

window.onload = function(){
		// Initialize the canvas
    const canvas = document.getElementById('canvas');
    const context = canvas.getContext("2d");

    canvas.width = WINDOW_WIDTH;
    canvas.height = WINDOW_HEIGHT;
		curShowTimeSeconds = getCurrentShowTimeSeconds()
  	// Time render
		setInterval(() = > {
      render( context );
      update()	// Update the function
    }, 50)}Copy the code

Update the clock

function update() {
  // Next second clock
  const nextShowTimeSeconds = getCurrentShowTimeSeconds()
  let nextHours = parseInt( nextShowTimeSeconds / 3600);
	let nextMinutes = parseInt( (nextShowTimeSeconds - nextHours * 3600) /60 )
	let nextSeconds = nextShowTimeSeconds % 60
  
  // The clock of the current second
  let curHours = parseInt( curShowTimeSeconds / 3600);
	let curMinutes = parseInt( (curShowTimeSeconds - curHours* 3600) /60 )
	let curSeconds = curShowTimeSeconds % 60
  
  // Update seconds
  if(nextShowTimeSeconds ! = curShowTimeSeconds) { curShowTimeSeconds = nextShowTimeSeconds } }Copy the code

Update the canvas

function render( cxt ){

  // Refresh the entire screen rectangle
	cxt.clearRect(0.0, WINDOW_WIDTH, WINDOW_HEIGHT)

	let hours = parseInt( curShowTimeSeconds / 3600);
	let minutes = parseInt( (curShowTimeSeconds - hours * 3600) /60 )
	let seconds = curShowTimeSeconds % 60

	renderDigit( MARGIN_LEFT , MARGIN_TOP , parseInt(hours/10) , cxt )
	renderDigit( MARGIN_LEFT + 15*(RADIUS+1) , MARGIN_TOP , parseInt(hours%10) , cxt )
	renderDigit( MARGIN_LEFT + 30*(RADIUS + 1) , MARGIN_TOP , 10 , cxt )
	renderDigit( MARGIN_LEFT + 39*(RADIUS+1) , MARGIN_TOP , parseInt(minutes/10) , cxt);
	renderDigit( MARGIN_LEFT + 54*(RADIUS+1) , MARGIN_TOP , parseInt(minutes%10) , cxt);
	renderDigit( MARGIN_LEFT + 69*(RADIUS+1) , MARGIN_TOP , 10 , cxt);
	renderDigit( MARGIN_LEFT + 78*(RADIUS+1) , MARGIN_TOP , parseInt(seconds/10) , cxt);
	renderDigit( MARGIN_LEFT + 93*(RADIUS+1) , MARGIN_TOP , parseInt(seconds%10) , cxt);
}
Copy the code

Small ball animation

<! DOCTYPEhtml>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>Title</title>
</head>
<body>
<canvas id="canvas" width="1024" height="768" style="border: 1px solid #aaa; display: block; margin: auto auto">
	<! -- Compatible error reduction -->The current browser does not support Canvas. Please change your browser and try again</canvas>

<script>
// Initialize the ball
const ball = { x: 512.y: 100.r: 20.g: 2.vx: -4.vy: 0.color: '# 005588' }
window.onload = function() {
	const canvas = document.getElementById('canvas')
	// Draw context 2D drawing
	const context = canvas.getContext('2d')

	// Timer animation
	setInterval(() = > {
		/ / rendering
		render(context)
		/ / update
		update()
	}, 50)}/ / animation
function update() {
	ball.x += ball.vx
	ball.y += ball.vy
	ball.vy += ball.g

	// Hit the bottom
	if (ball.y >= 768 - ball.r) {
		ball.y = 768 - ball.r
		ball.vy = -ball.vy*0.6	/ / the friction}}function render(ctx) {
	// Refresh the canvas
	ctx.clearRect(0.0, ctx.canvas.width, ctx.canvas.height)
	ctx.fillStyle = ball.color
	ctx.beginPath()
	ctx.arc(ball.x, ball.y, ball.r, 0.2*Math.PI)
	ctx.closePath()
	ctx.fill()
}
</script>
</body>
</html>

Copy the code

Clock ball animation

The directory structure

Digin.js // A two-dimensional lattice that stores numbers

Countdown.js // Concrete logic

Index.html // The specific page

Initialize data

/ / constant
const WINDOW_WIDTH = 1024;
const WINDOW_HEIGHT = 768;
const RADIUS = 8;
const MARGIN_TOP = 60;
const MARGIN_LEFT = 30;

const endTime = new Date(2021.3.7.0.0.0);
let curShowTimeSeconds = 0

// Store the ball
const balls = [];
// Change the color
const colors = ["#33B5E5"."#0099CC"."#AA66CC"."#9933CC"."#99CC00"."# 669900"."#FFBB33"."#FF8800"."#FF4444"."#CC0000"]
Copy the code

Update the ball

function update() {
	// Next second clock
	const nextShowTimeSeconds = getCurrentShowTimeSeconds()
	let nextHours = parseInt(nextShowTimeSeconds / 3600);
	let nextMinutes = parseInt((nextShowTimeSeconds - nextHours * 3600) / 60)
	let nextSeconds = nextShowTimeSeconds % 60

	// The clock of the current second
	let curHours = parseInt(curShowTimeSeconds / 3600);
	let curMinutes = parseInt((curShowTimeSeconds - curHours * 3600) / 60)
	let curSeconds = curShowTimeSeconds % 60

	// Update seconds
	if(nextShowTimeSeconds ! = curShowTimeSeconds) {// Update all 6 numbers together
		if (parseInt(curHours / 10) != parseInt(nextHours / 10)) {
      // Add the ball
			addBalls(MARGIN_LEFT + 0, MARGIN_TOP, parseInt(curHours / 10));
		}
		if (parseInt(curHours % 10) != parseInt(nextHours % 10)) {
			addBalls(MARGIN_LEFT + 15 * (RADIUS + 1), MARGIN_TOP, parseInt(curHours / 10));
		}
		if (parseInt(curMinutes / 10) != parseInt(nextMinutes / 10)) {
			addBalls(MARGIN_LEFT + 39 * (RADIUS + 1), MARGIN_TOP, parseInt(curMinutes / 10));
		}
		if (parseInt(curMinutes % 10) != parseInt(nextMinutes % 10)) {
			addBalls(MARGIN_LEFT + 54 * (RADIUS + 1), MARGIN_TOP, parseInt(curMinutes % 10));
		}
		if (parseInt(curSeconds / 10) != parseInt(nextSeconds / 10)) {
			addBalls(MARGIN_LEFT + 78 * (RADIUS + 1), MARGIN_TOP, parseInt(curSeconds / 10));
		}
		if (parseInt(curSeconds % 10) != parseInt(nextSeconds % 10)) {
			addBalls(MARGIN_LEFT + 93 * (RADIUS + 1), MARGIN_TOP, parseInt(nextSeconds % 10));
		}

		curShowTimeSeconds = nextShowTimeSeconds;
	}
	/ / update the ball
	updateBalls();
}
Copy the code

Add a small ball

// Add the ball
function addBalls(x, y, num) {
	for (let i = 0; i < digit[num].length; i++)
		for (let j = 0; j < digit[num][i].length; j++)
			if (digit[num][i][j] == 1) {
				const aBall = {
          / / the origin
					x: x + j * 2 * (RADIUS + 1) + (RADIUS + 1),
					y: y + i * 2 * (RADIUS + 1) + (RADIUS + 1),
          // Animation random design
					g: 1.5 + Math.random(),
					vx: Math.pow(-1.Math.ceil(Math.random() * 1000)) * 4.vy: -5.color: colors[Math.floor(Math.random() * colors.length)]
				}

				balls.push(aBall)
			}
}
Copy the code

Update ball movement

// Update the ball movement
function updateBalls() {
	for (let i = 0; i < balls.length; i++) {
		balls[i].x += balls[i].vx;
		balls[i].y += balls[i].vy;
		balls[i].vy += balls[i].g;

    // Collision detection
		if (balls[i].y >= WINDOW_HEIGHT - RADIUS) {
			balls[i].y = WINDOW_HEIGHT - RADIUS;
			balls[i].vy = -balls[i].vy * 0.75; }}}Copy the code

Draw the ball

// Draw each dot of the number
function render(cxt) {

	// Refresh the entire screen rectangle
	cxt.clearRect(0.0, WINDOW_WIDTH, WINDOW_HEIGHT)

	let hours = parseInt(curShowTimeSeconds / 3600);
	let minutes = parseInt((curShowTimeSeconds - hours * 3600) / 60)
	let seconds = curShowTimeSeconds % 60

	renderDigit(MARGIN_LEFT, MARGIN_TOP, parseInt(hours / 10), cxt)
	renderDigit(MARGIN_LEFT + 15 * (RADIUS + 1), MARGIN_TOP, parseInt(hours % 10), cxt)
	renderDigit(MARGIN_LEFT + 30 * (RADIUS + 1), MARGIN_TOP, 10, cxt)
	renderDigit(MARGIN_LEFT + 39 * (RADIUS + 1), MARGIN_TOP, parseInt(minutes / 10), cxt);
	renderDigit(MARGIN_LEFT + 54 * (RADIUS + 1), MARGIN_TOP, parseInt(minutes % 10), cxt);
	renderDigit(MARGIN_LEFT + 69 * (RADIUS + 1), MARGIN_TOP, 10, cxt);
	renderDigit(MARGIN_LEFT + 78 * (RADIUS + 1), MARGIN_TOP, parseInt(seconds / 10), cxt);
	renderDigit(MARGIN_LEFT + 93 * (RADIUS + 1), MARGIN_TOP, parseInt(seconds % 10), cxt);

  // Draw the ball
	for (let i = 0; i < balls.length; i++) {
		cxt.fillStyle = balls[i].color;

		cxt.beginPath();
		cxt.arc(balls[i].x, balls[i].y, RADIUS, 0.2 * Math.PI, true); cxt.closePath(); cxt.fill(); }}Copy the code

Optimize and extend the clock

Performance optimization

The directory structure

Digin.js // A two-dimensional lattice that stores numbers

Countdown.js // Concrete logic

Index.html // The specific page

implementation

Problem: The array of Balls keeps growing until it reaches a limit.

Fix: When the ball moves off the canvas, it should be deleted.

The ball array should only exist as the canvas appears with the ball (which will get slower and slower and card)

// Update the ball movement
function updateBalls() {
	for (let i = 0; i < balls.length; i++) {
		balls[i].x += balls[i].vx;
		balls[i].y += balls[i].vy;
		balls[i].vy += balls[i].g;

		if (balls[i].y >= WINDOW_HEIGHT - RADIUS) {
			balls[i].y = WINDOW_HEIGHT - RADIUS;
			balls[i].vy = -balls[i].vy * 0.75; }}// Initialize the number to 0 => determine how many balls remain in the canvas
	let cnt = 0
	for( let i = 0 ; i < balls.length ; i ++ )
    // Whether it is in the canvas
		if( balls[i].x + RADIUS > 0 && balls[i].x -RADIUS < WINDOW_WIDTH )
      // If yes, add the ball
			balls[cnt++] = balls[i]
	// Keep only the ball with the canvas
	while( balls.length > cnt ){ balls.pop(); }}Copy the code

Screen adaptation

Let the initialized constant adapt to the current screen.

Prop up the width and height of the DOM element for fetching

<! DOCTYPEhtml>
<html style="height: 100%;">
<head lang="en">
    <meta charset="UTF-8">
    <title></title>
</head>
<body style="height: 100%;">
    <canvas id="canvas" style="height: 100%;">The current browser does not support Canvas. Please change your browser and try again</canvas>

    <script src="digit.js"></script>
    <script src="countdown.js"></script>
</body>
</html>

Copy the code

Dynamically define canvas initialization variables

// countdown.js

/ / constant
let WINDOW_WIDTH = 1024;
let WINDOW_HEIGHT = 768;
let RADIUS = 8;
let MARGIN_TOP = 60;
let MARGIN_LEFT = 30;

const endTime = new Date(2021.3.8.0.0.0);
let curShowTimeSeconds = 0

// Store the ball
const balls = [];
// Change the color
const colors = ["#33B5E5"."#0099CC"."#AA66CC"."#9933CC"."#99CC00"."# 669900"."#FFBB33"."#FF8800"."#FF4444"."#CC0000"]

window.onload = function () {
  // Define constants based on the user screen
  WINDOW_WIDTH = document.body.clientWidth;
  WINDOW_HEIGHT = document.body.clientHeight;
  MARGIN_LEFT = Math.round(WINDOW_WIDTH/10);
  // Select * from renderDigit
  RADIUS = Math.round(WINDOW_WIDTH * 4 / 5 / 108) -1
  MARGIN_TOP = Math.round(WINDOW_HEIGHT / 5)
  
	// Initialize the canvas
	const canvas = document.getElementById('canvas');
	const context = canvas.getContext("2d");

	canvas.width = WINDOW_WIDTH;
	canvas.height = WINDOW_HEIGHT;
	curShowTimeSeconds = getCurrentShowTimeSeconds()
	// Time render
	setInterval(() = > {
		render(context);
		update()	// Update the function
	}, 50)}Copy the code

Reset time updated

Problem: If the defined endTime is less than 99 hours up to now => return 00:00:00

Solution: Dynamically obtain endTime

// Add one hour to the current time
let endTime = new Date()
endTime.setTime(endTime.getTime() + 3600 * 1000)
Copy the code

Gorgeous clock effect

Can do the effect of the current clock, not just the countdown

The focus is on getCurrentShowTimeSecond(), which returns how long the day has passed.

// Get the current time
function getCurrentShowTimeSeconds() {
	let curTime = new Date(a);// Save is how long has passed today => clock
	let ret = curTime.getHours() * 3600 + curTime.getMinutes() * 60 + curTime.getSeconds()

	return ret
}
Copy the code

conclusion

Put on the canvas Api

The common methods and attributes used in this course are reviewed again.

Canvas

HTML

JavaScript

  • const canvas = document.getElementById('canvas')	// Get the Canvas element
    const context = canvas.getContext('2d')	// Get the context to draw
    Copy the code
  • Commonly used attributes

// The width and height of the current canvas
canvas.width
canvas.height

Copy the code
  • Commonly used method
// Get the context of the current canvas
canvas.getContext('2d')
Copy the code

Context

  • Commonly used attributes
/ / pen line width
context.lineWidth

// String color
context.strokeStyle

/ / fill color
context.fillStyle

// The canvas corresponding to the context
context.canvas

Copy the code
  • Commonly used method
// Move the nib
context.moveTo(x, y)

Draw a line / /
context.lineTo(x, y)

/ / draw arc
context.arc(cx, cy, r, sAng, eAng, anticlock = false)

// Start painting
context.beginPath()

// Finish painting
context.closePath()

// Draw the pen line color
context.stroke()

// Fill the color
context.fill()

// Refresh the rectangle
context.clearRect(x, y, width, height)
Copy the code

Afterword.

Come on ~

Clean up your front-end architecture and find a good internship!