-
-
Save L10n42/5625e88371b978a8874727d668425b95 to your computer and use it in GitHub Desktop.
DNA Helix Animation in 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
import androidx.compose.animation.core.LinearEasing | |
import androidx.compose.animation.core.RepeatMode | |
import androidx.compose.animation.core.animateFloat | |
import androidx.compose.animation.core.infiniteRepeatable | |
import androidx.compose.animation.core.rememberInfiniteTransition | |
import androidx.compose.animation.core.tween | |
import androidx.compose.foundation.Canvas | |
import androidx.compose.runtime.Composable | |
import androidx.compose.runtime.getValue | |
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.unit.Dp | |
import androidx.compose.ui.unit.dp | |
import kotlin.math.sin | |
/** | |
* A Composable function that draws an animated DNA Helix. | |
* | |
* This function creates a horizontal DNA Helix animation that spans the canvas width, | |
* using its height as the diameter for the helix. | |
* | |
* @param modifier Modifier to be applied to the Canvas | |
* @param firstColor Color of the first set of points | |
* @param secondColor Color of the second set of points | |
* @param pointSize Diameter of the points | |
* @param lineWidth Width of the lines connecting the points | |
* @param spacing Distance between consecutive points | |
* @param shifting Horizontal shift for the points | |
* @param curvature Curvature of the helix | |
* @param cycleDuration Duration of one animation cycle in milliseconds | |
* @param lineBrush Function to define the brush for the lines | |
*/ | |
@Composable | |
fun DNAHelix( | |
modifier: Modifier, | |
firstColor: Color, | |
secondColor: Color, | |
pointSize: Dp = 5.dp, | |
lineWidth: Dp = 1.5.dp, | |
spacing: Dp = 10.dp, | |
shifting: Dp = 0.dp, | |
curvature: Float = 16f, | |
cycleDuration: Int = 3000, | |
lineBrush: (firstPoint: Offset, secondPoint: Offset) -> Brush = { fp, sp -> | |
Brush.linearGradient( | |
colors = listOf(firstColor, secondColor), | |
start = fp, | |
end = sp | |
) | |
} | |
) { | |
val helixTransition = rememberInfiniteTransition() | |
val animatedAngle by helixTransition.animateFloat( | |
initialValue = 0f, | |
targetValue = 360f, | |
animationSpec = infiniteRepeatable( | |
animation = tween(durationMillis = cycleDuration, easing = LinearEasing), | |
repeatMode = RepeatMode.Restart | |
) | |
) | |
Canvas(modifier) { | |
val spacingPx = spacing.toPx() | |
val pointsCount = (size.width / spacingPx).toInt() | |
val helixRadius = size.height / 2 | |
val pointRadiusPx = pointSize.toPx() / 2 | |
val lineWidthPx = lineWidth.toPx() | |
val shiftingPx = shifting.toPx() | |
for (i in 1 until pointsCount) { | |
val currentAngle = (animatedAngle + i * curvature) % 360 | |
val xOffset = i * spacingPx | |
val firstPoint = calculateCoordinates(currentAngle, helixRadius, xOffset - shiftingPx, helixRadius) | |
val secondPoint = calculateCoordinates(currentAngle + 180, helixRadius, xOffset + shiftingPx, helixRadius) | |
drawLine( | |
brush = lineBrush(firstPoint, secondPoint), | |
strokeWidth = lineWidthPx, | |
start = firstPoint, | |
end = secondPoint | |
) | |
drawCircle( | |
color = firstColor, | |
radius = pointRadiusPx, | |
center = firstPoint | |
) | |
drawCircle( | |
color = secondColor, | |
radius = pointRadiusPx, | |
center = secondPoint | |
) | |
} | |
} | |
} | |
private fun calculateCoordinates(angle: Float, radius: Float, centerX: Float, centerY: Float): Offset { | |
val y = centerY + radius * sin(Math.toRadians(angle.toDouble())).toFloat() | |
return Offset(centerX, y) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment