Search code examples
windowskotlindesktopskia

How do I make a basic animation on Skiko (Kotlin MPP bindings to Skia)?


Using Skiko and Kotlin I want to make a basic animation: A 0 to 100 counter that automatically updates the text each second. I managed to do it, but it has a problem, it is blinking each time the window repaints. Here is the code:

import kotlinx.coroutines.*
import org.jetbrains.skija.*
import org.jetbrains.skiko.*
import javax.swing.*

public fun main() {
    val window = SkiaWindow().apply {
        layer.renderer = CounterRenderer()
        setSize(400, 175)
        isVisible = true
        defaultCloseOperation = WindowConstants.EXIT_ON_CLOSE
    }
    GlobalScope.launch {
        for (i in 0..100) {
            delay(1000)
            window.layer.repaint()
        }
    }
}

public class CounterRenderer : SkiaRenderer {
    private lateinit var canvas: Canvas
    private var counter = 0

    override fun onInit() {
    }

    override fun onDispose() {
    }

    override fun onReshape(width: Int, height: Int) {
    }

    override fun onRender(canvas: Canvas, width: Int, height: Int) {
        this.canvas = canvas

        val typeface = Typeface.makeFromName("Roboto", FontStyle.NORMAL)
        val fontSize = 30F
        val font = Font(typeface, fontSize)

        val paint = Paint().setColor(0XFF000000.toInt())
        canvas.drawString("Counter: ${counter++}", 10F, 50F, font, paint)
    }
}

I have tried to search for examples of animations with skija or skiko without success. I would really appreciate if you could give me some examples.


Solution

  • After navigating the Android Compose code, especially this class: ComposeLayer. I finally got it to work with this code:

    import kotlinx.coroutines.*
    import org.jetbrains.skija.*
    import org.jetbrains.skiko.*
    import javax.swing.*
    
    public fun main() {
        val window = SkiaWindow().apply {
            layer.renderer = CounterRenderer()
            setSize(400, 175)
            isVisible = true
            defaultCloseOperation = WindowConstants.EXIT_ON_CLOSE
        }
        GlobalScope.launch(Dispatchers.Main) {
            for (i in 0..100) {
                delay(500)
                window.layer.redrawLayer()
            }
        }
    }
    
    public class CounterRenderer : SkiaRenderer {
        private var counter = 0
        private val typeface = Typeface.makeFromName("Roboto", FontStyle.NORMAL)
        private val fontSize = 30F
        private val font = Font(typeface, fontSize)
        private val paint = Paint().setColor(0XFF000000.toInt())
    
        override fun onInit() {
        }
    
        override fun onDispose() {
        }
    
        override fun onReshape(width: Int, height: Int) {
        }
    
        override fun onRender(canvas: Canvas, width: Int, height: Int) {
            canvas.drawString("Counter: ${counter++}", 10F, 50F, font, paint)
        }
    }
    

    To run this code you need to install the specific Main Dispatcher, in this case it is by adding this to the gradle configuration:

    implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.9")
    implementation("org.jetbrains.kotlinx:kotlinx-coroutines-swing:1.3.9")