【问题标题】:How to get transparent while erasing the canvas in Jetpack Compose , now I'm getting white color?如何在 Jetpack Compose 中擦除画布时变得透明,现在我变成了白色?
【发布时间】:2022-07-19 00:10:15
【问题描述】:

如何使画布的某些部分透明?我希望用户能够擦除像这样的照片的一部分 link 显示是透明的。我的画布代码:

Canvas(
    modifier = modifier
        .background(Color.Transparent)
) {
    with(drawContext.canvas.nativeCanvas) {
        val checkPoint = saveLayer(null, null)
        drawImage(
            image = bitmap,
            srcSize = IntSize(bitmap.width, bitmap.height),
            dstSize = IntSize(canvasWidth, canvasHeight)
        )
        drawPath(
            path = erasePath,
            style = Stroke(
                width = 30f,
                cap = StrokeCap.Round,
                join = StrokeJoin.Round
            ),
            blendMode = BlendMode.Clear,
            color = Color.Transparent,
        )
        restoreToCount(checkPoint)
    }
}

【问题讨论】:

    标签: android kotlin canvas android-jetpack-compose transparent


    【解决方案1】:

    你得到的透明是Color(0x00000000),你得到的白色是你的背景颜色,即使你的 Canvas 有透明背景,你的根或父 Composable 的颜色是白色。

    您需要先绘制棋盘格布局或棋盘格图像,在层内您应该使用BlendMode.Clear绘制图像和路径

    val width = this.size.width
    val height = this.size.height
    
    val checkerWidth = 10.dp.toPx()
    val checkerHeight = 10.dp.toPx()
    
    val horizontalSteps = (width / checkerWidth).toInt()
    val verticalSteps = (height / checkerHeight).toInt()
    
    for (y in 0..verticalSteps) {
        for (x in 0..horizontalSteps) {
            val isGrayTile = ((x + y) % 2 == 1)
            drawRect(
                color = if (isGrayTile) Color.LightGray else Color.White,
                topLeft = Offset(x * checkerWidth, y * checkerHeight),
                size = Size(checkerWidth, checkerHeight)
            )
        }
    }
    
    val space = 20.dp.roundToPx()
    
    with(drawContext.canvas.nativeCanvas) {
        val checkPoint = saveLayer(null, null)
    
        // Destination
        drawImage(
            image = dstBitmap,
            dstOffset = IntOffset(
                space / 2,
                space / 2
            ),
            dstSize = IntSize(
                canvasWidth - space, canvasHeight - space
            )
        )
    
        // Source
        drawPath(
            color = Color.Transparent,
            path = erasePath,
            style = Stroke(
                width = 30f,
                cap = StrokeCap.Round,
                join = StrokeJoin.Round
            ),
            blendMode = BlendMode.Clear
        )
        restoreToCount(checkPoint)
    }
    

    全面实施

    @Composable
    private fun MyImageDrawer(modifier: Modifier) {
    
        // This is the image to draw onto
        val dstBitmap = ImageBitmap.imageResource(id = R.drawable.landscape1)
    
    
        // Path used for erasing. In this example erasing is faked by drawing with canvas color
        // above draw path.
        val erasePath = remember { Path() }
    
        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 drawModifier = modifier
            .pointerMotionEvents(Unit,
                onDown = { pointerInputChange ->
                    motionEvent = MotionEvent.Down
                    currentPosition = pointerInputChange.position
                    pointerInputChange.consume()
                },
                onMove = { pointerInputChange ->
                    motionEvent = MotionEvent.Move
                    currentPosition = pointerInputChange.position
                    pointerInputChange.consume()
                },
                onUp = { pointerInputChange ->
                    motionEvent = MotionEvent.Up
                    pointerInputChange.consume()
                }
            )
    
        Canvas(modifier = drawModifier) {
    
            val canvasWidth = size.width.roundToInt()
            val canvasHeight = size.height.roundToInt()
    
            // Draw or erase depending on erase mode is active or not
    
            when (motionEvent) {
    
                MotionEvent.Down -> {
                    erasePath.moveTo(currentPosition.x, currentPosition.y)
                    previousPosition = currentPosition
    
                }
                MotionEvent.Move -> {
    
                    erasePath.quadraticBezierTo(
                        previousPosition.x,
                        previousPosition.y,
                        (previousPosition.x + currentPosition.x) / 2,
                        (previousPosition.y + currentPosition.y) / 2
    
                    )
                    previousPosition = currentPosition
                }
    
                MotionEvent.Up -> {
                    erasePath.lineTo(currentPosition.x, currentPosition.y)
                    currentPosition = Offset.Unspecified
                    previousPosition = currentPosition
                    motionEvent = MotionEvent.Idle
                }
                else -> Unit
            }
    
    
            val width = this.size.width
            val height = this.size.height
    
            val checkerWidth = 10.dp.toPx()
    
            val checkerHeight = 10.dp.toPx()
    
            val horizontalSteps = (width / checkerWidth).toInt()
            val verticalSteps = (height / checkerHeight).toInt()
    
            for (y in 0..verticalSteps) {
                for (x in 0..horizontalSteps) {
                    val isGrayTile = ((x + y) % 2 == 1)
                    drawRect(
                        color = if (isGrayTile) Color.LightGray else Color.White,
                        topLeft = Offset(x * checkerWidth, y * checkerHeight),
                        size = Size(checkerWidth, checkerHeight)
                    )
                }
            }
    
            val space = 20.dp.roundToPx()
    
            with(drawContext.canvas.nativeCanvas) {
                val checkPoint = saveLayer(null, null)
    
                // Destination
                drawImage(
                    image = dstBitmap,
                    dstOffset = IntOffset(
                        space / 2,
                        space / 2
                    ),
                    dstSize = IntSize(
                        canvasWidth - space, canvasHeight - space
                    )
                )
    
                // Source
                drawPath(
                    color = Color.Transparent,
                    path = erasePath,
                    style = Stroke(
                        width = 30f,
                        cap = StrokeCap.Round,
                        join = StrokeJoin.Round
                    ),
                    blendMode = BlendMode.Clear
                )
                restoreToCount(checkPoint)
            }
        }
    }
    

    结果

    【讨论】:

    • 感谢您的回答,您知道如何将其保存为透明吗?
    • 比如你可以使用这个库截取Canvas的截图,它会被转换为Bitmap,然后你需要将它保存到物理文件夹中。 github.com/SmartToolFactory/Compose-Screenshot
    • 谢谢,它成功了,但你知道灰色部分也与图像一起保存,我只想要图像部分,你能帮我吗?
    • 每个屏幕截图库都通过捕获屏幕上的视图来实现这一点。您可以在屏幕截图之前停止绘制灰色区域,但是您将在问题中看到空白。如果您确实想要保存图像。你应该把它作为另一个问题来问,因为在 cmets 中回答并不容易,而且超出了这个问题的范围。您需要使用 androidx.compose.ui.graphics.Canvas 接受位图作为参数和路径和混合模式
    • 您可以在这里查看我的问题以了解想法stackoverflow.com/questions/72168588/…
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-11-14
    • 1970-01-01
    • 2022-01-03
    • 1970-01-01
    • 2015-01-28
    • 1970-01-01
    相关资源
    最近更新 更多