agrosner
08/18/2022, 12:07 PMdeviant
08/18/2022, 12:46 PMImage
composable?agrosner
08/18/2022, 1:11 PMCasey Brooks
08/18/2022, 1:51 PMval image = Image()
image.src = backgroundImageUrl
image.onload = { doSomethingWithDownloadedImage(image) }
Michael Paus
08/18/2022, 2:12 PMCasey Brooks
08/18/2022, 2:14 PM@Composable
fun ComposableCanvas(
backgroundImageUrl: String,
vararg keys: Any?,
onDraw: CanvasRenderingContext2D.(width: Double, height: Double) -> Unit
) {
val imageBitmap: Image? by produceState<Image?>(null, backgroundImageUrl) {
// I had to use a `callbackFlow` here because of a weird scoping issue where I couldn't assign `.value = ` from the callback as normal
val flow = callbackFlow<Image?> {
val image = Image()
image.src = backgroundImageUrl
image.onload = { trySend(image) }
awaitClose { image.onload = null }
}
flow
.onEach { value = it }
.launchIn(this)
}
var canvasEl: HTMLCanvasElement? by remember { mutableStateOf(null) }
var drawScope: CanvasRenderingContext2D? by remember { mutableStateOf(null) }
LaunchedEffect(drawScope, imageBitmap, canvasEl, *keys) {
imageBitmap?.let { bmp ->
canvasEl?.let { canvas ->
drawScope?.apply {
canvas.width = bmp.naturalWidth
canvas.height = bmp.naturalHeight
drawImage(bmp, 0.0, 0.0, bmp.width.toDouble(), bmp.height.toDouble())
onDraw(canvas.width.toDouble(), canvas.height.toDouble())
}
}
}
}
Canvas(
{
ref {
canvasEl = it
drawScope = it.getContext("2d") as CanvasRenderingContext2D
onDispose {
canvasEl = null
drawScope = null
}
}
}
)
}
Image
as a ByteArray
, you should be able to feed that into the JS Canvas APIs. If it’s using skiko for the canvas the same as Desktop, you might be able to use this function to convert the ByteArray to an Image that can be drawn in Compose
fun ByteArray.asImage(): ImageBitmap {
return Image.makeFromEncoded(this).toComposeImageBitmap()
}
Michael Paus
08/18/2022, 2:21 PMimageBitmap
although it actually is just a JS Image 😉. But I guess in a real Compose Canvas-Web application you also want to render into your Compose Canvas.Casey Brooks
08/18/2022, 2:25 PMimageBitmap
in that snippet is this, which is both a bitmap, but also an HTML node, I guess?
JS makes no sense.Michael Paus
08/18/2022, 2:30 PMfun main() {
onWasmReady {
BrowserViewportWindow("MapDemoMpp") {
MaterialTheme {
var imageBitmap: ImageBitmap? by remember { mutableStateOf(null) }
LaunchedEffect("K1") {
val client = HttpClient(Js)
val httpResponse: HttpResponse = client.get("<https://pbs.twimg.com/media/FP_EJdPXwAAhoSR?format=jpg&name=360x360>")
client.close()
val encodedImageData: ByteArray = httpResponse.body()
val loadedImageBitmap: ImageBitmap = imageBitmapFromBytes(encodedImageData)
imageBitmap = loadedImageBitmap
}
App(imageBitmap)
}
}
}
}
fun imageBitmapFromBytes(encodedImageData: ByteArray): ImageBitmap {
return Image.makeFromEncoded(encodedImageData).toComposeImageBitmap()
}
loads an image from an URL via the Ktor client, converts it into a Compose ImageBitmap and then the App renders into into a normal Compose Canvas. Here is the result:agrosner
08/19/2022, 2:39 PM