Created
April 28, 2024 06:15
-
-
Save ardakazanci/0256b50c28de6472ed7454c7d0a6b418 to your computer and use it in GitHub Desktop.
RulerView - Jetpack Compose
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@Composable | |
fun RulerView( | |
startValue: Int, | |
endValue: Int, | |
step: Int | |
) { | |
val density = LocalDensity.current | |
val scrollState = rememberScrollState() | |
val sectionWidth = 80.dp | |
var sliderValue by remember { mutableFloatStateOf(0.5f) } | |
var angleSliderValue by remember { mutableFloatStateOf(0f) } | |
var colorSliderValue by remember { mutableFloatStateOf(0f) } | |
var sliderX by remember { mutableFloatStateOf(0f) } | |
var sliderY by remember { mutableFloatStateOf(0f) } | |
var sliderZ by remember { mutableFloatStateOf(1f) } | |
var isAnimating by remember { mutableStateOf(false) } | |
val currentValue = remember(scrollState.value, with(density) { sectionWidth.toPx() }) { | |
startValue + (scrollState.value / with(density) { sectionWidth.toPx() }).toInt() * step | |
} | |
Column { | |
SliderControl("Thickness and Length Adjustment:", sliderValue, 0.1f..3f) { | |
sliderValue = it | |
} | |
SliderControl("Angle Adjustment:", angleSliderValue, -45f..45f) { angleSliderValue = it } | |
SliderControl("Color Change:", colorSliderValue, 0f..1f) { colorSliderValue = it } | |
SliderControl("Rotation on X Axis:", sliderX, -360f..360f) { sliderX = it } | |
SliderControl("Rotation on Y Axis:", sliderY, -360f..360f) { sliderY = it } | |
SliderControl("Scaling on Z Axis:", sliderZ, 0.5f..2f) { sliderZ = it } | |
Button(onClick = { isAnimating = !isAnimating }) { | |
Text(if (isAnimating) "Stop Animation" else "Start Animation") | |
} | |
Spacer(modifier = Modifier.height(24.dp)) | |
Box(modifier = Modifier.graphicsLayer { | |
rotationX = sliderX | |
rotationY = sliderY | |
scaleX = sliderZ | |
scaleY = sliderZ | |
}) { | |
Image( | |
modifier = Modifier | |
.clip(RoundedCornerShape(20.dp)) | |
.fillMaxWidth() | |
.height(60.dp) | |
.align(Alignment.TopEnd), | |
contentScale = ContentScale.Crop, | |
painter = painterResource(id = android.R.drawable.button_onoff_indicator_off), | |
contentDescription = "" | |
) | |
Box(modifier = Modifier.padding(horizontal = 24.dp)) { | |
Row( | |
modifier = Modifier | |
.horizontalScroll(scrollState) | |
) { | |
for (i in startValue..endValue step step) { | |
RulerSection( | |
number = i, | |
step, | |
sliderValue, | |
angleSliderValue, | |
colorSliderValue | |
) | |
} | |
} | |
ValueDisplay(value = currentValue, isAnimating) | |
} | |
} | |
} | |
} | |
@Composable | |
fun SliderControl( | |
label: String, | |
value: Float, | |
valueRange: ClosedFloatingPointRange<Float>, | |
onValueChange: (Float) -> Unit | |
) { | |
Text(label) | |
Slider( | |
value = value, | |
onValueChange = onValueChange, | |
valueRange = valueRange | |
) | |
} | |
@Composable | |
fun RulerSection(number: Int, step: Int, sliderValue: Float, angle: Float, colorValue: Float) { | |
val mainColor = Color(0xFF6200EE) | |
val secondaryColor = Color(0xFF03DAC5) | |
val interpolatedColor = lerp(mainColor, secondaryColor, colorValue) | |
Column(horizontalAlignment = Alignment.CenterHorizontally) { | |
Text( | |
text = number.toString(), | |
fontSize = 18.sp, | |
textAlign = TextAlign.Center, | |
modifier = Modifier.padding(vertical = 8.dp) | |
) | |
Canvas( | |
modifier = Modifier | |
.height(50.dp) | |
.width(80.dp) | |
) { | |
val angleRadians = Math.toRadians(angle.toDouble()).toFloat() | |
val bigLineHeight = 20f * sliderValue | |
val bigLineX = size.width / 2 | |
drawLine( | |
color = interpolatedColor, | |
start = Offset(x = bigLineX, y = 0f), | |
end = Offset( | |
x = bigLineX + bigLineHeight * sin(angleRadians), | |
y = bigLineHeight * cos(angleRadians) | |
), | |
strokeWidth = 7f * sliderValue | |
) | |
val totalSmallLines = 4 | |
val smallLineHeight = 10f * sliderValue | |
val smallLineStrokeWidth = 2f * sliderValue | |
val availableWidth = size.width - (4f * 2) | |
val interval = availableWidth / (totalSmallLines + 1) | |
for (i in 1..totalSmallLines) { | |
val xPosition = bigLineX + (interval * i) | |
drawLine( | |
color = interpolatedColor.copy(alpha = 0.5f), | |
start = Offset(x = xPosition, y = 0f), | |
end = Offset( | |
x = xPosition + smallLineHeight * sin(angleRadians), | |
y = smallLineHeight * cos(angleRadians) | |
), | |
strokeWidth = smallLineStrokeWidth | |
) | |
} | |
} | |
} | |
} | |
@Composable | |
fun ValueDisplay(value: Int, isAnimating: Boolean, maxValue: Int = 100) { | |
Box( | |
modifier = Modifier | |
.padding(top = 40.dp) | |
.fillMaxWidth() | |
.height(50.dp) | |
) { | |
val animatedLineLength = animateFloatAsState( | |
targetValue = if (isAnimating) value.toFloat() / maxValue else 0f, | |
animationSpec = spring(), label = "animatedLineLength" | |
) | |
Canvas( | |
modifier = Modifier | |
.fillMaxWidth() | |
.height(10.dp) | |
) { | |
drawLineWithAnimation(this, animatedLineLength.value) | |
} | |
} | |
} | |
fun drawLineWithAnimation(drawScope: DrawScope, fraction: Float) { | |
val lineLength = fraction * drawScope.size.width * 2.2f | |
drawScope.drawLine( | |
brush = Brush.linearGradient(listOf(Color.Red.copy(alpha = 0.5f), Color.Red)), | |
start = Offset(x = 0f, y = 0f), | |
end = Offset(x = lineLength, y = 0f), | |
strokeWidth = 8f | |
) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment