JavaScript Browser
Kotlin compiles to JavaScript just as well as to Java byte code. Therefore, as the final step, we implement our game as an application for web browsers.
The Kotlin Multi Platform Gradle plugin comes with everything we need to create a single page web application. It uses Webpack to generate the necessary JavaScript files and NPM/Yarn to manage the dependencies to JavaScript libraries. All this from within Gradle and with sensible defaults which make the project “just work”.
We start by adding the js
target to our common project.
plugins {
id 'org.jetbrains.kotlin.multiplatform'
}
kotlin {
jvm()
js {
browser()
}
…
sourceSets {
…
jsMain {
dependencies {
implementation(kotlin("stdlib-js"))
}
}
jsTest {
dependencies {
implementation(kotlin("test-js"))
}
}
}
}
As for iOS and Java we now gain the ability to run our tests in JavaScript. This is done under the hood by the Gradle plugin which configures and runs Karma.
As for Java and iOS we need to provide an implementation of a GolTimer.
import kotlin.browser.window
actual class GolTimer actual constructor(action: () -> Unit) {
private val timer = window.setInterval({ action() }, 500)
actual fun stop() {
window.clearInterval(timer)
}
}
Then as in the previous step we create a new Gradle project. This time a Kotlin JS project and add our common project as a dependency. The Gradle plugin handles the build process automatically.
plugins {
id 'org.jetbrains.kotlin.js'
}
repositories {
mavenCentral()
}
dependencies {
implementation project(':kotlin-mpp-common')
implementation "org.jetbrains.kotlin:kotlin-stdlib-js"
testImplementation "org.jetbrains.kotlin:kotlin-test-js"
}
kotlin.target.browser { }
A simple index.html
will do the load our application into the browser
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>kotlin-mpp-browser</title>
</head>
<body>
</body>
<script src="kotlin-mpp-browser.js"></script>
</html>
Now we need to program the UI. This can be done in pure Kotlin. All we need is to create an HTML canvas, the button, and provide the necessary callbacks for our existing game logic.
fun main() {
document.body?.appendElement("button") {
this as HTMLButtonElement
textContent = "Pause"
onclick = {
if (game.running) {
textContent = "Resume"
game.pause()
} else {
textContent = "Pause"
game.resume()
}
}
}
document.body?.appendElement("canvas") {
this as HTMLCanvasElement
width = canvasWidth
height = canvasHeight
val context = getContext("2d") as CanvasRenderingContext2D
fun drawBoard() {
golCanvas.drawBoard(
board = board,
clear = {
context.fillStyle = "white"
context.fillRect(0.0, 0.0, canvasWidth.toDouble(), canvasHeight.toDouble())
},
drawRect = { left, top, size ->
context.beginPath()
context.fillStyle = "gray"
context.fillRect(left.toDouble(), top.toDouble(), size.toDouble(), size.toDouble())
context.stroke()
}
)
}
….
addEventListener("touch", { event: Event ->
event as TouchEvent
console.log(event)
})
game.afterNextGenerationCalculated = { drawBoard() }
game.resume()
}
onwheel = { event ->
val wheelMax = 100f
val wheelDelta = event.asDynamic().wheelDelta as Float
val windowedDelta = when {
wheelDelta > wheelMax -> wheelMax
wheelDelta < -wheelMax -> -wheelMax
else -> wheelDelta
}
val zoom = (windowedDelta + wheelMax) / wheelMax
golCanvas.zoom(board, zoom, event.clientX.toFloat(), event.clientY.toFloat())
drawBoard()
event.preventDefault()
}
}
Use ./gradlew run
to start the application. This will compile the Kotlin code to
JavaScript, use Webpack to create a single JS file, start a simple webserver and a browser
which loads the application.