-
-
Save tomriddle25/ba4da99e3ed546218224e0c0ecd3e517 to your computer and use it in GitHub Desktop.
Mechanical red switch - jetpack compose
This file contains hidden or 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
| package com.example.switch | |
| import androidx.compose.animation.core.Spring | |
| import androidx.compose.animation.core.animateFloatAsState | |
| import androidx.compose.animation.core.spring | |
| import androidx.compose.foundation.Canvas | |
| import androidx.compose.foundation.background | |
| import androidx.compose.foundation.gestures.detectTapGestures | |
| import androidx.compose.foundation.layout.Box | |
| import androidx.compose.foundation.layout.BoxScope | |
| import androidx.compose.foundation.layout.fillMaxSize | |
| import androidx.compose.foundation.layout.height | |
| import androidx.compose.foundation.layout.padding | |
| import androidx.compose.foundation.layout.width | |
| import androidx.compose.foundation.shape.RoundedCornerShape | |
| import androidx.compose.runtime.Composable | |
| import androidx.compose.runtime.getValue | |
| import androidx.compose.runtime.mutableStateOf | |
| import androidx.compose.runtime.remember | |
| import androidx.compose.runtime.setValue | |
| import androidx.compose.ui.Alignment | |
| import androidx.compose.ui.Modifier | |
| import androidx.compose.ui.draw.clip | |
| import androidx.compose.ui.draw.dropShadow | |
| import androidx.compose.ui.geometry.Offset | |
| import androidx.compose.ui.geometry.Size | |
| import androidx.compose.ui.geometry.center | |
| import androidx.compose.ui.graphics.Brush | |
| import androidx.compose.ui.graphics.Color | |
| import androidx.compose.ui.graphics.RadialGradientShader | |
| import androidx.compose.ui.graphics.RectangleShape | |
| import androidx.compose.ui.graphics.Shader | |
| import androidx.compose.ui.graphics.ShaderBrush | |
| import androidx.compose.ui.graphics.lerp | |
| import androidx.compose.ui.graphics.shadow.Shadow | |
| import androidx.compose.ui.input.pointer.pointerInput | |
| import androidx.compose.ui.tooling.preview.Preview | |
| import androidx.compose.ui.unit.DpOffset | |
| import androidx.compose.ui.unit.dp | |
| import androidx.compose.ui.util.lerp | |
| @Composable | |
| fun MechanicalSwitchBackground( | |
| modifier: Modifier, | |
| isOn: Boolean, | |
| button: @Composable BoxScope.() -> Unit | |
| ) { | |
| val isOnFloat by animateFloatAsState( | |
| targetValue = if (isOn) 0f else 1f, | |
| animationSpec = spring( | |
| stiffness = Spring.StiffnessHigh | |
| ) | |
| ) | |
| val largeRadialGradient = object : ShaderBrush() { | |
| override fun createShader(size: Size): Shader { | |
| val biggerDimension = maxOf(size.height, size.width) | |
| return RadialGradientShader( | |
| colors = listOf( | |
| lerp(Color(0xFFFF960A), Color(0x99681401), isOnFloat), | |
| lerp(Color(0xFFFF6E04), Color(0x99681401), isOnFloat), | |
| lerp(Color(0xFFF02305), Color(0x99681401), isOnFloat), | |
| lerp(Color(0xFF962801), Color(0x99681401), isOnFloat), | |
| lerp(Color(0x99681401), Color(0x99681401), isOnFloat), | |
| ), | |
| center = size.center, | |
| radius = biggerDimension / 2f, | |
| colorStops = listOf(0f, 0.2f, 0.4f, 0.75f, 1f) | |
| ) | |
| } | |
| } | |
| Box( | |
| modifier = Modifier | |
| .fillMaxSize() | |
| .background(Color(0xFF1E2022)), | |
| contentAlignment = Alignment.Center | |
| ) { | |
| Box( | |
| modifier = modifier | |
| .height(400.dp) | |
| .width(200.dp) | |
| .dropShadow( | |
| shape = RoundedCornerShape(16.dp), | |
| shadow = Shadow( | |
| radius = 30.dp, | |
| spread = 5.dp, | |
| color = Color(0x66000000), | |
| offset = DpOffset(x = 0.dp, 30.dp) | |
| ) | |
| ) | |
| .clip(RoundedCornerShape(16.dp)) | |
| .background(Color.White) | |
| .padding(8.dp) | |
| ) { | |
| Box( | |
| modifier = Modifier | |
| .fillMaxSize() | |
| .dropShadow( | |
| shape = RoundedCornerShape(12.dp), | |
| shadow = Shadow( | |
| radius = 12.dp, | |
| spread = 2.dp, | |
| brush = Brush.verticalGradient( | |
| colorStops = arrayOf( | |
| 0.0f to Color(0x66000000), | |
| 0.03f to Color(0x66000000), | |
| 0.45f to lerp(Color(0xBFF02305), Color(0x66000000), isOnFloat), | |
| 0.55f to lerp(Color(0xBFF02305), Color(0x66000000), isOnFloat), | |
| 0.97f to Color(0x66000000), | |
| 1f to Color(0x66000000), | |
| ) | |
| ), | |
| offset = DpOffset(x = 0.dp, 0.dp) | |
| ) | |
| ) | |
| .clip(RoundedCornerShape(12.dp)) | |
| .background(Color.LightGray.copy(alpha = 0.8f)) | |
| .padding(10.dp) | |
| ) { | |
| Box( | |
| modifier = Modifier | |
| .fillMaxSize() | |
| .dropShadow( | |
| shape = RoundedCornerShape(12.dp), | |
| shadow = Shadow( | |
| radius = 24.dp, | |
| spread = 6.dp, | |
| brush = Brush.verticalGradient( | |
| colorStops = arrayOf( | |
| 0.0f to Color(0x66000000), | |
| 0.03f to Color(0x66000000), | |
| 0.45f to lerp(Color(0xBFF02305), Color(0x66000000), isOnFloat), | |
| 0.55f to lerp(Color(0xBFF02305), Color(0x66000000), isOnFloat), | |
| 0.97f to Color(0x66000000), | |
| 1f to Color(0x66000000), | |
| ) | |
| ), | |
| offset = DpOffset(x = 0.dp, 0.dp) | |
| ) | |
| ) | |
| .clip(RoundedCornerShape(12.dp)) | |
| .background(Color.Black) | |
| .padding(vertical = 10.dp, horizontal = 8.dp) | |
| ) { | |
| Box( | |
| modifier = Modifier | |
| .clip(RoundedCornerShape(6.dp)) | |
| .fillMaxSize() | |
| .background(largeRadialGradient) | |
| ) { | |
| button.invoke(this) | |
| } | |
| } | |
| } | |
| } | |
| } | |
| } | |
| @Composable | |
| fun MechanicalSwitch(initialIsOn: Boolean) { | |
| var isOn by remember { mutableStateOf(initialIsOn) } | |
| val touch by animateFloatAsState( | |
| targetValue = if (isOn) 0f else 1f, | |
| animationSpec = spring( | |
| stiffness = Spring.StiffnessHigh | |
| ) | |
| ) | |
| MechanicalSwitchBackground( | |
| modifier = Modifier | |
| .pointerInput(Unit) { | |
| detectTapGestures( | |
| onTap = { | |
| isOn = !isOn | |
| }, | |
| ) | |
| }, | |
| isOn = isOn | |
| ) { | |
| Canvas( | |
| modifier = Modifier | |
| .clip(RectangleShape) | |
| .fillMaxSize() | |
| ) { | |
| val dotDiameter = (size.width - 12.dp.toPx()) / 21 | |
| val paddingHeight = dotDiameter * 10 | |
| val topOffShadowHeight = lerp(0f, paddingHeight, touch) | |
| // TOP REFLECT - ON | |
| val onReflectTop = lerp(5f, 0f, touch) | |
| drawRect( | |
| brush = Brush.horizontalGradient( | |
| colorStops = arrayOf( | |
| 0.0f to Color(0x00410D01), | |
| 0.03f to Color(0x0D410D01), | |
| 0.45f to Color(0xE68D6859), | |
| 0.55f to Color(0xE68D6859), | |
| 0.97f to Color(0x0D410D01), | |
| 1f to Color(0x00410D01), | |
| ), | |
| ), | |
| size = Size(size.width, onReflectTop), | |
| topLeft = Offset(0f, 7f) | |
| ) | |
| // VERTICAL GRADIENT BOTTOM - OFF | |
| drawRect( | |
| brush = Brush.verticalGradient( | |
| colorStops = arrayOf( | |
| 0.0f to Color(0x73DC3C05), | |
| 0.4f to Color(0xBF4B1902), | |
| 0.8f to Color(0x99030303), | |
| 0.9f to Color(0x99030303), | |
| 1f to Color(0x59AE0302), | |
| ), | |
| endY = topOffShadowHeight, | |
| ), | |
| size = Size(size.width - 12.dp.toPx(), topOffShadowHeight), | |
| topLeft = Offset(6.dp.toPx(), 0f) | |
| ) | |
| // HORIZONTAL GRADIENT TOP - OFF | |
| drawRect( | |
| brush = Brush.horizontalGradient( | |
| colorStops = arrayOf( | |
| 0.0f to Color(0x00410D01), | |
| 0.03f to Color(0x0D410D01), | |
| 0.05f to Color(0xE62D0E02), | |
| 0.15f to Color(0x0D2D0E02), | |
| 0.85f to Color(0x0D2D0E02), | |
| 0.95f to Color(0xE62D0E02), | |
| 0.97f to Color(0x0D410D01), | |
| 1f to Color(0x00410D01), | |
| ), | |
| ), | |
| size = Size(size.width, topOffShadowHeight), | |
| topLeft = Offset(0f, 0f) | |
| ) | |
| // FLAT SPOT TOP - OFF | |
| drawRect( | |
| brush = Brush.horizontalGradient( | |
| colorStops = arrayOf( | |
| 0.0f to Color(0x00410D01), | |
| 0.05f to Color(0x4D8C1903), | |
| 0.95f to Color(0x4D8C1903), | |
| 1f to Color(0x00410D01), | |
| ), | |
| ), | |
| size = Size(size.width, 20f), | |
| topLeft = Offset(0f, topOffShadowHeight - 20f) | |
| ) | |
| // VERTICAL GRADIENT BOTTOM - ON | |
| val bottomShadowOffset = lerp((dotDiameter * 43), size.height, touch) | |
| drawRect( | |
| brush = Brush.verticalGradient( | |
| colorStops = arrayOf( | |
| 0.0f to Color(0xBFDC3C05), | |
| 0.4f to Color(0xBF4B1902), | |
| 0.8f to Color(0x99030303), | |
| 0.85f to Color(0x99030303), | |
| 1f to Color(0x8CAE0302), | |
| ), | |
| startY = bottomShadowOffset, | |
| endY = size.height, | |
| ), | |
| size = Size(size.width - 12.dp.toPx(), size.height - bottomShadowOffset), | |
| topLeft = Offset(6.dp.toPx(), bottomShadowOffset) | |
| ) | |
| // HORIZONTAL GRADIENT BOTTOM - ON | |
| drawRect( | |
| brush = Brush.horizontalGradient( | |
| colorStops = arrayOf( | |
| 0.0f to Color(0x00410D01), | |
| 0.03f to Color(0x0D410D01), | |
| 0.05f to Color(0xE62D0E02), | |
| 0.15f to Color(0x0D2D0E02), | |
| 0.85f to Color(0x0D2D0E02), | |
| 0.95f to Color(0xE62D0E02), | |
| 0.97f to Color(0x0D410D01), | |
| 1f to Color(0x00410D01), | |
| ), | |
| ), | |
| topLeft = Offset(0f, bottomShadowOffset) | |
| ) | |
| // FLAT SPOT BOTTOM - ON | |
| drawRect( | |
| brush = Brush.horizontalGradient( | |
| colorStops = arrayOf( | |
| 0.0f to Color(0x00410D01), | |
| 0.05f to Color(0xCC8C1903), | |
| 0.95f to Color(0xCC8C1903), | |
| 1f to Color(0x00410D01), | |
| ), | |
| ), | |
| size = Size(size.width, 20f), | |
| topLeft = Offset(0f, bottomShadowOffset) | |
| ) | |
| // BOTTOM REFLECT - OFF | |
| val offReflectBottom = lerp(0f, 5f, touch) | |
| drawRect( | |
| brush = Brush.horizontalGradient( | |
| colorStops = arrayOf( | |
| 0.0f to Color(0x00410D01), | |
| 0.03f to Color(0x0D410D01), | |
| 0.45f to Color(0x8C282015), | |
| 0.55f to Color(0x8C282015), | |
| 0.97f to Color(0x0D410D01), | |
| 1f to Color(0x00410D01), | |
| ), | |
| ), | |
| size = Size(size.width, offReflectBottom), | |
| topLeft = Offset(0f, size.height - 16f) | |
| ) | |
| } | |
| // DOTS | |
| Canvas( | |
| modifier = Modifier | |
| .padding(6.dp) | |
| .clip(RoundedCornerShape(6.dp)) | |
| .fillMaxSize() | |
| ) { | |
| val dotDiameter = (size.width / 21) | |
| val radius = dotDiameter / 2 | |
| val paddingHeight = dotDiameter * 9 | |
| var heightCenter = lerp(radius, radius + paddingHeight, touch) | |
| repeat(42) { indexRow -> | |
| repeat(22) { indexColumn -> | |
| val widthCenter = dotDiameter * indexColumn | |
| drawCircle( | |
| brush = Brush.verticalGradient( | |
| colorStops = getDotColor(indexRow, 43, isOn), | |
| startY = heightCenter - radius, | |
| endY = heightCenter + radius, | |
| ), | |
| radius = radius, | |
| center = Offset(widthCenter, heightCenter) | |
| ) | |
| } | |
| heightCenter += dotDiameter | |
| } | |
| } | |
| } | |
| } | |
| fun getDotColor( | |
| rowIndex: Int, | |
| rowSize: Int, | |
| isOn: Boolean | |
| ): Array<Pair<Float, Color>> { | |
| val colorStartOn = Color(0xFFF01E1E) | |
| val colorMiddleOn = Color(0xFFF5F51E) | |
| val colorEndOn = Color(0xFFF5F0F0) | |
| val colorStartOff = Color(0xFF525050) | |
| val colorMiddleOff = Color(0xFF590E0E) | |
| val colorEndOff = Color(0xFF070505) | |
| val colorStart = if (isOn) { | |
| colorStartOn | |
| } else { | |
| colorStartOff | |
| } | |
| val colorMiddle = if (isOn) { | |
| colorMiddleOn | |
| } else { | |
| colorMiddleOff | |
| } | |
| val colorEnd = if (isOn) { | |
| colorEndOn | |
| } else { | |
| colorEndOff | |
| } | |
| // MANUAL 3 COLORS GRADIENT | |
| val returnColorStart = if (rowIndex < (rowSize / 2)) { | |
| val frac = rowIndex.toFloat() / (rowSize.toFloat() / 2) | |
| lerp(colorStart, colorMiddle, frac) | |
| } else { | |
| val startRow = rowIndex - (rowSize / 2) | |
| val frac = (startRow.toFloat() / (rowSize.toFloat() / 2)) | |
| lerp(colorMiddle, colorEnd, frac) | |
| } | |
| return arrayOf( | |
| 0.0f to returnColorStart.copy(alpha = if (isOn) 0.9f else 0.9f), | |
| 0.5f to returnColorStart.copy(alpha = if (isOn) 0.5f else 0.5f), | |
| 1.0f to returnColorStart.copy(alpha = if (isOn) 0.1f else 0.1f), | |
| ) | |
| } | |
| @Preview | |
| @Composable | |
| fun MechanicalSwitchPreview() { | |
| MechanicalSwitch(true) | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment