golang在Go代码中实现HTML5 Canvas绘图功能插件库go-canvas的使用

Golang在Go代码中实现HTML5 Canvas绘图功能插件库go-canvas的使用

go-canvas简介

go-canvas是一个纯Go+WebAssembly库,用于在浏览器中高效地在HTML5 canvas元素上绘图,无需回调JS来使用canvas绘图函数。

该库提供以下功能:

  • 抽象了初始化DOM交互以设置canvas的过程
  • 创建阴影图像帧和用于在其上绘图的图形上下文
  • 初始化使用truetype字体进行文本绘制的基本字体缓存
  • 设置和处理来自浏览器的requestAnimationFrame回调

标准syscall方式与go-canvas方式的对比

标准syscall方式

在标准的WASM应用中,Go代码必须创建一个响应requestAnimationFrame回调的函数,并通过syscall/js函数和上下文切换与canvas绘图原语交互。例如:

laserCtx.Call("beginPath")
laserCtx.Call("arc", gs.laserX, gs.laserY, gs.laserSize, 0, math.Pi*2, false)
laserCtx.Call("fill")
laserCtx.Call("closePath")

这种方式存在以下缺点:

  • 无法在编译时轻松检查JS调用
  • 强制每帧完全重绘,即使canvas上没有任何变化
  • 变化可能比请求的帧速率慢得多

go-canvas方式

go-canvas允许使用Go原生方式完成所有绘图,它创建一个完全独立的图像缓冲区,使用2D绘图库进行绘制。目前使用的是github.com/llgcode/draw2d,它提供了大多数标准canvas原语和更多功能。

这个阴影图像缓冲区可以在开发者认为合适的速率下更新,可能比浏览器动画速率慢。在每次requestAnimationFrame回调期间,阴影图像缓冲区会自动复制到浏览器canvas缓冲区。

使用示例

下面是一个完整的go-canvas使用示例demo:

package main

import (
	"image/color"
	"math"
	"time"

	"github.com/markfarnan/go-canvas/canvas"
	"github.com/llgcode/draw2d/draw2dimg"
)

// 全局状态
var gs struct {
	laserX, laserY, laserSize float64
	laserSpeedX, laserSpeedY  float64
}

func init() {
	// 初始化激光位置和速度
	gs.laserX = 100
	gs.laserY = 100
	gs.laserSize = 10
	gs.laserSpeedX = 3
	gs.laserSpeedY = 3
}

// Render 是绘图回调函数
func Render(gc *draw2dimg.GraphicContext) bool {
	// 更新激光位置
	gs.laserX += gs.laserSpeedX
	gs.laserY += gs.laserSpeedY

	// 边界检查
	if gs.laserX > 300 || gs.laserX < 0 {
		gs.laserSpeedX = -gs.laserSpeedX
	}
	if gs.laserY > 300 || gs.laserY < 0 {
		gs.laserSpeedY = -gs.laserSpeedY
	}

	// 清除画布
	gc.SetFillColor(color.RGBA{0xff, 0xff, 0xff, 0xff})
	gc.Clear()

	// 绘制红色激光
	gc.SetFillColor(color.RGBA{0xff, 0x00, 0x00, 0xff})
	gc.SetStrokeColor(color.RGBA{0xff, 0x00, 0x00, 0xff})

	gc.BeginPath()
	gc.ArcTo(gs.laserX, gs.laserY, gs.laserSize, gs.laserSize, 0, math.Pi*2)
	gc.FillStroke()
	gc.Close()

	return true // 表示绘制了内容,需要复制到浏览器
}

func main() {
	// 创建canvas
	c, err := canvas.NewCanvas()
	if err != nil {
		panic(err)
	}

	// 启动canvas渲染,最大帧率为60fps
	c.Start(Render, 60)

	// 保持程序运行
	select {
	case <-time.After(time.Minute):
		c.Stop()
	}
}

编译和运行

  1. 编译WASM模块:
GOOS=js GOARCH=wasm go build -o main.wasm
  1. 创建一个简单的HTML文件加载WASM模块:
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>Go Canvas Demo</title>
</head>
<body>
    <canvas id="canvas" width="400" height="400"></canvas>
    <script src="wasm_exec.js"></script>
    <script>
        const go = new Go();
        WebAssembly.instantiateStreaming(fetch("main.wasm"), go.importObject)
            .then(result => go.run(result.instance));
    </script>
</body>
</html>
  1. 使用Caddy或其他Web服务器提供文件服务:
caddy file-server

功能控制

go-canvas提供了几个选项来控制绘图行为:

  1. 用户可以在调用START函数时指定Go渲染/绘制回调方法
  2. 渲染例程可以选择返回是否进行了任何绘制。如果返回false,则requestAnimationFrame回调不做任何事情
  3. 'start’函数接受maxFPS参数,库会自动限制requestAnimationFrame回调
  4. 可以为渲染函数传递’nil’,在这种情况下,所有绘图完全在用户控制下进行

已知问题

目前对于长时间绘制函数可能存在竞态条件,requestAnimationFrame可能会获得部分完成的图像缓冲区。但随着WASM支持适当的多线程,这个问题需要处理。

未来计划

go-canvas计划扩展的功能包括:

  • 支持分层canvas
  • 鼠标交互的陷阱和辅助函数
  • 单元测试
  • 图像缓冲区复制的性能改进
  • 添加FPS计算指标

这个库是为#webassembly上的Gophers Slack成员编写的,欢迎反馈和建议。


更多关于golang在Go代码中实现HTML5 Canvas绘图功能插件库go-canvas的使用的实战教程也可以访问 https://www.itying.com/category-94-b0.html

1 回复

更多关于golang在Go代码中实现HTML5 Canvas绘图功能插件库go-canvas的使用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


使用go-canvas在Go中实现HTML5 Canvas绘图

go-canvas是一个Go语言库,允许你在服务器端或桌面应用中创建HTML5 Canvas风格的绘图,并可以导出为PNG、JPEG等格式。下面我将详细介绍如何使用这个库。

安装go-canvas

首先安装go-canvas库:

go get github.com/tfriedel6/canvas

基本使用示例

package main

import (
	"image/color"
	"os"

	"github.com/tfriedel6/canvas"
	"github.com/tfriedel6/canvas/backend/softwarebackend"
)

func main() {
	// 创建一个软件后端画布,尺寸为800x600
	backend := softwarebackend.New(800, 600)
	cv, err := canvas.New(backend)
	if err != nil {
		panic(err)
	}

	// 设置填充颜色为蓝色
	cv.SetFillStyle(color.RGBA{0, 0, 255, 255})
	// 绘制一个填充矩形
	cv.FillRect(100, 100, 200, 150)

	// 设置描边颜色为红色,线宽为5
	cv.SetStrokeStyle(color.RGBA{255, 0, 0, 255})
	cv.SetLineWidth(5)
	// 绘制一个圆形路径
	cv.BeginPath()
	cv.Arc(400, 300, 100, 0, 6.283185307179586, false) // 6.283...是2π
	cv.Stroke()

	// 设置字体并绘制文本
	cv.SetFont("40px Arial")
	cv.SetFillStyle(color.RGBA{0, 0, 0, 255})
	cv.FillText("Hello, go-canvas!", 200, 500)

	// 将画布保存为PNG文件
	file, err := os.Create("output.png")
	if err != nil {
		panic(err)
	}
	defer file.Close()

	err = backend.WritePng(file)
	if err != nil {
		panic(err)
	}
}

高级功能示例

1. 绘制复杂路径

// 绘制一个五角星
func drawStar(cv *canvas.Canvas, x, y, outerRadius, innerRadius float64) {
	cv.BeginPath()
	for i := 0; i <= 5; i++ {
		angle := float64(i)*2*math.Pi/5 - math.Pi/2
		// 外点
		cv.LineTo(
			x+outerRadius*math.Cos(angle),
			y+outerRadius*math.Sin(angle),
		)
		// 内点
		angle += math.Pi / 5
		cv.LineTo(
			x+innerRadius*math.Cos(angle),
			y+innerRadius*math.Sin(angle),
		)
	}
	cv.ClosePath()
	cv.Fill()
}

// 使用
cv.SetFillStyle(color.RGBA{255, 215, 0, 255}) // 金色
drawStar(cv, 400, 300, 100, 40)

2. 使用渐变

// 创建线性渐变
grad := cv.CreateLinearGradient(0, 0, 400, 400)
grad.AddColorStop(0, color.RGBA{255, 0, 0, 255})   // 红色
grad.AddColorStop(0.5, color.RGBA{0, 255, 0, 255}) // 绿色
grad.AddColorStop(1, color.RGBA{0, 0, 255, 255})   // 蓝色

cv.SetFillStyle(grad)
cv.FillRect(0, 0, 400, 400)

3. 图像操作

// 加载图像文件
imgFile, err := os.Open("image.png")
if err != nil {
    panic(err)
}
defer imgFile.Close()

img, _, err := image.Decode(imgFile)
if err != nil {
    panic(err)
}

// 绘制图像
cv.DrawImage(img, 100, 100)

性能优化技巧

  1. 批量操作:尽量减少状态变化(如颜色、线宽等设置)
// 不好的做法 - 频繁改变状态
for i := 0; i < 100; i++ {
    cv.SetFillStyle(colors[i])
    cv.FillRect(positions[i].x, positions[i].y, 10, 10)
}

// 好的做法 - 按状态分组绘制
cv.SetFillStyle(color1)
for _, pos := range group1 {
    cv.FillRect(pos.x, pos.y, 10, 10)
}
cv.SetFillStyle(color2)
for _, pos := range group2 {
    cv.FillRect(pos.x, pos.y, 10, 10)
}
  1. 使用缓存:对于不常变化的复杂图形,可以渲染到临时画布
// 创建临时画布
tempBackend := softwarebackend.New(200, 200)
tempCv, _ := canvas.New(tempBackend)

// 在临时画布上绘制复杂图形
drawComplexShape(tempCv)

// 在主画布上多次绘制临时画布内容
for i := 0; i < 10; i++ {
    cv.DrawImage(tempBackend.Image(), i*50, i*50)
}

与其他库集成

go-canvas可以与其他Go图像处理库配合使用:

// 使用gg库处理图像后绘制到canvas
import "github.com/fogleman/gg"

func drawWithGG(cv *canvas.Canvas) {
    // 创建gg上下文
    dc := gg.NewContext(800, 600)
    
    // 使用gg绘图
    dc.DrawCircle(400, 300, 200)
    dc.SetRGB(1, 0, 0)
    dc.Fill()
    
    // 将gg图像绘制到canvas
    cv.DrawImage(dc.Image(), 0, 0)
}

注意事项

  1. go-canvas的API与浏览器中的Canvas API非常相似,但不完全相同
  2. 某些高级功能如滤镜、混合模式可能不支持
  3. 文本渲染在不同平台上可能有差异
  4. 性能上,对于复杂图形,软件后端可能不如GPU加速后端快

go-canvas是一个强大的库,特别适合需要在服务器端生成Canvas风格图形的场景,如生成图表、报告或游戏资源等。

回到顶部