函数调用

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》,可以看到下方的图片色调被调整,球衣的颜色变为城城(队徽为笔者头像)的天蓝色。

webp