[compose] 仿刮刮乐效果

需求

下班路上新开了一家彩票店,路过时总是心痒,本着小D怡情的心态,偶尔去刮几张,可是随着时间久了,发现也花了不少钱,看网上有人开发电子木鱼,突然奇想,为什么不做一张电子彩票。

分析

传统View,网上有很多解决方案,大多数是通过混合模式进行两个图层的合并。

大致思路:

1、使用onDraw()方法中的Canvas绘制底层中奖层

2、在上面绘制一个蒙版层Bitmap, 在蒙版层Bitmap里面,放置一个新的Canvas

3、绘制一个灰色的矩阵,绘制一个path,将paint的Xfermode设置为 PorterDuff.Mode.DST_IN 4、手指移动时,更新Path路径

Compose实现

1、通过实现DrawModifier,重写draw() 方法

2、绘制原始内容层,drawContent() 3、绘制蒙版和手势层,

kotlin 复制代码
//配置画笔 blendMode = Xfermode
private val pathPaint = Paint().apply {
    alpha = 0f
    style = PaintingStyle.Stroke
    strokeWidth = 70f
    blendMode = BlendMode.SrcIn
    strokeJoin = StrokeJoin.Round
    strokeCap = StrokeCap.Round
} 

drawIntoCanvas {
    //设置画布大小尺寸
    val rect = Rect(0f, 0f, size.width, size.height)
    //从原始画布层,转换一个新的画布层
    it.saveLayer(rect, layerPaint)
    //设置新画布大小尺寸
    it.drawRect(rect, layerPaint)
    startPath.lineTo(moveOffset.x, moveOffset.y)
    //绘制手指移动path
    it.drawPath(startPath, pathPaint)
    it.restore()
}

完整代码

kotlin 复制代码
fun ScrapeLayePage(){
    var linePath by remember { mutableStateOf(Offset.Zero) }
    val path by remember { mutableStateOf(Path()) }
    Column(modifier = Modifier
        .fillMaxWidth()
        .pointerInput("dragging") {
            awaitEachGesture {
                while (true) {
                    val event = awaitPointerEvent()
                    when (event.type) {
                        //按住时,更新起始点
                        Press -> {
                            path.moveTo(
                                event.changes.first().position.x,
                                event.changes.first().position.y
                            )
                        }
                        //移动时,更新起始点 移动时,记录路径path
                        Move -> {
                            linePath = event.changes.first().position
                        }
                    }
                }
            }
        }
        .scrapeLayer(path, linePath)
    ) {
        Image(
            modifier = Modifier.fillMaxWidth(),
            painter = painterResource(id = R.mipmap.cat),
            contentDescription = ""
        )
        Text(text = "这是一只可爱的猫咪~~")
    }
}

fun Modifier.scrapeLayer(startPath: Path, moveOffset: Offset) =
    this.then(ScrapeLayer(startPath, moveOffset))

class ScrapeLayer(private val startPath: Path, private val moveOffset: Offset) : DrawModifier {

    private val pathPaint = Paint().apply {
        alpha = 0f
        style = PaintingStyle.Stroke
        strokeWidth = 70f
        blendMode = BlendMode.SrcIn
        strokeJoin = StrokeJoin.Round
        strokeCap = StrokeCap.Round
    }

    private val layerPaint = Paint().apply {
        color = Color.Gray
    }

    override fun ContentDrawScope.draw() {
        drawContent()
        drawIntoCanvas {
            val rect = Rect(0f, 0f, size.width, size.height)
            //从当前画布,裁切一个新的图层
            it.saveLayer(rect, layerPaint)
            it.drawRect(rect, layerPaint)
            startPath.lineTo(moveOffset.x, moveOffset.y)
            it.drawPath(startPath, pathPaint)
            it.restore()
        }
    }
}

参考资料

相关推荐
然我17 分钟前
不用 Redux 也能全局状态管理?看我用 useReducer+Context 搞个 Todo 应用
前端·javascript·react.js
前端小巷子21 分钟前
Web 实时通信:从短轮询到 WebSocket
前端·javascript·面试
神仙别闹25 分钟前
基于C#+SQL Server实现(Web)学生选课管理系统
前端·数据库·c#
web前端神器31 分钟前
指定阿里镜像原理
前端
枷锁—sha36 分钟前
【DVWA系列】——CSRF——Medium详细教程
android·服务器·前端·web安全·网络安全·csrf
枷锁—sha38 分钟前
跨站请求伪造漏洞(CSRF)详解
运维·服务器·前端·web安全·网络安全·csrf
群联云防护小杜1 小时前
深度隐匿源IP:高防+群联AI云防护防绕过实战
运维·服务器·前端·网络·人工智能·网络协议·tcp/ip
汉得数字平台1 小时前
【鲲苍提效】全面洞察用户体验,助力打造高性能前端应用
前端·前端监控
花海如潮淹1 小时前
前端性能追踪工具:用户体验的毫秒战争
前端·笔记·ux
_丿丨丨_6 小时前
XSS(跨站脚本攻击)
前端·网络·xss