mirror of
https://github.com/taigrr/wasm-experiments
synced 2025-01-18 04:03:21 -08:00
211 lines
4.7 KiB
Go
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
|
|
}
|