Created
March 11, 2019 02:30
-
-
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
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
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