Compose笔记(二十四)--Canvas
这一节主要了解一下Compose中Canvas,在Jetpack Compose中,Canvas是用于创建自定义绘图和动画的核心组件,它提供了强大的API来绘制各种图形、图像和效果。简单总给如下:
API
1.1 基本图形
// 绘制圆形
drawCircle(color = Color.Red, radius = 50f)// 绘制矩形
drawRect(color = Color.Blue, size = Size(100f, 150f))// 绘制圆角矩形
drawRoundRect(color = Color.Green,cornerRadius = CornerRadius(10f, 10f)
)// 绘制椭圆
drawOval(color = Color.Yellow, topLeft = Offset(50f, 50f))// 绘制线条
drawLine(color = Color.Black,start = Offset(0f, 0f),end = Offset(100f, 100f),strokeWidth = 5f
)
1.2 路径 (Path)
val path = Path().apply {moveTo(50f, 50f)lineTo(150f, 50f)lineTo(100f, 150f)close() // 闭合路径
}drawPath(path = path,color = Color.Magenta,style = Fill // 填充
)// 或描边
drawPath(path = path,color = Color.Black,style = Stroke(width = 3f)
)
1.3 文本
drawIntoCanvas { canvas ->val paint = Paint().apply {color = Color.BlacktextSize = 24.sp.toPx()textAlign = android.graphics.Paint.Align.CENTER}canvas.nativeCanvas.drawText("Hello Canvas",size.width / 2,size.height / 2,paint)
}
2. 样式控制
2.1 画笔样式
// 填充样式
drawCircle(color = Color.Red,radius = 50f,style = Fill
)// 描边样式
drawCircle(color = Color.Blue,radius = 60f,style = Stroke(width = 5f)
)// 描边+圆角
drawCircle(color = Color.Green,radius = 70f,style = Stroke(width = 10f,cap = StrokeCap.Round, // 圆角端点join = StrokeJoin.Round // 圆角连接)
)
2.2 渐变
// 线性渐变
val brush = LinearGradientBrush(start = Offset(0f, 0f),end = Offset(size.width, size.height),colors = listOf(Color.Red, Color.Blue)
)drawRect(brush = brush, size = size)// 径向渐变
val radialBrush = RadialGradientBrush(center = Offset(size.width / 2, size.height / 2),radius = size.minDimension / 2,colors = listOf(Color.Yellow, Color.Transparent)
)drawCircle(brush = radialBrush, radius = size.minDimension / 2)
3. 变换操作
3.1 平移、旋转、缩放
withTransform({translate(left = 50f, top = 50f) // 平移rotate(degrees = 45f) // 旋转scale(scaleX = 1.5f, scaleY = 1.5f) // 缩放
}) {drawCircle(color = Color.Red, radius = 30f)
}
3.2 裁剪
// 裁剪为圆形区域
clipPath(Path().apply {addOval(Rect(left = size.width / 4,top = size.height / 4,right = size.width * 3 / 4,bottom = size.height * 3 / 4))
}) {drawRect(color = Color.Blue, size = size)
}
4. 组合效果
4.1 阴影
drawCircle(color = Color.Red,radius = 50f,shadowColor = Color.Black.copy(alpha = 0.5f),blurRadius = 10f,style = Fill
)
4.2 混合模式
drawIntoCanvas { canvas ->canvas.saveLayer(Rect(0f, 0f, size.width, size.height), null) // 绘制底层drawCircle(color = Color.Red, radius = 60f) // 设置混合模式canvas.nativeCanvas.compositeMode = PorterDuff.Mode.SRC_IN // 绘制上层drawCircle(color = Color.Blue, radius = 40f)canvas.restore()
}
栗子:
import androidx.compose.foundation.Canvas
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp@Composable
fun GridBackground(cellSize: Dp = 30.dp,lineColor: Color = Color.LightGray.copy(alpha = 0.5f),lineWidth: Float = 1f
) {val cellPx = cellSize.valueCanvas(modifier = Modifier.fillMaxSize()) {val width = size.widthval height = size.height// 绘制水平线for (y in 0..height.toInt() step cellPx.toInt()) {drawLine(color = lineColor,start = Offset(0f, y.toFloat()),end = Offset(width, y.toFloat()),strokeWidth = lineWidth)}// 绘制垂直线for (x in 0..width.toInt() step cellPx.toInt()) {drawLine(color = lineColor,start = Offset(x.toFloat(), 0f),end = Offset(x.toFloat(), height),strokeWidth = lineWidth)}// 绘制对角线drawLine(color = lineColor,start = Offset(0f, 0f),end = Offset(width, height),strokeWidth = lineWidth)drawLine(color = lineColor,start = Offset(width, 0f),end = Offset(0f, height),strokeWidth = lineWidth)}
}
import androidx.compose.animation.core.*
import androidx.compose.foundation.Canvas
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.tooling.preview.Preview
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch@Composable
fun RippleEffect() {val scope = rememberCoroutineScope()val ripples = remember { mutableStateListOf<Ripple>() }LaunchedEffect(Unit) {while (true) {delay(1000)val ripple = Ripple()ripples.add(ripple)// 在协程中启动动画scope.launch {ripple.animatable.animateTo(targetValue = 1f,animationSpec = tween(2000, easing = LinearEasing))}if (ripples.size > 3) {ripples.removeAt(0)}}}Canvas(modifier = Modifier.fillMaxSize()) {val center = Offset(size.width / 2, size.height / 2)ripples.forEach { ripple ->val radius = ripple.animatable.value * size.minDimension * 0.3fval alpha = 1f - ripple.animatable.valueif(radius>0) {drawCircle(brush = Brush.radialGradient(colors = listOf(Color(0xFF42A5F5).copy(alpha = alpha * 0.7f),Color(0xFF42A5F5).copy(alpha = 0f)),center = center,radius = radius),radius = radius,center = center)}}}
}
注意:
1 避免阻塞UI线程,复杂计算应在后台协程中进行,只将结果传递给Canvas;
2 动画必须在协程中运行,Animatable、animate*AsState需在协程作用域内启动;
3 避免在绘制过程中创建对象,每次重组都会调用draw方法,应避免在其中创建新对象;