背景: 项目中有一个非常耗时的操作,扩大选区,即找到图片边缘的部分,以边缘的每个点为圆心画圆,以此平滑扩大选区;但是随着选区变大,带来的耗时非常恐怖,大到200+ms; 遂尝试用 WebAssembly 试试,看看耗时能否降低。
尝试:
用 go 重写了这块代码
func main() {
jsProcess := js.FuncOf(Process)
js.Global().Set("process", jsProcess)
defer jsProcess.Release()
select {}
}
type process struct {
data js.Value
img js.Value
}
func Process(this js.Value, args []js.Value) interface{} {
imgSrc := args[0]
storkeWidth := args[1]
jsCallback := args[2]
if storkeWidth.Int() == 0 {
return js.ValueOf(process{data: js.ValueOf(nil), img: imgSrc})
}
document := js.Global().Get("document")
canvas := document.Call("createElement", "canvas")
ctx := canvas.Call("getContext", "2d")
fillColor := js.ValueOf("rgba(116, 90, 241, .5)")
img := document.Call("createElement", "img")
img.Set("crossorigin", "anonymous")
img.Set("src", imgSrc)
var onloadCallback js.Func
onloadCallback = js.FuncOf(func(this js.Value, _ []js.Value) interface{} {
canvas.Set("width", img.Get("width"))
canvas.Set("height", img.Get("height"))
ctx.Call("drawImage", img, 0, 0)
imageData := ctx.Call("getImageData", 0, 0, img.Get("width").Int(), img.Get("height").Int())
data := imageData.Get("data")
boundaries := []int{}
length := data.Get("length").Int()
// tag1
for i := 0; i < length; i += 4 {
if data.Index(i+3).Int() != 0 {
boundaries = append(boundaries, i)
}
}
// tag1 end
minX := imageData.Get("width").Int()
maxX := 0
maxY := 0
var w = img.Get("width").Int()
// tag2
for _, v := range boundaries {
x := (v / 4) % w
y := (v / 4 / w)
if x < minX {
minX = x
}
if x > maxX {
maxX = x
}
if y > maxY {
maxY = y
}
FillCriclePath(ctx, js.ValueOf(x), js.ValueOf(y), storkeWidth, fillColor)
}
// tag2 end
newImageData := ctx.Call("getImageData", 0, 0, canvas.Get("width").Int(), canvas.Get("height").Int())
res := process{data: newImageData, img: canvas.Call("toDataURL")}
resMap := map[string]interface{}{
"data": res.data,
"img": res.img,
}
jsCallback.Invoke(js.ValueOf(resMap))
onloadCallback.Release()
return js.ValueOf(nil)
})
img.Set("onload", onloadCallback)
return js.ValueOf(nil)
}
func FillCriclePath(ctx js.Value, x js.Value, y js.Value, N js.Value, color js.Value) interface{} {
path := GetCriclePath(x, y, N)
ctx.Set("fillStyle", color)
ctx.Call("fill", path)
return nil
}
func GetCriclePath(x js.Value, y js.Value, radius js.Value) interface{} {
path := js.Global().Get("Path2D").New()
path.Call("arc", x, y, radius, js.ValueOf(0), js.Global().Get("Math").Get("PI").Float()*2)
return path
}
然后编译成wasm
GOOS=js GOARCH=wasm go build -o process.wasm
然后满怀期待的放到js中
const go = new Go();
WebAssembly.instantiateStreaming(
fetch('/public/assets/main.wasm'),
go.importObject
).then((result) => {
go.run(result.instance);
});
一看耗时

再看原 js 耗时

:)
在go中加了时间同级,主要耗时点在 tag1 和 tag2 中;tag2 很耗时也好理解,每个循环都要操作dom画圆,但是 tag1 耗时就堪比 js 整个逻辑操作了,就很难理解

待我找找答案