Skip to content

Instantly share code, notes, and snippets.

@dsvoronin
Last active September 2, 2020 10:53
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 dsvoronin/99ab590de7f46a95901f to your computer and use it in GitHub Desktop.
Save dsvoronin/99ab590de7f46a95901f to your computer and use it in GitHub Desktop.
Protools

Protools splash animation

Screencapture GIF

import android.content.Context
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Paint
import android.util.AttributeSet
class StarsView(context: Context, attrs: AttributeSet) : TickView(context, attrs) {
data class Star(val x: Float,
val y: Float,
val radius: Float,
val paint: Paint,
val alpha: Int,
val shiftFunc: (alpha: Int, shift: Int) -> Int)
val density = 50
val alphaRange = 50..222
val radiusRange = 3.0..10.0
val alphaShift = 90
var paint = Paint().apply { color = Color.WHITE }
val stars: List<Star> by lazy { generateStars() }
override fun tick(canvas: Canvas) {
val currentMillis = System.currentTimeMillis()
val time = (currentMillis - (currentMillis / 100000 * 100000)).toDouble() / 10000 * 20
val sinus = Math.sin(time)
val s = (alphaShift * sinus).toInt()
stars.forEach { star ->
canvas.drawCircle(
star.x,
star.y,
star.radius,
paint.apply { alpha = star.shiftFunc(star.alpha, s).byteAbs() })
}
}
private fun Int.byteAbs(): Int {
return when {
this < 0 -> 0
this > 255 -> 255
else -> this
}
}
private fun generateStars(): List<Star> =
(0..density).map {
val a = rnd.nextInt(alphaRange.first, alphaRange.last)
Star(
x = rnd.nextInt(0, measuredWidth).toFloat(),
y = rnd.nextInt(0, measuredHeight).toFloat(),
radius = rnd.nextDouble(radiusRange.start, radiusRange.endInclusive).toFloat(),
alpha = a,
paint = Paint(paint).apply { alpha = a },
shiftFunc = when (rnd.nextBoolean()) {
true -> { alpha, shift -> alpha + shift }
false -> { alpha, shift -> alpha - shift }
})
}
}
import android.content.Context
import android.graphics.Canvas
import android.util.AttributeSet
import android.view.View
import java.util.concurrent.ThreadLocalRandom
abstract class TickView(context: Context, attrs: AttributeSet) : View(context, attrs) {
protected val rnd = ThreadLocalRandom.current()
protected var fps = 12
val tick: Runnable
get() = Runnable {
invalidate()
postDelayed(tick, 1000L / fps)
}
override fun onAttachedToWindow() {
super.onAttachedToWindow()
post(tick)
}
override fun onDetachedFromWindow() {
super.onDetachedFromWindow()
removeCallbacks(tick)
}
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
tick(canvas)
}
abstract fun tick(canvas: Canvas)
}
import android.content.Context
import android.graphics.*
import android.util.AttributeSet
import java.util.concurrent.ThreadLocalRandom
class TracesView(context: Context, attrs: AttributeSet) : TickView(context, attrs) {
val density = 5
val speed = 100
val spawnChance = 0.10
val lengthRange = 170..230
val alphaRange = 200..230
init {
fps = 60
}
val paint = Paint().apply {
color = Color.WHITE
isAntiAlias = true
shader = LinearGradient(0f, 0f, 0f, 0f, Color.WHITE, Color.DKGRAY, Shader.TileMode.MIRROR)
}
val traces by lazy { generateTraces() }
val spawnZone: Rect by lazy { Rect(measuredWidth, -measuredWidth, 2 * measuredWidth, measuredWidth) }
val visibleZone: Rect by lazy { Rect(0, 0, measuredWidth, measuredHeight) }
override fun tick(canvas: Canvas) {
val deadTraces = traces.filter { it.isDead(visibleZone) }
val aliveTraces = traces.filter { !it.isDead(visibleZone) }
traces.forEach {
if (!it.isDead(visibleZone)) {
canvas.drawLine(
it.x.toFloat(),
it.y.toFloat(),
it.tailX.toFloat(),
it.tailY.toFloat(),
paint.apply { alpha = it.alpha })
it.move(speed)
}
}
if (aliveTraces.size < 2 && deadTraces.size > 0 && rnd.nextFloat() <= spawnChance) {
deadTraces.random().respawn(spawnZone, rnd, lengthRange, alphaRange)
}
}
private fun generateTraces(): List<Trace> = (0..density).map { Trace() }
private fun <T> List<T>.random(): T = get(rnd.nextInt(size))
data class Trace(var x: Int = 0, var y: Int = 0, var tailX: Int = 0, var tailY: Int = 0, var alpha: Int = 0) {
fun respawn(zone: Rect, rnd: ThreadLocalRandom, lengthRange: IntRange, alphaRange: IntRange) {
val point = zone.randomPoint(rnd)
val length = rnd.nextInt(lengthRange.first, lengthRange.last)
alpha = rnd.nextInt(alphaRange.first, alphaRange.last)
x = point.x
y = point.y
tailX = (point.x + length)
tailY = (point.y - length)
}
fun move(speed: Int) {
x -= speed
y += speed
tailX -= speed
tailY += speed
}
fun isDead(visibleZone: Rect): Boolean = tailX < visibleZone.left || tailY > visibleZone.bottom
fun Rect.randomPoint(rnd: ThreadLocalRandom): Point = Point(
rnd.nextInt(this.left, this.right),
rnd.nextInt(this.top, this.bottom))
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment