Skip to content

Instantly share code, notes, and snippets.

@alexjlockwood
Created March 11, 2019 02:30
Show Gist options
  • Star 8 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save alexjlockwood/ae93e0e5d5a3b2ed0763bd11c1942873 to your computer and use it in GitHub Desktop.
Save alexjlockwood/ae93e0e5d5a3b2ed0763bd11c1942873 to your computer and use it in GitHub Desktop.
Kotlin implementation of a wave square animation, inspired by https://twitter.com/beesandbombs/status/1101169015299420163
import android.content.Context
import android.graphics.Canvas
import android.graphics.Paint
import android.graphics.Path
import android.util.AttributeSet
import android.view.View
import kotlin.math.atan2
import kotlin.math.cos
import kotlin.math.sin
import kotlin.math.sqrt
private const val N = 720
private const val NUM_LINES = 18
private const val NUM_WAVES = 18
private const val PI = Math.PI.toFloat()
private const val TWO_PI = 2 * PI
private const val PERIOD = 5000
private const val LINE_LENGTH = 500f
private const val WAVE_HEIGHT = 20f
private const val SPACING = 27f
private const val CURL_AMOUNT = 12f
class WaveSquareView(context: Context, attrs: AttributeSet?) : View(context, attrs) {
private val density = resources.displayMetrics.density
private val t: Float
get() = (System.currentTimeMillis() % PERIOD) / PERIOD.toFloat()
private val path = Path()
private val paint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
style = Paint.Style.STROKE
strokeMiter = 1f
strokeWidth = 2f
}
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
canvas.save()
canvas.translate(width / 2f, height / 2f)
canvas.scale(density, density)
path.rewind()
for (l in 0 until NUM_LINES) {
for (n in 0 until N) {
val qq = n.toFloat() / (N - 1)
val phase = map(n.toFloat(), 0f, N - 1f, 0f, TWO_PI * NUM_WAVES) - TWO_PI * t
var x = lerp(-LINE_LENGTH / 2f, LINE_LENGTH / 2f, qq)
var y = (SPACING * (l - 0.5f * (NUM_LINES - 1)))
val amount = ease(map(cos(TWO_PI * t + atan2(x, y) - 0.01f * dist(x, y, 0f, 0f)), 1f, -1f, 0f, 1f))
y += 0.5f * WAVE_HEIGHT * sin(phase + PI * l) * amount - 0.2f * WAVE_HEIGHT * amount
x -= CURL_AMOUNT * cos(phase + PI * l) * amount
if (n == 0) {
path.moveTo(x, y)
} else {
path.lineTo(x, y)
}
}
}
canvas.drawPath(path, paint)
canvas.restore()
postInvalidateOnAnimation()
}
}
private fun map(value: Float, start1: Float, stop1: Float, start2: Float, stop2: Float): Float {
return start2 + (stop2 - start2) * ((value - start1) / (stop1 - start1))
}
private fun lerp(a: Float, b: Float, t: Float): Float {
return a + (b - a) * t
}
private fun dist(x1: Float, y1: Float, x2: Float, y2: Float): Float {
return sqrt(((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1)))
}
private fun ease(p: Float): Float {
return 3 * p * p - 2 * p * p * p
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment