在 Canvas 上绘制 Scala.js
Draw on Canvas with Scala.js
在服务器端,我创建了一个带有 canvas:
的简单页面
import scalatags.Text.all._
html(
body(
div(
h3("Let's draw something ️"),
canvas("canvas-id")
)
)
)
现在,在客户端,我希望用户能够使用鼠标在 canvas 上绘图。
我该怎么做?
在客户端,我通过其 ID 获取 canvas 并在用户将鼠标移到它上面时在其上绘制:
get[Canvas]("canvas-id").fold(
errorMsg => logger.warn("Could not find canvas. Error is {}", errorMsg),
canvas => drawOnCanvasWhenMouseMoved(canvas)
)
这是 returns 类型化元素的 get
方法:
/**
* Gets an element of type `T` by an `elementId`. Returns either the element if
* found or an [[ErrorMsg]].
*/
def get[T: ClassTag](elementId: String): Either[ErrorMsg, T] = {
val queryResult = document.querySelector(s"#$elementId")
queryResult match {
case elem: T => Right(elem)
case other => Left(ErrorMsg(s"Element with ID $elementId is $other"))
}
}
其中 ErrorMsg
是一个简单值 class:
case class ErrorMsg(value: String) extends AnyVal {
override def toString: String = value
}
绘图
private def drawOnCanvasWhenMouseMoved(canvas: Canvas) = {
getContext2D(canvas).fold(
errorMsg => logger.warn("Couldn't get rendering context of canvas: {}. Error: {}", canvas, errorMsg),
context => canvas.onmousemove = { e: MouseEvent => drawOnCanvas(e, context) }
)
def drawOnCanvas(e: MouseEvent, context: CanvasRenderingContext2D) = {
val x = e.clientX - canvas.offsetLeft
val y = e.clientY - canvas.offsetTop
context.fillStyle = "green"
context.fillRect(x, y, 2, 2)
}
}
最后,为了获取渲染上下文,我使用 getContext
:
/** Returns either this [[Canvas]]' [[CanvasRenderingContext2D]] or
* an [[ErrorMsg]] if that fails. */
private def getContext2D(canvas: Canvas): Either[ErrorMsg, CanvasRenderingContext2D] =
if (canvas != null)
canvas.getContext("2d") match {
case context: CanvasRenderingContext2D => Right(context)
case other => Left(ErrorMsg(s"getContext(2d) returned $other"))
}
else
Left(ErrorMsg("Can't get rendering context of null canvas"))
结果:
在服务器端,我创建了一个带有 canvas:
的简单页面import scalatags.Text.all._
html(
body(
div(
h3("Let's draw something ️"),
canvas("canvas-id")
)
)
)
现在,在客户端,我希望用户能够使用鼠标在 canvas 上绘图。
我该怎么做?
在客户端,我通过其 ID 获取 canvas 并在用户将鼠标移到它上面时在其上绘制:
get[Canvas]("canvas-id").fold(
errorMsg => logger.warn("Could not find canvas. Error is {}", errorMsg),
canvas => drawOnCanvasWhenMouseMoved(canvas)
)
这是 returns 类型化元素的 get
方法:
/**
* Gets an element of type `T` by an `elementId`. Returns either the element if
* found or an [[ErrorMsg]].
*/
def get[T: ClassTag](elementId: String): Either[ErrorMsg, T] = {
val queryResult = document.querySelector(s"#$elementId")
queryResult match {
case elem: T => Right(elem)
case other => Left(ErrorMsg(s"Element with ID $elementId is $other"))
}
}
其中 ErrorMsg
是一个简单值 class:
case class ErrorMsg(value: String) extends AnyVal {
override def toString: String = value
}
绘图
private def drawOnCanvasWhenMouseMoved(canvas: Canvas) = {
getContext2D(canvas).fold(
errorMsg => logger.warn("Couldn't get rendering context of canvas: {}. Error: {}", canvas, errorMsg),
context => canvas.onmousemove = { e: MouseEvent => drawOnCanvas(e, context) }
)
def drawOnCanvas(e: MouseEvent, context: CanvasRenderingContext2D) = {
val x = e.clientX - canvas.offsetLeft
val y = e.clientY - canvas.offsetTop
context.fillStyle = "green"
context.fillRect(x, y, 2, 2)
}
}
最后,为了获取渲染上下文,我使用 getContext
:
/** Returns either this [[Canvas]]' [[CanvasRenderingContext2D]] or
* an [[ErrorMsg]] if that fails. */
private def getContext2D(canvas: Canvas): Either[ErrorMsg, CanvasRenderingContext2D] =
if (canvas != null)
canvas.getContext("2d") match {
case context: CanvasRenderingContext2D => Right(context)
case other => Left(ErrorMsg(s"getContext(2d) returned $other"))
}
else
Left(ErrorMsg("Can't get rendering context of null canvas"))
结果: