Last active
August 29, 2015 14:22
-
-
Save bitwalker/26d0dbbe6bfa5e880efc to your computer and use it in GitHub Desktop.
.NET Newsletter Challenge #5...in Scala [http://www.scala-js-fiddle.com/gist/26d0dbbe6bfa5e880efc]
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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