51 lines
1.8 KiB
Scala
51 lines
1.8 KiB
Scala
package snake
|
|
package web
|
|
|
|
import scala.scalajs.js
|
|
import scala.scalajs.js.Dynamic.global
|
|
import scala.scalajs.js.typedarray.Uint32Array
|
|
|
|
/**
|
|
* Browser glue: turns a canvas into a Platform. Impure by nature; not part of the exercises' logic.
|
|
* `js.Dynamic` lets us call browser APIs by name without type facades (and without extra libraries).
|
|
*/
|
|
final class BrowserPlatform(canvas: js.Dynamic) extends Platform:
|
|
private val ctx = canvas.getContext("2d")
|
|
private val window = global.window
|
|
|
|
// Keys that scroll the page should only steer the game.
|
|
private val prevent = Set("ArrowUp", "ArrowDown", "ArrowLeft", "ArrowRight", " ")
|
|
|
|
private def listen(kind: String, cb: String => Unit): Unit =
|
|
val handler: js.Function1[js.Dynamic, Unit] = e =>
|
|
val key = e.key.asInstanceOf[String]
|
|
if prevent(key) then e.preventDefault()
|
|
cb(key)
|
|
window.addEventListener(kind, handler)
|
|
|
|
def fillRect(x: Int, y: Int, w: Int, h: Int, color: String): Unit =
|
|
ctx.fillStyle = color
|
|
ctx.fillRect(x, y, w, h)
|
|
|
|
def fillText(x: Int, y: Int, text: String, color: String, size: Int, align: String): Unit =
|
|
ctx.fillStyle = color
|
|
ctx.font = s"${size}px monospace"
|
|
ctx.textAlign = align
|
|
ctx.fillText(text, x, y)
|
|
|
|
def randomSeed(): Long =
|
|
val buf = new Uint32Array(1)
|
|
global.crypto.getRandomValues(buf)
|
|
buf(0).toLong
|
|
|
|
// t is the number of milliseconds since the first animation frame.
|
|
def startClock(onTime: Long => Unit): Unit =
|
|
def frame(start: Option[Double]): js.Function1[Double, Unit] = now =>
|
|
val t0 = start.getOrElse(now)
|
|
onTime((now - t0).toLong)
|
|
window.requestAnimationFrame(frame(Some(t0)))
|
|
window.requestAnimationFrame(frame(None))
|
|
|
|
def onKeyDown(cb: String => Unit): Unit = listen("keydown", cb)
|
|
def onKeyUp(cb: String => Unit): Unit = listen("keyup", cb)
|