1
0
mirror of https://github.com/taigrr/wasm-experiments synced 2025-01-18 04:03:21 -08:00
2019-06-09 20:09:50 +01:00

211 lines
4.7 KiB
Go

// +build js,wasm
package main
import (
"fmt"
"math"
"math/rand"
"strconv"
"syscall/js"
)
var (
width float64
height float64
mousePos [2]float64
ctx js.Value
lineDistSq float64 = 100 * 100
)
func main() {
// Init Canvas stuff
doc := js.Global().Get("document")
canvasEl := doc.Call("getElementById", "mycanvas")
width = doc.Get("body").Get("clientWidth").Float()
height = doc.Get("body").Get("clientHeight").Float()
canvasEl.Call("setAttribute", "width", width)
canvasEl.Call("setAttribute", "height", height)
ctx = canvasEl.Call("getContext", "2d")
done := make(chan struct{}, 0)
dt := DotThing{speed: 160}
mouseMoveEvt := js.FuncOf(func(this js.Value, args []js.Value) interface{} {
e := args[0]
mousePos[0] = e.Get("clientX").Float()
mousePos[1] = e.Get("clientY").Float()
return nil
})
defer mouseMoveEvt.Release()
countChangeEvt := js.FuncOf(func(this js.Value, args []js.Value) interface{} {
evt := args[0]
intVal, err := strconv.Atoi(evt.Get("target").Get("value").String())
if err != nil {
println("Invalid value", err)
return nil
}
dt.SetNDots(intVal)
return nil
})
defer countChangeEvt.Release()
speedInputEvt := js.FuncOf(func(this js.Value, args []js.Value) interface{} {
evt := args[0]
fval, err := strconv.ParseFloat(evt.Get("target").Get("value").String(), 64)
if err != nil {
println("Invalid value", err)
return nil
}
dt.speed = fval
return nil
})
defer speedInputEvt.Release()
// Handle mouse
doc.Call("addEventListener", "mousemove", mouseMoveEvt)
doc.Call("getElementById", "count").Call("addEventListener", "change", countChangeEvt)
doc.Call("getElementById", "speed").Call("addEventListener", "input", speedInputEvt)
dt.SetNDots(100)
dt.lines = false
var renderFrame js.Func
var tmark float64
var markCount = 0
var tdiffSum float64
renderFrame = js.FuncOf(func(this js.Value, args []js.Value) interface{} {
now := args[0].Float()
tdiff := now - tmark
tdiffSum += now - tmark
markCount++
if markCount > 10 {
doc.Call("getElementById", "fps").Set("innerHTML", fmt.Sprintf("FPS: %.01f", 1000/(tdiffSum/float64(markCount))))
tdiffSum, markCount = 0, 0
}
tmark = now
// Pool window size to handle resize
curBodyW := doc.Get("body").Get("clientWidth").Float()
curBodyH := doc.Get("body").Get("clientHeight").Float()
if curBodyW != width || curBodyH != height {
width, height = curBodyW, curBodyH
canvasEl.Set("width", width)
canvasEl.Set("height", height)
}
dt.Update(tdiff / 1000)
js.Global().Call("requestAnimationFrame", renderFrame)
return nil
})
defer renderFrame.Release()
// Start running
js.Global().Call("requestAnimationFrame", renderFrame)
<-done
}
// DotThing manager
type DotThing struct {
dots []*Dot
lines bool
speed float64
}
// Update updates the dot positions and draws
func (dt *DotThing) Update(dtTime float64) {
if dt.dots == nil {
return
}
ctx.Call("clearRect", 0, 0, width, height)
// Update
for i, dot := range dt.dots {
if dot.pos[0] < dot.size {
dot.pos[0] = dot.size
dot.dir[0] *= -1
}
if dot.pos[0] > width-dot.size {
dot.pos[0] = width - dot.size
dot.dir[0] *= -1
}
if dot.pos[1] < dot.size {
dot.pos[1] = dot.size
dot.dir[1] *= -1
}
if dot.pos[1] > height-dot.size {
dot.pos[1] = height - dot.size
dot.dir[1] *= -1
}
mdx := mousePos[0] - dot.pos[0]
mdy := mousePos[1] - dot.pos[1]
d := math.Sqrt(mdx*mdx + mdy*mdy)
if d < 200 {
dInv := 1 - d/200
dot.dir[0] += (-mdx / d) * dInv * 8
dot.dir[1] += (-mdy / d) * dInv * 8
}
for j, dot2 := range dt.dots {
if i == j {
continue
}
mx := dot2.pos[0] - dot.pos[0]
my := dot2.pos[1] - dot.pos[1]
d := math.Sqrt(mx*mx + my*my)
if d < 100 {
dInv := 1 - d/100
dot.dir[0] += (-mx / d) * dInv
dot.dir[1] += (-my / d) * dInv
}
}
dot.dir[0] *= 0.1 //friction
dot.dir[1] *= 0.1 //friction
dot.pos[0] += dot.dir[0] * dt.speed * dtTime * 10
dot.pos[1] += dot.dir[1] * dt.speed * dtTime * 10
ctx.Set("globalAlpha", 0.5)
ctx.Call("beginPath")
ctx.Set("fillStyle", fmt.Sprintf("#%06x", dot.color))
ctx.Set("strokeStyle", fmt.Sprintf("#%06x", dot.color))
ctx.Set("lineWidth", dot.size)
ctx.Call("arc", dot.pos[0], dot.pos[1], dot.size, 0, 2*math.Pi)
ctx.Call("fill")
}
}
// SetNDots reinitializes dots with n size
func (dt *DotThing) SetNDots(n int) {
dt.dots = make([]*Dot, n)
for i := 0; i < n; i++ {
dt.dots[i] = &Dot{
pos: [2]float64{
rand.Float64() * width,
rand.Float64() * height,
},
dir: [2]float64{
rand.NormFloat64(),
rand.NormFloat64(),
},
color: uint32(rand.Intn(0xFFFFFF)),
size: 10,
}
}
}
// Dot represents a dot ...
type Dot struct {
pos [2]float64
dir [2]float64
color uint32
size float64
}