Skip to content

Instantly share code, notes, and snippets.

@AdrianoCelentano
Last active October 27, 2020 18:45
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save AdrianoCelentano/55e2dad72d148182cdf4fe6f4c60f7b9 to your computer and use it in GitHub Desktop.
Save AdrianoCelentano/55e2dad72d148182cdf4fe6f4c60f7b9 to your computer and use it in GitHub Desktop.
Jetpack Compose Tornado
import androidx.annotation.FloatRange
import androidx.compose.foundation.Canvas
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.*
import androidx.compose.ui.graphics.drawscope.DrawScope
import androidx.compose.ui.graphics.drawscope.Stroke
import androidx.compose.ui.graphics.drawscope.translate
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import com.adriano.compose.util.TWO_PI
import com.adriano.compose.util.animationTimeMillis
import com.adriano.compose.util.map
import kotlin.math.cos
import kotlin.math.sin
import kotlin.random.Random
import kotlin.random.nextInt
@Composable
fun CircularMotionPath() {
//https://github.com/alexjlockwood/bees-and-bombs-compose/blob/master/app/src/main/java/com/alexjlockwood/beesandbombs/demos/utils/AnimationUtils.kt
val millis by animationTimeMillis()
val circleDataList = remember { createCircleData() }
val path = remember { Path() }
Canvas(modifier = Modifier.fillMaxSize()) {
translate(size.width / 2, size.height / 2) {
circleDataList.forEach { circleData ->
drawArc(
millis = millis,
path = path,
circleData = circleData,
)
}
}
}
}
private fun DrawScope.drawArc(
millis: Long,
path: Path,
circleData: CircleData,
) {
path.reset()
val angles = getAngles(circleData.fraction)
val circleOffsets = getCircleOffsets(angles, circleData, millis)
circleOffsets.forEachIndexed { index, offset ->
if (index == 0) path.moveTo(offset.x, offset.y)
else path.lineTo(offset.x, offset.y)
}
drawPath(
path = path,
brush = LinearGradient(
colors = listOf(circleData.color, circleData.color.copy(alpha = 0f)),
startX = circleOffsets.first().x,
startY = circleOffsets.first().y,
endX = circleOffsets.last().x,
endY = circleOffsets.last().y,
),
style = Stroke(
width = 4.dp.toPx(),
join = StrokeJoin.Round,
)
)
}
private fun DrawScope.getCircleOffsets(
angles: List<Float>,
circleData: CircleData,
millis: Long,
): List<Offset> {
return angles.map { angle ->
val circleProgress = millis / 1000f * TWO_PI * circleData.speed
val x = circleData.radius.toPx() * cos(angle - circleProgress)
val y = circleData.radius.toPx() * sin(angle - circleProgress)
return@map Offset(x, y)
}
}
private fun getAngles(
@FloatRange(from = 0.0, to = 1.0) circleFraction: Float = 1.0f
): List<Float> {
return (0..100).map {
map(
it.toFloat(),
0f,
100f,
0f,
TWO_PI * circleFraction
)
}
}
private fun createCircleData(): List<CircleData> {
return List(50) {
CircleData(
radius = Random.nextInt(10..120).dp,
fraction = Random.nextInt(3..6) / 10f,
speed = Random.nextInt(20..80) / 100f,
color = lerp(Color.Cyan, Color.Blue, Random.nextFloat())
)
}
}
data class CircleData(
val radius: Dp,
val fraction: Float,
val speed: Float,
val color: Color
)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment