Skip to content

Instantly share code, notes, and snippets.

@bitwalker
Last active Aug 29, 2015
Embed
What would you like to do?
.NET Newsletter Challenge #5...in Scala [http://www.scala-js-fiddle.com/gist/26d0dbbe6bfa5e880efc]
import js.{JSApp, Date}
import Page.{canvas, renderer}
import dom.window
/**
* An example of Scala/Scala.js code which renders a clock using HTML5 Canvas
* @author Paul Schoenfelder
**/
object ScalaJSExample extends JSApp {
// The drawable canvas dimensions
private val (height, width) = (canvas.height, canvas.width)
// The clock face to draw
private val face = ClockFace(origin = 0, destination = 142, lineWidth = 14, color = "black")
// The ticks to be drawn on the clock
private val ticks = Seq[Tick](
HourTick(origin = 100, destination = 120, lineWidth = 8, color = "green"),
MinuteTick(origin = 117, destination = 120, lineWidth = 5, color = "black")
)
// The hands to be drawn on the clock
private val hands = Seq[ClockHand](
HourHand(origin = -20, destination = 80, lineWidth = 14),
MinuteHand(origin = -28, destination = 112, lineWidth = 10),
SecondHand(origin = -30, destination = 83, lineWidth = 5)
)
/**
* The main entry point, runs the render loop once per second
**/
def main() = dom.setInterval(render, 1000)
private def render = () => {
// The moment to render on the clock
val now = Moment.now
// Reset canvas
reset
// Draw the clock elements
ticks.foreach(_ draw now)
hands.foreach(_ draw now)
face draw now
}
private def reset = {
renderer.save()
renderer.clearRect(0, 0, width, height)
renderer.translate(width / 2, height / 2)
renderer.scale(1, 1)
renderer.rotate(-Math.PI/2)
renderer.lineCap = "round"
}
}
/**
* An immutable class representing a moment in time
*/
case class Moment(hour: Int, minute: Int, second: Int)
object Moment {
def now = {
val d = new Date()
val hour = d.getHours() match {
case h if h >= 12 => h - 12
case h => h
}
Moment(hour, d.getMinutes(), d.getSeconds())
}
}
/**
* Defines an object which can be drawn and knows how to draw itself
**/
trait Drawable {
val origin: Int
val destination: Int
val lineWidth: Int
val color: String
def draw(m: Moment): Unit
}
/**
* The clock face definition, which ultimately draws the border and fill for the clock itself.
**/
case class ClockFace(origin: Int, destination: Int, lineWidth: Int, color: String = "white") extends Drawable {
def draw(m: Moment) = {
renderer.beginPath()
renderer.lineWidth = lineWidth
renderer.strokeStyle = color
renderer.arc(origin, 0, destination, 0, Math.PI*2, true)
renderer.stroke()
renderer.restore()
}
}
/**
* The contract for ticks which can be drawn on the clock.
**/
sealed trait Tick extends Drawable {
// Ticks must define how many need to be drawn on the clock
def ticks: Int
def draw(m: Moment) = {
renderer.save()
renderer.lineWidth = lineWidth
renderer.fillStyle = color
renderer.strokeStyle = color
for (hour <- 0 until ticks) {
renderer.beginPath()
renderer.rotate(Math.PI / (ticks / 2))
renderer.moveTo(origin, 0)
renderer.lineTo(destination, 0)
renderer.stroke()
}
renderer.restore()
}
}
case class HourTick(origin: Int, destination: Int, lineWidth: Int, color: String) extends Tick {
override val ticks = 12
}
case class MinuteTick(origin: Int, destination: Int, lineWidth: Int, color: String) extends Tick {
override val ticks = 60
}
/**
* The contract for hands which can be drawn on the clock.
**/
sealed trait ClockHand extends Drawable {
// Clock hands must define how to rotate
def rotate(m: Moment): Double
def draw(m: Moment) = {
renderer.save()
renderer.rotate(rotate(m))
renderer.strokeStyle = color
renderer.fillStyle = color
renderer.lineWidth = lineWidth
renderer.beginPath()
renderer.moveTo(origin,0)
renderer.lineTo(destination,0)
renderer.stroke()
renderer.restore()
}
}
case class HourHand(origin: Int, destination: Int, lineWidth: Int, color: String = "black") extends ClockHand {
def rotate(m: Moment) = m match { case Moment(h, m, s) =>
h*(Math.PI/6) + (Math.PI/360)*m + (Math.PI/21600)*s
}
}
case class MinuteHand(origin: Int, destination: Int, lineWidth: Int, color: String = "black") extends ClockHand {
def rotate(m: Moment) = m match { case Moment(_, m, s) =>
(Math.PI/30)*m + (Math.PI/1800)*s
}
}
case class SecondHand(origin: Int, destination: Int, lineWidth: Int, color: String = "red") extends ClockHand {
def rotate(m: Moment) = m match { case Moment(_, _, s) =>
s * Math.PI/30
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment