Aaron Yoder
04/07/2021, 2:52 AMval stateVertical = rememberScrollState(0)
val stateHorizontal = rememberScrollState(0)
Box(modifier = Modifier.verticalScroll(stateVertical).padding(end = 12.dp, bottom = 12.dp).horizontalScroll(stateHorizontal)) {
Canvas(modifier = Modifier.size(width.dp, height.dp) {
drawIntoCanvas { canvas ->
// draw things
}
}
}
VerticalScrollbar(modifier = Modifier.fillMaxHeight(), adapter = rememberScrollbarAdapter(stateVertical))
HorizontalScrollbar(modifier = Modifier.fillMaxWidth().padding(end = 12.dp), adapter = rememberScrollbarAdapter(stateHorizontal))
I'm not sure if this is the proper way to implement a scrollable canvas, because while it does seem to scroll across the canvas fine, there don't appear to be any visual scrollbars, just empty space on the bottom and right sides of the global canvas, rather than relative to the window. The other issue I was having was that I was putting a pointerMoveFilter
modifier on the canvas modifier to get x, y coordinates in the canvas, which apparently isn't the proper way to do it because when adding scroll bars the coordinates of the top-left position are always 0, 0 regardless of where that actually might be on the canvas. I feel like I'm definitely misunderstanding something about scrollbars here. Are they only meant for scrolling through a list, and not across an image? How would I correctly implement it for a canvas, and is there a proper method of getting the relative-to-Canvas x, y coordinates?Igor Demin
04/07/2021, 10:17 AMimport androidx.compose.desktop.Window
import androidx.compose.foundation.Canvas
import androidx.compose.foundation.HorizontalScrollbar
import androidx.compose.foundation.VerticalScrollbar
import androidx.compose.foundation.horizontalScroll
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.rememberScrollbarAdapter
import androidx.compose.foundation.verticalScroll
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.drawscope.drawIntoCanvas
import androidx.compose.ui.input.pointer.pointerMoveFilter
import androidx.compose.ui.unit.dp
fun main() = Window {
val stateVertical = rememberScrollState(0)
val stateHorizontal = rememberScrollState(0)
var pointerCoords: Offset? by remember { mutableStateOf(null) }
Box(Modifier.fillMaxSize()) {
Box(
modifier = Modifier
.fillMaxSize()
.verticalScroll(stateVertical)
.padding(end = 12.dp, bottom = 12.dp)
.horizontalScroll(stateHorizontal)
) {
Canvas(
modifier = Modifier.width(3000.dp).height(4000.dp)
.pointerMoveFilter(
onMove = { pointerCoords = it; false },
onExit = { pointerCoords = null; false }
)
) {
drawIntoCanvas {
drawCircle(Color.Red, radius = 100f, center = Offset(100f, 100f))
if (pointerCoords != null) {
val scrollOffset = Offset(stateHorizontal.value.toFloat(), stateVertical.value.toFloat())
drawCircle(Color.Blue, radius = 20f, center = pointerCoords!! + scrollOffset)
}
}
}
}
VerticalScrollbar(
modifier = Modifier.fillMaxHeight().align(Alignment.CenterEnd),
adapter = rememberScrollbarAdapter(stateVertical)
)
HorizontalScrollbar(
modifier = Modifier.fillMaxWidth().align(Alignment.BottomCenter).padding(end = 12.dp),
adapter = rememberScrollbarAdapter(stateHorizontal)
)
}
}
Canvas should have fixed size to be able to scroll it.
Probably we should return not a window-relative coordinates, but a scroll-relative coordinates. Until then you can manually addmodifier on the canvas modifier to get x, y coordinates in the canvaspointerMoveFilter
scrollOffset
(see example).Aaron Yoder
04/08/2021, 12:32 AM