函数调用
syscall/jsjs.Global()js.ValuewindowGet()windowjs.ValueInvoke()
js.ValueSet()js.NewCallback()func(args []js.Value)args
package mainimport ( "sync" "syscall/js")func main() { jsGlobal := js.Global() goFuncs := jsGlobal.Get("goFuncs") goFuncs.Set("goFunc", js.NewCallback(func(args []js.Value) { i, s := args[0].Int(), args[1].String() i, s = i+2, s+"b" res := jsGlobal.Get("jsFunc").Invoke(i, s) i, s = res.Get("i").Int(), res.Get("s").String() ret := args[2] ret.Set("i", i) ret.Set("s", s) })) wg := &sync.WaitGroup{} wg.Add(1) wg.Wait() }
<html> <head> <meta charset="utf-8"> <script class="lazyload" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsQAAA7EAZUrDhsAAAANSURBVBhXYzh8+PB/AAffA0nNPuCLAAAAAElFTkSuQmCC" data-original="wasm_exec.js"></script> <script> window.goFuncs = {} window.jsFunc = (i, s) => { return {i: i + 3, s: s + "c"} } const go = new Go() WebAssembly.instantiateStreaming(fetch("go_main.wasm"), go.importObject). then(res => { go.run(res.instance) }) window.onload = () => { document.getElementById("btn").addEventListener("click", event => { let ret = {} goFuncs.goFunc(1, "a", ret) console.dir(ret) }) } </script> </head> <body> <input id="btn" type="button" value="go" /> </body></html>
go.run()main()main()
内存访问
除了函数调用的交互,还可以通过内存直接共享数据。
instance.exports.meminstanceWebAssembly.instantiate*memTypedArray
下面的例子会在 JS 端打开一个图片文件,显示在页面上,并将文件内容直接写入 Golang 使用的内存,在 Golang 中将图片的色调改变,再回调 JS 端来读取改变之后的图片,并显示在页面上。
package mainimport ( "bytes" "image" "reflect" "sync" "syscall/js" "unsafe" "github.com/anthonynsimon/bild/adjust" "github.com/anthonynsimon/bild/imgio")type Ctx struct { SetFileArrCb js.Value SetImageToHueCb js.Value }func setFile(ctx *Ctx, fileJsArr js.Value, length int) { bs := make([]byte, length) ptr := (*reflect.SliceHeader)(unsafe.Pointer(&bs)).Data ctx.SetFileArrCb.Invoke(fileJsArr, ptr) img, _, _ := image.Decode(bytes.NewReader(bs)) buf := &bytes.Buffer{} imgio.JPEGEncoder(93)(buf, adjust.Hue(img, -150)) bs = buf.Bytes() ptr = (*reflect.SliceHeader)(unsafe.Pointer(&bs)).Data ctx.SetImageToHueCb.Invoke(ptr, len(bs)) }func main() { jsGlobal := js.Global() ctx := &Ctx{ SetFileArrCb: jsGlobal.Get("setFileArrCb"), SetImageToHueCb: jsGlobal.Get("setImageToHueCb"), } goFuncs := jsGlobal.Get("goFuncs") goFuncs.Set("setFile", js.NewCallback(func(args []js.Value) { setFile(ctx, args[0], args[1].Int()) })) wg := &sync.WaitGroup{} wg.Add(1) wg.Wait() }
<html> <head> <meta charset="utf-8"> <script class="lazyload" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsQAAA7EAZUrDhsAAAANSURBVBhXYzh8+PB/AAffA0nNPuCLAAAAAElFTkSuQmCC" data-original="wasm_exec.js"></script> <script> let goMemArr, fileType let setImageToElem = (elemId, dateArr) => { document.getElementById(elemId).src = URL.createObjectURL( new Blob([dateArr], {"type": fileType})) } window.setFileArrCb = (fileArr, ptr) => { goMemArr.set(fileArr, ptr) } window.setImageToHueCb = (ptr, len) => { setImageToElem("img-hue", goMemArr.slice(ptr, ptr + len)) } window.goFuncs = {} const go = new Go() WebAssembly.instantiateStreaming(fetch("go_main.wasm"), go.importObject). then(res => { goMemArr = new Uint8Array(res.instance.exports.mem.buffer) go.run(res.instance) } ) let onFileSelected = event => { let reader = new FileReader() let file = event.target.files[0] fileType = file.type reader.onload = event => { let fileArr = new Uint8Array(event.target.result) setImageToElem("img-ori", fileArr) window.goFuncs.setFile(fileArr, fileArr.length) } reader.readAsArrayBuffer(file) } window.onload = () => { document.getElementById("file-input").addEventListener("change", onFileSelected) } </script> </head> <body> <input id="file-input" type="file" /> <br /> <image id="img-ori" /> <br /> <image id="img-hue" /> </body></html>
放上一张浏览器截图,笔者选择了一张摄影名作《曼联球迷罗玉phone》,可以看到下方的图片色调被调整,球衣的颜色变为城城(队徽为笔者头像)的天蓝色。