I'm trying to implement a line chart with points(as circles) on it. But regardless of which Blend Mode I'm using circles reveals line's intersection on it. Here is a picture of actual result:
I want my circle to be in a solid color. Here is my implementation:
path.apply {
data.forEach { value ->
val textResult = textMeasurer.measure(value.toString())
val textOffsetX = -20f - textResult.firstBaseline
yCursor = graphDepth - ((value - data.min()) * oneDegree)
val textOffsetY = yCursor - textResult.lastBaseline / 2
drawText(textResult, color = graphStyle.textColor, topLeft = Offset(textOffsetX, textOffsetY))
drawLine(
Color.Gray,
start = Offset(0f, yCursor),
end = Offset(xCursor, yCursor),
strokeWidth = 3f,
pathEffect = PathEffect.dashPathEffect(
intervals = floatArrayOf(
10f,
5.dp.toPx()
)
)
)
moveTo(xCursor, yCursor)
xCursor += oneInterval
}
xCursor = 0f
yCursor = graphDepth - ((data.first() - data.min()) * oneDegree)
}
path.apply {
moveTo(0f, yCursor)
data.forEach { value ->
yCursor = graphDepth - ((value - data.min()) * oneDegree)
lineTo(xCursor, yCursor)
drawLine(Color.Gray, start = Offset(xCursor, 0f), end = Offset(xCursor, graphDepth))
moveTo(xCursor, yCursor)
xCursor += oneInterval
}
xCursor = 0f
yCursor = graphDepth - ((data.first() - data.min()) * oneDegree)
}
val circlePath = Path().apply {
moveTo(0f, yCursor)
data.forEach { value ->
yCursor = graphDepth - ((value - data.min()) * oneDegree)
drawCircle(
color = graphStyle.jointColor,
radius = graphStyle.jointRadius,
center = Offset(xCursor, yCursor),
)
moveTo(xCursor, yCursor)
xCursor += oneInterval
}
}
drawPath(
path,
color = graphStyle.lineColor.copy(alpha = 0.5f),
style = Stroke(width = graphStyle.lineStroke),
blendMode = BlendMode.Clear
)
drawPath(
circlePath,
color = graphStyle.jointColor,
)
In which part, what I'm missing ? Can you help me with that ?
Thanks in advance.
For blendModes to work in Jetpack Compose you need draw an offscreen buffer.
This can be done in 3 ways. By setting alpha less than 1f, Modifier.graphicsLayer{compositingStrategy = CompositingStrategy.Offscreen }
Or saving and restoring layer with
private fun DrawScope.drawWithLayer(block: DrawScope.() -> Unit) {
with(drawContext.canvas.nativeCanvas) {
val checkPoint = saveLayer(null, null)
block()
restoreToCount(checkPoint)
}
}
Good thing about this solution is you can set which section you wish to apply blendModes or which drawings will be subject to blendModes while other 2 methods makes your Composable fully drawn with blendModes.
@Preview
@Composable
fun BlendModeSample() {
Canvas(
modifier = Modifier.fillMaxSize().background(Color.DarkGray)
.graphicsLayer {
compositingStrategy = CompositingStrategy.Offscreen
}
) {
drawWithLayer {
drawLine(
color = Color.White,
start = Offset(100f, 100f),
end = Offset(300f, 100f),
strokeWidth = 10f
)
drawLine(
color = Color.White,
start = Offset(340f, 100f),
end = Offset(540f, 100f),
strokeWidth = 10f
)
drawCircle(
color = Color.Red,
center = Offset(320f, 100f),
blendMode = BlendMode.Clear,
radius = 50f
)
}
drawCircle(
color = Color.Red,
center = Offset(320f, 100f),
radius = 50f,
style = Stroke(10f)
)
}
}
By applying BlendMode.Clear inside layer you remove any line section inside the circle while you can draw a circle with stroke at same position without any blend mode being applied
Other option when you use paths is to use clipPath(pathToClip) {} to clip anything inside this path or androidx.compose.ui.graphics.Path().op()
.