I am new to android development and have made a canvas of my own, I am using drawLine for writing on the canvas, but i want to have neon color in it and should have smooth drawing also.
androidxpose.foundation.Canvas(
modifier = Modifier
.fillMaxSize()
.pointerInput(true) {
detectDragGestures { change, dragAmount ->
change.consume()
val line = Line(
start = change.position - dragAmount,
end = change.position,
color = currentColor
)
lines.add(line)
}
}
) {
val neonGradient = Brush.linearGradient(
colors = listOf(
Color.Red.copy(alpha = 0.7f),
Color.White,
Color.Red.copy(alpha = 0.7f)
)
)
lines.forEach { line->
drawLine(
brush = neonGradient,
start = line.start,
end = line.end,
strokeWidth = line.strokeWidth,
cap = StrokeCap.Round
)
}
}
I wrote the above for this, but not getting the neon color as expected can anyone help with this.
I am new to android development and have made a canvas of my own, I am using drawLine for writing on the canvas, but i want to have neon color in it and should have smooth drawing also.
androidx.compose.foundation.Canvas(
modifier = Modifier
.fillMaxSize()
.pointerInput(true) {
detectDragGestures { change, dragAmount ->
change.consume()
val line = Line(
start = change.position - dragAmount,
end = change.position,
color = currentColor
)
lines.add(line)
}
}
) {
val neonGradient = Brush.linearGradient(
colors = listOf(
Color.Red.copy(alpha = 0.7f),
Color.White,
Color.Red.copy(alpha = 0.7f)
)
)
lines.forEach { line->
drawLine(
brush = neonGradient,
start = line.start,
end = line.end,
strokeWidth = line.strokeWidth,
cap = StrokeCap.Round
)
}
}
I wrote the above for this, but not getting the neon color as expected can anyone help with this.
Share Improve this question asked Feb 7 at 4:18 codeKarcodeKar 212 silver badges4 bronze badges1 Answer
Reset to default 1One of the ways of achieving this in Compose is using a Paint
with shadowLayer
.
val paint = remember {
Paint().apply {
style = PaintingStyle.Stroke
strokeWidth = 20f
strokeCap = StrokeCap.Round
this.asFrameworkPaint().apply {
val transparent = color
.copy(alpha = 0f)
.toArgb()
this.color = transparent
}
asFrameworkPaint().setShadowLayer(
35f * phase,
0f,
0f,
color
.copy(alpha = phase)
.toArgb()
)
}
}
and draw path with
this.drawIntoCanvas {
it.drawPath(path, paint)
drawPath(
color = Color.White.copy((0.7f + phase).coerceAtMost(1f)),
path = path,
style = Stroke(width = 4.dp.toPx(), cap = StrokeCap.Round, join = StrokeJoin.Round)
)
}
Full code
Gesture in sample below is a custom gesture i wrote to draw on Canvas as counterPart of touch events for view which does not have slope pass threshold like drag.
@Composable
private fun NeonDrawingSample() {
var motionEvent by remember { mutableStateOf(MotionEvent.Idle) }
// This is our motion event we get from touch motion
var currentPosition by remember { mutableStateOf(Offset.Unspecified) }
// This is previous motion event before next touch is saved into this current position
var previousPosition by remember { mutableStateOf(Offset.Unspecified) }
val transition: InfiniteTransition = rememberInfiniteTransition()
// Infinite phase animation for PathEffect
val phase by transition.animateFloat(
initialValue = .9f,
targetValue = .6f,
animationSpec = infiniteRepeatable(
animation = tween(
durationMillis = 1500,
easing = FastOutSlowInEasing
),
repeatMode = RepeatMode.Reverse
)
)
val color = Color.Magenta
val paint = remember {
Paint().apply {
style = PaintingStyle.Stroke
strokeWidth = 20f
strokeCap = StrokeCap.Round
this.asFrameworkPaint().apply {
val transparent = color
.copy(alpha = 0f)
.toArgb()
this.color = transparent
}
asFrameworkPaint().setShadowLayer(
35f * phase,
0f,
0f,
color
.copy(alpha = phase)
.toArgb()
)
}
}
// Path is what is used for drawing line on Canvas
val path = remember { Path() }
val drawModifier = Modifier
.background(Color.Black)
.fillMaxSize()
.clipToBounds()
.pointerMotionEvents(
onDown = { pointerInputChange: PointerInputChange ->
currentPosition = pointerInputChange.position
motionEvent = MotionEvent.Down
pointerInputChange.consume()
},
onMove = { pointerInputChange: PointerInputChange ->
currentPosition = pointerInputChange.position
motionEvent = MotionEvent.Move
pointerInputChange.consume()
},
onUp = { pointerInputChange: PointerInputChange ->
motionEvent = MotionEvent.Up
pointerInputChange.consume()
},
delayAfterDownInMillis = 25L
)
Canvas(modifier = drawModifier) {
when (motionEvent) {
MotionEvent.Down -> {
path.moveTo(currentPosition.x, currentPosition.y)
previousPosition = currentPosition
}
MotionEvent.Move -> {
path.quadraticTo(
previousPosition.x,
previousPosition.y,
(previousPosition.x + currentPosition.x) / 2,
(previousPosition.y + currentPosition.y) / 2
)
previousPosition = currentPosition
}
MotionEvent.Up -> {
path.lineTo(currentPosition.x, currentPosition.y)
currentPosition = Offset.Unspecified
previousPosition = currentPosition
motionEvent = MotionEvent.Idle
}
else -> Unit
}
this.drawIntoCanvas {
it.drawPath(path, paint)
// this is for blinking effect, remove it if not needed
drawPath(
color = Color.White.copy((0.7f + phase).coerceAtMost(1f)),
path = path,
style = Stroke(width = 4.dp.toPx(), cap = StrokeCap.Round, join = StrokeJoin.Round)
)
}
}
}
This and other samples about drawing on Canvas are available in this tutorial.