Anonymous
Android Compose нарисуйте путь для стирания переднего плана
Сообщение
Anonymous » 17 ноя 2024, 17:58
Я учусь, немного возясь с холстом в Composable.
Идея, которую я ищу, заключается в следующем:
Имейте цвет фона (в данном случае просто делайте градиент цветов)
Черный передний план, закрывающий фон
Нарисуйте путь на экране, который в некотором смысле сотрет черный передний план там, где он нарисован чтобы раскрыть фон.
Я успешно это сделал, но немного не понимаю, как это должно работать, поскольку я наткнулся на решение. Возможно, это все-таки неправильно, и я поступаю неправильным путем.
Ниже мой код:
Код: Выделить всё
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
//enableEdgeToEdge()
setContent {
SampleScratchPadTheme {
var reset by remember {
mutableStateOf(false)
}
Scaffold(modifier = Modifier.fillMaxSize(), floatingActionButton = {
FloatingActionButton(onClick = {
reset = true
}, shape = RoundedCornerShape(25.dp)) {
Icon(
imageVector = Icons.Default.Refresh,
contentDescription = "Restart"
)
}
}) { innerPadding ->
Column(
modifier = Modifier
.fillMaxSize()
.padding(innerPadding)
) {
Column {
Sample(reset, resetCanvas = {
reset = false
})
}
}
}
}
}
}
@Composable
private fun Sample(
reset : Boolean,
resetCanvas : () -> Unit
) {
var circles = remember {
mutableStateListOf(Offset(-100f, -100f)) // start off screen
}
var erasedPoints by remember {
mutableStateOf(Path())
}
fun changeColor(): List {
return listOf(
Color(
Random.nextInt(255),
Random.nextInt(255),
Random.nextInt(255),
255
),
Color(
Random.nextInt(255),
Random.nextInt(255),
Random.nextInt(255),
255
),
Color(
Random.nextInt(255),
Random.nextInt(255),
Random.nextInt(255),
255
)
)
}
var gradientColors by remember {
mutableStateOf(changeColor())
}
var pointerOffset by remember {
mutableStateOf(Offset(0f, 0f))
}
if(reset){
circles.clear()
erasedPoints.reset()
gradientColors = changeColor()
resetCanvas()
}
Box(modifier = Modifier
.fillMaxSize()
.clipToBounds()
.pointerInput("dragging") {
detectDragGestures(onDragStart = {
pointerOffset = it
erasedPoints.moveTo(pointerOffset.x, pointerOffset.y)
circles.add(pointerOffset)
}) { change, dragAmount ->
pointerOffset += dragAmount
erasedPoints.lineTo(pointerOffset.x, pointerOffset.y)
circles.add(pointerOffset)
}
}
.drawBehind {
drawRect(
brush = Brush.linearGradient(
colors = gradientColors,
start = Offset.Zero,
end = Offset.Infinite
)
)
}
.drawWithContent {
drawRect(Color.Black)
circles.forEach {
drawCircle(
brush = Brush.linearGradient(
colors = gradientColors,
start = Offset.Zero,
end = Offset.Infinite
), center = it,
radius = 25f,
blendMode = BlendMode.SrcIn
)
}
if (!erasedPoints.isEmpty) {
drawPath(
path = erasedPoints,
brush = Brush.linearGradient(
colors = gradientColors,
start = Offset.Zero,
end = Offset.Infinite
),
style = Stroke(
width = 50f,
cap = StrokeCap.Round,
join = StrokeJoin.Round
),
blendMode = BlendMode.SrcIn
)
}
}) {
}
}
Меня смущает следующее:
Код: Выделить всё
drawRect(Color.Black)
circles.forEach {
drawCircle(
brush = Brush.linearGradient(
colors = gradientColors,
start = Offset.Zero,
end = Offset.Infinite
), center = it,
radius = 25f,
blendMode = BlendMode.SrcIn
)
}
if (!erasedPoints.isEmpty) {
drawPath(
path = erasedPoints,
brush = Brush.linearGradient(
colors = gradientColors,
start = Offset.Zero,
end = Offset.Infinite
),
style = Stroke(
width = 50f,
cap = StrokeCap.Round,
join = StrokeJoin.Round
),
blendMode = BlendMode.SrcIn
)
}
Изображение показывает желаемые результаты, но работает только тогда, когда я добавляю drawPath и прохожу через методы drawCircle.
Если я оставлю только drawPath и удалю цикл drawCircle, я думаю, это все, что нужно, то ничего не происходит экран. Просто черный.
Если я изменю режим наложения на BlendMode.CLEAR в drawCircle, я получу следующее:
Кажется, нет необходимости использовать оба метода. Думаю, все, что мне нужно, это drawPath.
Буду очень признателен за любую информацию или рекомендации.
*************
ОТВЕТ **************
Благодаря Thracian я решил обновить свой код на случай, если кому-то еще понадобится ссылка. Я не последовал его примеру, а просто внес коррективы в две проблемы, на которые он указал. Это означает минимальное редактирование только для проверки концепции.
Код: Выделить всё
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
//enableEdgeToEdge()
setContent {
SampleScratchPadTheme {
var reset by remember {
mutableStateOf(false)
}
Scaffold(modifier = Modifier.fillMaxSize(), floatingActionButton = {
FloatingActionButton(onClick = {
reset = true
}, shape = RoundedCornerShape(25.dp)) {
Icon(
imageVector = Icons.Default.Refresh,
contentDescription = "Restart"
)
}
}) { innerPadding ->
Column(
modifier = Modifier
.fillMaxSize()
.padding(innerPadding)
) {
Column {
Sample(reset, resetCanvas = {
reset = false
})
}
}
}
}
}
}
@Composable
private fun Sample(
reset : Boolean,
resetCanvas : () -> Unit
) {
var circles = remember {
mutableStateListOf(Offset(-100f, -100f)) // start off screen
}
var erasedPoints by remember {
mutableStateOf(Path())
}
fun changeColor(): List {
return listOf(
Color(
Random.nextInt(255),
Random.nextInt(255),
Random.nextInt(255),
255
),
Color(
Random.nextInt(255),
Random.nextInt(255),
Random.nextInt(255),
255
),
Color(
Random.nextInt(255),
Random.nextInt(255),
Random.nextInt(255),
255
)
)
}
var gradientColors by remember {
mutableStateOf(changeColor())
}
var pointerOffset by remember {
mutableStateOf(Offset(0f, 0f))
}
if(reset){
circles.clear()
erasedPoints.reset()
gradientColors = changeColor()
pointerOffset = Offset.Unspecified
resetCanvas()
}
Box(modifier = Modifier
.fillMaxSize()
.clipToBounds()
.pointerInput("dragging") {
detectDragGestures(onDragStart = {
pointerOffset = it
erasedPoints.moveTo(pointerOffset.x, pointerOffset.y)
circles.add(pointerOffset)
}) { change, dragAmount ->
pointerOffset += dragAmount
erasedPoints.lineTo(pointerOffset.x, pointerOffset.y)
circles.add(pointerOffset)
}
}
.drawBehind {
drawRect(
brush = Brush.linearGradient(
colors = gradientColors,
start = Offset.Zero,
end = Offset.Infinite
)
)
}
.graphicsLayer {
compositingStrategy = CompositingStrategy.Offscreen
}
.drawWithContent {
drawRect(Color.Black)
/*circles.forEach {
drawCircle(
brush = Brush.linearGradient(
colors = gradientColors,
start = Offset.Zero,
end = Offset.Infinite
), center = it,
radius = 25f,
blendMode = BlendMode.SrcIn
)
}*/
if(pointerOffset != Offset.Unspecified){
if (!erasedPoints.isEmpty) {
drawPath(
path = erasedPoints,
color = Color.Transparent,
style = Stroke(
width = 50f,
cap = StrokeCap.Round,
join = StrokeJoin.Round
),
blendMode = BlendMode.Clear
)
}
}
}) {
}
}
Подробнее здесь:
https://stackoverflow.com/questions/791 ... foreground
1731855514
Anonymous
Я учусь, немного возясь с холстом в Composable. Идея, которую я ищу, заключается в следующем: [list] [*]Имейте цвет фона (в данном случае просто делайте градиент цветов) [*]Черный передний план, закрывающий фон [*]Нарисуйте путь на экране, который в некотором смысле сотрет черный передний план там, где он нарисован чтобы раскрыть фон. [/list] Я успешно это сделал, но немного не понимаю, как это должно работать, поскольку я наткнулся на решение. Возможно, это все-таки неправильно, и я поступаю неправильным путем. Ниже мой код: [code]class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) //enableEdgeToEdge() setContent { SampleScratchPadTheme { var reset by remember { mutableStateOf(false) } Scaffold(modifier = Modifier.fillMaxSize(), floatingActionButton = { FloatingActionButton(onClick = { reset = true }, shape = RoundedCornerShape(25.dp)) { Icon( imageVector = Icons.Default.Refresh, contentDescription = "Restart" ) } }) { innerPadding -> Column( modifier = Modifier .fillMaxSize() .padding(innerPadding) ) { Column { Sample(reset, resetCanvas = { reset = false }) } } } } } } @Composable private fun Sample( reset : Boolean, resetCanvas : () -> Unit ) { var circles = remember { mutableStateListOf(Offset(-100f, -100f)) // start off screen } var erasedPoints by remember { mutableStateOf(Path()) } fun changeColor(): List { return listOf( Color( Random.nextInt(255), Random.nextInt(255), Random.nextInt(255), 255 ), Color( Random.nextInt(255), Random.nextInt(255), Random.nextInt(255), 255 ), Color( Random.nextInt(255), Random.nextInt(255), Random.nextInt(255), 255 ) ) } var gradientColors by remember { mutableStateOf(changeColor()) } var pointerOffset by remember { mutableStateOf(Offset(0f, 0f)) } if(reset){ circles.clear() erasedPoints.reset() gradientColors = changeColor() resetCanvas() } Box(modifier = Modifier .fillMaxSize() .clipToBounds() .pointerInput("dragging") { detectDragGestures(onDragStart = { pointerOffset = it erasedPoints.moveTo(pointerOffset.x, pointerOffset.y) circles.add(pointerOffset) }) { change, dragAmount -> pointerOffset += dragAmount erasedPoints.lineTo(pointerOffset.x, pointerOffset.y) circles.add(pointerOffset) } } .drawBehind { drawRect( brush = Brush.linearGradient( colors = gradientColors, start = Offset.Zero, end = Offset.Infinite ) ) } .drawWithContent { drawRect(Color.Black) circles.forEach { drawCircle( brush = Brush.linearGradient( colors = gradientColors, start = Offset.Zero, end = Offset.Infinite ), center = it, radius = 25f, blendMode = BlendMode.SrcIn ) } if (!erasedPoints.isEmpty) { drawPath( path = erasedPoints, brush = Brush.linearGradient( colors = gradientColors, start = Offset.Zero, end = Offset.Infinite ), style = Stroke( width = 50f, cap = StrokeCap.Round, join = StrokeJoin.Round ), blendMode = BlendMode.SrcIn ) } }) { } } [/code] Меня смущает следующее: [code]drawRect(Color.Black) circles.forEach { drawCircle( brush = Brush.linearGradient( colors = gradientColors, start = Offset.Zero, end = Offset.Infinite ), center = it, radius = 25f, blendMode = BlendMode.SrcIn ) } if (!erasedPoints.isEmpty) { drawPath( path = erasedPoints, brush = Brush.linearGradient( colors = gradientColors, start = Offset.Zero, end = Offset.Infinite ), style = Stroke( width = 50f, cap = StrokeCap.Round, join = StrokeJoin.Round ), blendMode = BlendMode.SrcIn ) } [/code] [img]https://i.sstatic.net/BOnytvvz.png[/img] Изображение показывает желаемые результаты, но работает только тогда, когда я добавляю drawPath и прохожу через методы drawCircle. Если я оставлю только drawPath и удалю цикл drawCircle, я думаю, это все, что нужно, то ничего не происходит экран. Просто черный. Если я изменю режим наложения на BlendMode.CLEAR в drawCircle, я получу следующее: [img]https://i.sstatic.net/5W7EbZHO.png[/img] Кажется, нет необходимости использовать оба метода. Думаю, все, что мне нужно, это drawPath. Буду очень признателен за любую информацию или рекомендации. ************* [b]ОТВЕТ [/b] ************** Благодаря Thracian я решил обновить свой код на случай, если кому-то еще понадобится ссылка. Я не последовал его примеру, а просто внес коррективы в две проблемы, на которые он указал. Это означает минимальное редактирование только для проверки концепции. [code]class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) //enableEdgeToEdge() setContent { SampleScratchPadTheme { var reset by remember { mutableStateOf(false) } Scaffold(modifier = Modifier.fillMaxSize(), floatingActionButton = { FloatingActionButton(onClick = { reset = true }, shape = RoundedCornerShape(25.dp)) { Icon( imageVector = Icons.Default.Refresh, contentDescription = "Restart" ) } }) { innerPadding -> Column( modifier = Modifier .fillMaxSize() .padding(innerPadding) ) { Column { Sample(reset, resetCanvas = { reset = false }) } } } } } } @Composable private fun Sample( reset : Boolean, resetCanvas : () -> Unit ) { var circles = remember { mutableStateListOf(Offset(-100f, -100f)) // start off screen } var erasedPoints by remember { mutableStateOf(Path()) } fun changeColor(): List { return listOf( Color( Random.nextInt(255), Random.nextInt(255), Random.nextInt(255), 255 ), Color( Random.nextInt(255), Random.nextInt(255), Random.nextInt(255), 255 ), Color( Random.nextInt(255), Random.nextInt(255), Random.nextInt(255), 255 ) ) } var gradientColors by remember { mutableStateOf(changeColor()) } var pointerOffset by remember { mutableStateOf(Offset(0f, 0f)) } if(reset){ circles.clear() erasedPoints.reset() gradientColors = changeColor() pointerOffset = Offset.Unspecified resetCanvas() } Box(modifier = Modifier .fillMaxSize() .clipToBounds() .pointerInput("dragging") { detectDragGestures(onDragStart = { pointerOffset = it erasedPoints.moveTo(pointerOffset.x, pointerOffset.y) circles.add(pointerOffset) }) { change, dragAmount -> pointerOffset += dragAmount erasedPoints.lineTo(pointerOffset.x, pointerOffset.y) circles.add(pointerOffset) } } .drawBehind { drawRect( brush = Brush.linearGradient( colors = gradientColors, start = Offset.Zero, end = Offset.Infinite ) ) } .graphicsLayer { compositingStrategy = CompositingStrategy.Offscreen } .drawWithContent { drawRect(Color.Black) /*circles.forEach { drawCircle( brush = Brush.linearGradient( colors = gradientColors, start = Offset.Zero, end = Offset.Infinite ), center = it, radius = 25f, blendMode = BlendMode.SrcIn ) }*/ if(pointerOffset != Offset.Unspecified){ if (!erasedPoints.isEmpty) { drawPath( path = erasedPoints, color = Color.Transparent, style = Stroke( width = 50f, cap = StrokeCap.Round, join = StrokeJoin.Round ), blendMode = BlendMode.Clear ) } } }) { } } [/code] Подробнее здесь: [url]https://stackoverflow.com/questions/79196978/android-compose-draw-path-to-erase-foreground[/url]