Skip to content

Commit 4f63d46

Browse files
committed
library: Optimize ColorPicker Component
1 parent 2d6ebf6 commit 4f63d46

File tree

1 file changed

+110
-28
lines changed

1 file changed

+110
-28
lines changed

miuix/src/commonMain/kotlin/top/yukonga/miuix/kmp/basic/ColorPicker.kt

Lines changed: 110 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,13 @@ import androidx.compose.runtime.setValue
2121
import androidx.compose.ui.Alignment
2222
import androidx.compose.ui.Modifier
2323
import androidx.compose.ui.draw.clip
24+
import androidx.compose.ui.geometry.Offset
25+
import androidx.compose.ui.geometry.Size
2426
import androidx.compose.ui.graphics.Brush
2527
import androidx.compose.ui.graphics.Color
2628
import androidx.compose.ui.graphics.drawscope.DrawScope
29+
import androidx.compose.ui.graphics.drawscope.scale
30+
import androidx.compose.ui.graphics.drawscope.translate
2731
import androidx.compose.ui.input.pointer.pointerInput
2832
import androidx.compose.ui.layout.onGloballyPositioned
2933
import androidx.compose.ui.platform.LocalDensity
@@ -52,7 +56,10 @@ fun ColorPicker(
5256
var currentValue by remember { mutableStateOf(0f) }
5357
var currentAlpha by remember { mutableStateOf(1f) }
5458

55-
// Set initial HSV values only once
59+
val selectedColor = remember(currentHue, currentSaturation, currentValue, currentAlpha) {
60+
Color.hsv(currentHue, currentSaturation, currentValue, currentAlpha)
61+
}
62+
5663
LaunchedEffect(initialColor, initialSetup) {
5764
if (initialSetup) {
5865
val hsv = FloatArray(3)
@@ -70,14 +77,9 @@ fun ColorPicker(
7077
}
7178
}
7279

73-
// Current selected color
74-
val selectedColor = Color.hsv(currentHue, currentSaturation, currentValue, currentAlpha)
75-
76-
// Track previous color to prevent recomposition loops
7780
var previousColor by remember { mutableStateOf(selectedColor) }
7881

79-
// Only trigger callback when colors actually change from user interaction
80-
LaunchedEffect(currentHue, currentSaturation, currentValue, currentAlpha) {
82+
LaunchedEffect(selectedColor) {
8183
if (!initialSetup && selectedColor != previousColor) {
8284
previousColor = selectedColor
8385
onColorChanged(selectedColor)
@@ -89,7 +91,7 @@ fun ColorPicker(
8991
modifier = Modifier
9092
.fillMaxWidth()
9193
.height(26.dp)
92-
.clip(SmoothRoundedCornerShape(13.dp))
94+
.clip(SmoothRoundedCornerShape(50.dp))
9395
.background(selectedColor)
9496
)
9597

@@ -132,7 +134,6 @@ fun ColorPicker(
132134
)
133135
}
134136

135-
136137
/**
137138
* A [HueSlider] component for selecting the hue of a color.
138139
*
@@ -148,11 +149,10 @@ fun HueSlider(
148149
value = currentHue / 360f,
149150
onValueChanged = onHueChanged,
150151
drawBrush = {
151-
val hueColors = List(36) { i ->
152-
Color.hsv(i * 10f, 1f, 1f)
153-
}
154152
drawRect(brush = Brush.horizontalGradient(hueColors))
155153
},
154+
startEdgeColor = Color.hsv(0f, 1f, 1f, 1f),
155+
endEdgeColor = Color.hsv(359f, 1f, 1f, 1f),
156156
modifier = Modifier.fillMaxWidth()
157157
)
158158
}
@@ -182,6 +182,8 @@ fun SaturationSlider(
182182
)
183183
drawRect(brush = brush)
184184
},
185+
startEdgeColor = Color.hsv(currentHue, 0f, 1f, 1f),
186+
endEdgeColor = Color.hsv(currentHue, 1f, 1f, 1f),
185187
modifier = Modifier.fillMaxWidth()
186188
)
187189
}
@@ -214,6 +216,8 @@ fun ValueSlider(
214216
)
215217
drawRect(brush = brush)
216218
},
219+
startEdgeColor = Color.Black,
220+
endEdgeColor = Color.hsv(currentHue, currentSaturation, 1f, 1f),
217221
modifier = Modifier.fillMaxWidth()
218222
)
219223
}
@@ -235,19 +239,54 @@ fun AlphaSlider(
235239
currentAlpha: Float,
236240
onAlphaChanged: (Float) -> Unit,
237241
) {
242+
val baseColor = Color.hsv(currentHue, currentSaturation, currentValue)
243+
val startColor = remember(baseColor) { baseColor.copy(alpha = 0f) }
244+
val endColor = remember(baseColor) { baseColor.copy(alpha = 1f) }
245+
246+
val checkerBrush = remember {
247+
object {
248+
// Pre-computed colors
249+
val light = Color(0xFFCCCCCC)
250+
val dark = Color(0xFFAAAAAA)
251+
val checkerSize = 3.dp
252+
253+
fun draw(drawScope: DrawScope, width: Float, height: Float) {
254+
with(drawScope) {
255+
val pixelSize = checkerSize.toPx()
256+
val horizontalCount = (width / pixelSize).toInt() + 1
257+
val verticalCount = (height / pixelSize).toInt() + 1
258+
259+
drawRect(light)
260+
261+
for (y in 0 until verticalCount) {
262+
val isEvenRow = y % 2 == 0
263+
val startX = if (isEvenRow) 0 else 1
264+
265+
for (x in startX until horizontalCount step 2) {
266+
drawRect(
267+
color = dark,
268+
topLeft = Offset(x * pixelSize, y * pixelSize),
269+
size = Size(pixelSize, pixelSize)
270+
)
271+
}
272+
}
273+
}
274+
}
275+
}
276+
}
277+
238278
ColorSlider(
239279
value = currentAlpha,
240280
onValueChanged = onAlphaChanged,
241281
drawBrush = {
242-
val brush = Brush.horizontalGradient(
243-
colors = listOf(
244-
Color.hsv(currentHue, currentSaturation, currentValue, 0f),
245-
Color.hsv(currentHue, currentSaturation, currentValue, 1f)
246-
)
282+
drawRect(
283+
brush = Brush.horizontalGradient(listOf(startColor, endColor))
247284
)
248-
drawRect(brush = brush)
249285
},
250-
modifier = Modifier.fillMaxWidth()
286+
startEdgeColor = startColor,
287+
endEdgeColor = endColor,
288+
modifier = Modifier.fillMaxWidth(),
289+
drawBackground = checkerBrush::draw
251290
)
252291
}
253292

@@ -259,36 +298,76 @@ private fun ColorSlider(
259298
value: Float,
260299
onValueChanged: (Float) -> Unit,
261300
drawBrush: DrawScope.() -> Unit,
262-
modifier: Modifier = Modifier
301+
startEdgeColor: Color,
302+
endEdgeColor: Color,
303+
modifier: Modifier = Modifier,
304+
drawBackground: (DrawScope.(width: Float, height: Float) -> Unit)? = null
263305
) {
264306
val density = LocalDensity.current
265307
var sliderWidth by remember { mutableStateOf(0.dp) }
266308
val indicatorSizeDp = 20.dp
267-
val sliderSizePx = with(density) { 26.dp.toPx() }
309+
val sliderSizePx = with(density) { remember { 26.dp.toPx() } }
310+
val halfSliderSizePx = remember(sliderSizePx) { sliderSizePx / 2f }
311+
val borderShape = remember { SmoothRoundedCornerShape(50.dp) }
312+
val borderStroke = remember { 0.5.dp }
313+
val borderColor = remember { Color.Gray.copy(0.1f) }
314+
315+
val dragHandler = remember(onValueChanged, sliderSizePx) {
316+
{ posX: Float, width: Float ->
317+
handleSliderInteraction(posX, width, sliderSizePx, onValueChanged)
318+
}
319+
}
268320

269321
Box(
270322
modifier = modifier
271323
.height(26.dp)
272-
.clip(SmoothRoundedCornerShape(13.dp))
324+
.clip(borderShape)
325+
.border(borderStroke, borderColor, borderShape)
273326
) {
274-
// Draw gradient
275327
Canvas(
276328
modifier = Modifier
277329
.fillMaxSize()
278330
.onGloballyPositioned { coordinates ->
279331
sliderWidth = with(density) { coordinates.size.width.toDp() }
280332
}
281-
.pointerInput(Unit) {
333+
.pointerInput(dragHandler) {
282334
detectHorizontalDragGestures { change, _ ->
283335
change.consume()
284-
handleSliderInteraction(change.position.x, size.width.toFloat(), sliderSizePx, onValueChanged)
336+
dragHandler(change.position.x, size.width.toFloat())
285337
}
286338
}
287339
) {
288-
drawBrush()
340+
val canvasWidth = size.width
341+
val effectiveWidth = canvasWidth - sliderSizePx
342+
343+
// Background drawing
344+
drawBackground?.invoke(this, canvasWidth, size.height)
345+
346+
// Gradient drawing with transformations
347+
translate(left = halfSliderSizePx, top = 0f) {
348+
scale(
349+
scaleX = effectiveWidth / canvasWidth,
350+
scaleY = 1f,
351+
pivot = Offset.Zero
352+
) {
353+
drawBrush()
354+
}
355+
}
356+
357+
// Edge color fills
358+
drawRect(
359+
color = startEdgeColor,
360+
topLeft = Offset(0f, 0f),
361+
size = Size(halfSliderSizePx, size.height)
362+
)
363+
364+
drawRect(
365+
color = endEdgeColor,
366+
topLeft = Offset(canvasWidth - halfSliderSizePx, 0f),
367+
size = Size(halfSliderSizePx, size.height)
368+
)
289369
}
290370

291-
// Current value indicator
292371
SliderIndicator(
293372
modifier = Modifier.align(Alignment.CenterStart),
294373
value = value,
@@ -299,6 +378,7 @@ private fun ColorSlider(
299378
}
300379
}
301380

381+
302382
@Composable
303383
private fun SliderIndicator(
304384
modifier: Modifier,
@@ -337,4 +417,6 @@ private fun handleSliderInteraction(
337417
val constrainedX = positionX.coerceIn(sliderHalfSizePx, totalWidth - sliderHalfSizePx)
338418
val newPosition = (constrainedX - sliderHalfSizePx) / effectiveWidth
339419
onValueChanged(newPosition.coerceIn(0f, 1f))
340-
}
420+
}
421+
422+
private val hueColors = List(36) { i -> Color.hsv(i * 10f, 1f, 1f) }

0 commit comments

Comments
 (0)