Skip to content

Commit 517edc3

Browse files
author
iOrchid
committed
canvas绘制path的操作符
1 parent 4ad798e commit 517edc3

File tree

4 files changed

+854
-1
lines changed

4 files changed

+854
-1
lines changed

compose/src/main/java/org/zhiwei/compose/model/Screen.kt

+6-1
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import org.zhiwei.compose.screen.gesture.TapDragGestures_Screen
2222
import org.zhiwei.compose.screen.gesture.TouchImage_Screen
2323
import org.zhiwei.compose.screen.gesture.TransformGestures_Screen
2424
import org.zhiwei.compose.screen.graphics.CanvasBasic_Screen
25+
import org.zhiwei.compose.screen.graphics.CanvasPathOperations_Screen
2526
import org.zhiwei.compose.screen.graphics.CanvasPath_Screen
2627
import org.zhiwei.compose.screen.layout_state.ConstraintLayout_Screen
2728
import org.zhiwei.compose.screen.layout_state.Constraints_Screen
@@ -202,8 +203,12 @@ internal object GraphicsScreenUIs {
202203
) { CanvasBasic_Screen(modifier) },
203204
CourseItemModel(
204205
"CanvasPath",
205-
"canvas也可以用于绘制不规则图形,根据path路径的设置,还可以应用不同的线条风格,以及Blend层叠交集方式"
206+
"canvas也可以用于绘制不规则图形,根据path路径的设置,还可以应用不同的线条风格。"
206207
) { CanvasPath_Screen(modifier) },
208+
CourseItemModel(
209+
"CanvasPathOps",
210+
"canvas绘制path,不同的图形使用交互方式不同,表现层叠交集效果。"
211+
) { CanvasPathOperations_Screen(modifier) },
207212
)
208213
}
209214

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
package org.zhiwei.compose.screen.graphics
2+
3+
import androidx.compose.foundation.Canvas
4+
import androidx.compose.foundation.background
5+
import androidx.compose.foundation.layout.Column
6+
import androidx.compose.foundation.layout.fillMaxSize
7+
import androidx.compose.foundation.layout.fillMaxWidth
8+
import androidx.compose.foundation.layout.height
9+
import androidx.compose.foundation.layout.padding
10+
import androidx.compose.foundation.rememberScrollState
11+
import androidx.compose.foundation.verticalScroll
12+
import androidx.compose.material3.Slider
13+
import androidx.compose.material3.Text
14+
import androidx.compose.runtime.Composable
15+
import androidx.compose.runtime.getValue
16+
import androidx.compose.runtime.mutableFloatStateOf
17+
import androidx.compose.runtime.mutableStateOf
18+
import androidx.compose.runtime.remember
19+
import androidx.compose.runtime.setValue
20+
import androidx.compose.ui.Modifier
21+
import androidx.compose.ui.draw.clipToBounds
22+
import androidx.compose.ui.draw.shadow
23+
import androidx.compose.ui.graphics.Color
24+
import androidx.compose.ui.graphics.Path
25+
import androidx.compose.ui.graphics.PathEffect
26+
import androidx.compose.ui.graphics.PathOperation
27+
import androidx.compose.ui.graphics.drawscope.Stroke
28+
import androidx.compose.ui.tooling.preview.Preview
29+
import androidx.compose.ui.unit.dp
30+
import org.zhiwei.compose.ui.widget.Title_Desc_Text
31+
import org.zhiwei.compose.ui.widget.Title_Sub_Text
32+
import org.zhiwei.compose.ui.widget.Title_Text
33+
import kotlin.math.roundToInt
34+
35+
@Composable
36+
internal fun CanvasPathOperations_Screen(modifier: Modifier = Modifier) {
37+
Column(
38+
modifier
39+
.fillMaxWidth()
40+
.verticalScroll(rememberScrollState())
41+
) {
42+
Title_Text(title = "Clip Path/Rect")
43+
Title_Sub_Text(title = "示例演示path的操作符,DrawScope的clipPath/clipRect使用不同的交互模式而形成不同的UI显示效果")
44+
Title_Desc_Text(desc = "path.op Stroke")
45+
PathOpStroke()
46+
}
47+
48+
}
49+
50+
@Composable
51+
private fun PathOpStroke() {
52+
53+
var sides1 by remember { mutableFloatStateOf(5f) }
54+
var radius1 by remember { mutableFloatStateOf(300f) }
55+
56+
var sides2 by remember { mutableFloatStateOf(7f) }
57+
var radius2 by remember { mutableFloatStateOf(300f) }
58+
59+
var operation by remember { mutableStateOf(PathOperation.Difference) }
60+
61+
val newPath = remember { Path() }
62+
63+
Canvas(modifier = canvasModifier) {
64+
val canvasWidth = size.width
65+
val canvasHeight = size.height
66+
67+
val cx1 = canvasWidth / 3
68+
val cx2 = canvasWidth * 2 / 3
69+
val cy = canvasHeight / 2
70+
71+
72+
val path1 = createPolygonPath(cx1, cy, sides1.roundToInt(), radius1)
73+
val path2 = createPolygonPath(cx2, cy, sides2.roundToInt(), radius2)
74+
75+
drawPath(
76+
color = Color.Red,
77+
path = path1,
78+
style = Stroke(
79+
width = 2.dp.toPx(),
80+
pathEffect = PathEffect.dashPathEffect(floatArrayOf(10f, 10f))
81+
)
82+
)
83+
84+
drawPath(
85+
color = Color.Blue,
86+
path = path2,
87+
style = Stroke(
88+
width = 2.dp.toPx(),
89+
pathEffect = PathEffect.dashPathEffect(floatArrayOf(10f, 10f))
90+
)
91+
)
92+
93+
// We apply operation to path1 and path2 and setting this new path to our newPath
94+
/*
95+
Set this path to the result of applying the Op to the two specified paths.
96+
The resulting path will be constructed from non-overlapping contours.
97+
The curve order is reduced where possible so that cubics may be turned into quadratics,
98+
and quadratics maybe turned into lines.
99+
*/
100+
newPath.op(path1, path2, operation = operation)
101+
102+
drawPath(
103+
color = Color.Green,
104+
path = newPath,
105+
style = Stroke(
106+
width = 4.dp.toPx(),
107+
)
108+
)
109+
}
110+
111+
Column(modifier = Modifier.padding(horizontal = 20.dp, vertical = 4.dp)) {
112+
113+
ExposedSelectionMenu(title = "Path Operation",
114+
index = when (operation) {
115+
PathOperation.Difference -> 0
116+
PathOperation.Intersect -> 1
117+
PathOperation.Union -> 2
118+
PathOperation.Xor -> 3
119+
else -> 4
120+
},
121+
options = listOf("Difference", "Intersect", "Union", "Xor", "ReverseDifference"),
122+
onSelected = {
123+
operation = when (it) {
124+
0 -> PathOperation.Difference
125+
1 -> PathOperation.Intersect
126+
2 -> PathOperation.Union
127+
3 -> PathOperation.Xor
128+
else -> PathOperation.ReverseDifference
129+
}
130+
}
131+
)
132+
133+
Text(text = "Sides left: ${sides1.roundToInt()}")
134+
Slider(
135+
value = sides1,
136+
onValueChange = { sides1 = it },
137+
valueRange = 3f..12f,
138+
steps = 10
139+
)
140+
Text(text = "radius left: ${radius1.roundToInt()}")
141+
Slider(
142+
value = radius1,
143+
onValueChange = { radius1 = it },
144+
valueRange = 100f..500f
145+
)
146+
147+
Text(text = "Sides right: ${sides2.roundToInt()}")
148+
Slider(
149+
value = sides2,
150+
onValueChange = { sides2 = it },
151+
valueRange = 3f..12f,
152+
steps = 10
153+
)
154+
Text(text = "radius right: ${radius2.roundToInt()}")
155+
Slider(
156+
value = radius2,
157+
onValueChange = { radius2 = it },
158+
valueRange = 100f..500f
159+
)
160+
}
161+
}
162+
163+
164+
private val canvasModifier = Modifier
165+
.padding(8.dp)
166+
.shadow(1.dp)
167+
.background(Color.White)
168+
.fillMaxSize()
169+
.clipToBounds()
170+
.height(300.dp)
171+
172+
@Preview
173+
@Composable
174+
private fun PreviewCanvasPathOps() {
175+
CanvasPathOperations_Screen()
176+
}

0 commit comments

Comments
 (0)