preface

This article will talk about how to operate canvas through Go and draw an online form similar to Tencent document form. Of course, it’s a demo (you get the idea). Then, this article will mainly talk about the following points:

  • Use Go to manipulate the Canvas drawing table
  • Use JS to manipulate the Canvas drawing table
  • Performance issues

Draw the table using the Go operation canvas

Let’s jump right into the Go code here, and check out the previous articles in this series if you have any basic questions about how to compile Go into WASM or anything else.

package main

import (
	"fmt"
	"syscall/js"
	"time"
)

var drawCtx js.Value
var window js.Value

func init(a) {
	window = js.Global()
	doc := window.Get("document")
	body := doc.Get("body")

	canvas := doc.Call("createElement"."canvas")
	canvas.Set("height", window.Get("innerHeight"))
	canvas.Set("width", window.Get("innerWidth"))
	body.Call("appendChild", canvas)

	drawCtx = canvas.Call("getContext"."2d")}func drawTable(a) {

	width := window.Get("innerWidth").Int()
	height := window.Get("innerHeight").Int()
	// Empty the canvas before drawing
	drawCtx.Call("clearRect".0.0, width, height)
	// The width and height of the cell
	cellHeight := 22
	cellWidth := 120

	drawCtx.Set("strokeStyle"."#dcdcdc")
	drawCtx.Set("lineWidth".1)
	drawCtx.Call("translate".0.5.0.5)
	drawCtx.Call("beginPath")
	/ / vertical lines
	for i := 0; i < width; i = i + cellWidth {
		drawCtx.Call("moveTo", i, 0)
		drawCtx.Call("lineTo", i, height)
		drawCtx.Call("stroke")}/ / draw a horizontal line
	for j := 0; j < height; j = j + cellHeight {
		drawCtx.Call("moveTo".0, j)
		drawCtx.Call("lineTo", width, j)
	}
	drawCtx.Call("stroke")
	drawCtx.Call("translate".0.5.0.5)}func main(a) {
	drawTable()
	// Prevent the go program from exiting, because the js side can no longer be called after exiting
	signal := make(chan int)
	<-signal
}

Copy the code

Front-end JS code:

const go = new Go()
WebAssembly.instantiateStreaming(fetch('main.wasm'), go.importObject)
    .then(async result => {
        go.run(result.instance);
    });
Copy the code

The above is an unremarkable demo of using Go to draw a two-dimensional table. Take a look at the effect first, and then the code:

The effect is like the above, it looks like the Tencent document table, right?

In the above code, we defined an init function and a drawTable function. As you may have noticed, init is not called anywhere else in the code. Why does this work? Objects such as drawCtx are initialized normally. Why is that? In fact, if you’re familiar with Go, the init method, before each package is loaded and executed, the init method under that package is executed. This is a feature of the Go language itself, and in our case, the init method is executed before the main method. So you get the idea.

Init function code description

	drawCtx = canvas.Call("getContext"."2d")
Copy the code

In the initialization method, the first few lines of code add a Canvas DOM to the page and get the Canvas object. The above line of code is the most critical. We obtain the 2D context object of canvas through the Canvas object. Only after we have this object, can we perform the drawing operation.

DrawTable function code description

   width := window.Get("innerWidth").Int()
   height := window.Get("innerHeight").Int()
Copy the code

As you can see from the screenshot, our table takes up the entire window, so we need to get the width and height of the window first.

   drawCtx.Call("translate".0.5.0.5)
   drawCtx.Call("translate".0.5.0.5)
Copy the code

If you don’t know much about canvas drawing, you probably won’t understand what these two lines of code are doing.

Let’s see what happens when we drop these two lines of code:

Comparing the screenshot above, you can see what these two lines are for.

drawCtx.Call("beginPath")
/ / vertical lines
for i := 0; i < width; i = i + cellWidth {
	drawCtx.Call("moveTo", i, 0)
	drawCtx.Call("lineTo", i, height)
	drawCtx.Call("stroke")}/ / draw a horizontal line
for j := 0; j < height; j = j + cellHeight {
	drawCtx.Call("moveTo".0, j)
	drawCtx.Call("lineTo", width, j)
}
drawCtx.Call("stroke")
Copy the code

The code here, which should be fairly simple, is to draw the horizontal and vertical lines of the table separately. It should be easy to see.

In main, only the drawTable() function is called, and there is no other operation.

The above is probably using Go to operate canvas to draw a two-dimensional table, but it is very simple. It is definitely not done like this in the actual project, but the above things are used. You know what I mean?

Use JS to manipulate the Canvas drawing table

In order to compare the performance of canvas in JS and WASM operations, we will give a javascript code:

let drawCtx ;
const width = window.innerWidth
const height = window.innerHeight
function init() {
    const canvas = document.createElement("canvas");
    canvas.width = width;
    canvas.height = height;
    document.body.appendChild(canvas);

    drawCtx = canvas.getContext("2d")}function drawTable() {
    // Empty the canvas before drawing
    drawCtx.clearRect(0.0, width, height)
    // The width and height of the cell
    const cellHeight = 22
    const cellWidth = 120

    drawCtx.strokeStyle = "#dcdcdc";
    drawCtx.lineWidth = 1;
    drawCtx.translate(0.5.0.5)
    drawCtx.beginPath()
    / / vertical lines
    for(let i = 0; i < width; i = i + cellWidth ){
        drawCtx.moveTo(i, 0)
        drawCtx.lineTo(i, height)
        drawCtx.stroke()
    }

    / / draw a horizontal line
    for(let j = 0; j < height; j = j + cellHeight) {
        drawCtx.moveTo(0, j)
        drawCtx.lineTo(width, j)
    }
    drawCtx.stroke()
    drawCtx.translate(0.5.0.5)
}

init();
// const s = Date.now();
// for (let i=0; i< 100000; i++) {
    drawTable();
// }
// console.log(Date.now() - s)
Copy the code

The code is exactly the same as the Go code above, because I copied it from above and changed it to JS. Js code, I will not say here, you should be able to understand, right?

Performance issues

Drawing 1000 times

Wasm (Go) : about 300ms

Js: About 24ms

Drawing 10000 times

Wasm (Go) : about 2.7s

Js: About 180ms

As you can see, drawing a Canvas using JS is much faster than wASM. Wasm is not used to manipulate the DOM. It is used in specific scenarios, such as the md5 calculation of files described in the previous article. We will continue to operate the DOM as usual. The above test is based on my demo code, and then test data in Chrome browser and iMac. If you have any questions, please feel free to contact me.

conclusion

As you can see from the examples above, you can use Go to manipulate anything on the front end, including DOM, JS objects, and so on. However, when we use it in practice, we must know what scenario, what kind of technology to achieve, can get the best performance, which is the point we need to consider in the actual project development. That’s about it. If you have any questions, please feel free to contact me at 🙏.