Last active
August 16, 2020 11:22
-
-
Save Skyyo/adbc9f30f1f4a50bc587958ccd442dff to your computer and use it in GitHub Desktop.
SnowFlake effect/view. SnowFlakesEffect.kt credits to https://github.com/DrKLO/Telegram #view #snow
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 android.graphics.Canvas | |
import android.graphics.Color | |
import android.graphics.Paint | |
import android.view.View | |
import android.view.animation.AccelerateInterpolator | |
import android.view.animation.DecelerateInterpolator | |
import java.security.SecureRandom | |
import java.util.* | |
class SnowFlakesEffect { | |
private val particlePaint: Paint = Paint(Paint.ANTI_ALIAS_FLAG) | |
private val particleThinPaint: Paint | |
private var lastAnimationTime: Long = 0 | |
private val angleDiff = (Math.PI / 180 * 60).toFloat() | |
private val particles = ArrayList<Particle>() | |
private val freeParticles = ArrayList<Particle>() | |
private var color = 0 | |
private var marginTop = 0 | |
private inner class Particle { | |
var x = 0f | |
var y = 0f | |
var vx = 0f | |
var vy = 0f | |
var velocity = 0f | |
var alpha = 0f | |
var lifeTime = 0f | |
var currentTime = 0f | |
var scale = 0f | |
var type = 0 | |
fun draw(canvas: Canvas) { | |
when (type) { | |
0 -> { | |
particlePaint.alpha = (255 * alpha).toInt() | |
canvas.drawPoint(x, y, particlePaint) | |
} | |
1 -> { | |
particleThinPaint.alpha = (255 * alpha).toInt() | |
var angle = (-Math.PI).toFloat() / 2 | |
val px = (2.0F.dp) * 2 * scale | |
val px1 = -(0.57F.dp) * 2 * scale | |
val py1 = (1.55F.dp) * 2 * scale | |
var a = 0 | |
while (a < 6) { | |
var x1 = | |
Math.cos(angle.toDouble()).toFloat() * px | |
var y1 = | |
Math.sin(angle.toDouble()).toFloat() * px | |
val cx = x1 * 0.66f | |
val cy = y1 * 0.66f | |
canvas.drawLine(x, y, x + x1, y + y1, particleThinPaint) | |
val angle2 = (angle - Math.PI / 2).toFloat() | |
x1 = | |
(Math.cos(angle2.toDouble()) * px1 - Math.sin(angle2.toDouble()) * py1).toFloat() | |
y1 = | |
(Math.sin(angle2.toDouble()) * px1 + Math.cos(angle2.toDouble()) * py1).toFloat() | |
canvas.drawLine(x + cx, y + cy, x + x1, y + y1, particleThinPaint) | |
x1 = ( | |
-Math.cos(angle2.toDouble()) * px1 - Math.sin( | |
angle2.toDouble() | |
) * py1 | |
).toFloat() | |
y1 = ( | |
-Math.sin(angle2.toDouble()) * px1 + Math.cos( | |
angle2.toDouble() | |
) * py1 | |
).toFloat() | |
canvas.drawLine(x + cx, y + cy, x + x1, y + y1, particleThinPaint) | |
angle += angleDiff | |
a++ | |
} | |
} | |
else -> { | |
particleThinPaint.alpha = (255 * alpha).toInt() | |
var angle = (-Math.PI).toFloat() / 2 | |
val px = (2.0F.dp) * 2 * scale | |
val px1 = -(0.57F.dp) * 2 * scale | |
val py1 = (1.55F.dp) * 2 * scale | |
var a = 0 | |
while (a < 6) { | |
var x1 = | |
Math.cos(angle.toDouble()).toFloat() * px | |
var y1 = | |
Math.sin(angle.toDouble()).toFloat() * px | |
val cx = x1 * 0.66f | |
val cy = y1 * 0.66f | |
canvas.drawLine(x, y, x + x1, y + y1, particleThinPaint) | |
val angle2 = (angle - Math.PI / 2).toFloat() | |
x1 = | |
(Math.cos(angle2.toDouble()) * px1 - Math.sin(angle2.toDouble()) * py1).toFloat() | |
y1 = | |
(Math.sin(angle2.toDouble()) * px1 + Math.cos(angle2.toDouble()) * py1).toFloat() | |
canvas.drawLine(x + cx, y + cy, x + x1, y + y1, particleThinPaint) | |
x1 = ( | |
-Math.cos(angle2.toDouble()) * px1 - Math.sin( | |
angle2.toDouble() | |
) * py1 | |
).toFloat() | |
y1 = ( | |
-Math.sin(angle2.toDouble()) * px1 + Math.cos( | |
angle2.toDouble() | |
) * py1 | |
).toFloat() | |
canvas.drawLine(x + cx, y + cy, x + x1, y + y1, particleThinPaint) | |
angle += angleDiff | |
a++ | |
} | |
} | |
} | |
} | |
} | |
private fun updateParticles(dt: Long) { | |
var count = particles.size | |
var a = 0 | |
while (a < count) { | |
val particle = particles[a] | |
if (particle.currentTime >= particle.lifeTime) { | |
if (freeParticles.size < 40) { | |
freeParticles.add(particle) | |
} | |
particles.removeAt(a) | |
a-- | |
count-- | |
a++ | |
continue | |
} | |
if (particle.currentTime < 200.0f) { | |
particle.alpha = | |
accelerateInterpolator.getInterpolation(particle.currentTime / 200.0f) | |
} else { | |
particle.alpha = | |
1.0f - decelerateInterpolator.getInterpolation((particle.currentTime - 200.0f) / (particle.lifeTime - 200.0f)) | |
} | |
particle.x += particle.vx * particle.velocity * dt / 500.0f | |
particle.y += particle.vy * particle.velocity * dt / 500.0f | |
particle.currentTime += dt.toFloat() | |
a++ | |
} | |
} | |
fun onDraw(parent: View?, canvas: Canvas?) { | |
if (parent == null || canvas == null) { | |
return | |
} | |
val count = particles.size | |
for (a in 0 until count) { | |
val particle = particles[a] | |
particle.draw(canvas) | |
} | |
if (random.nextFloat() > 0.7f && particles.size < 100) { | |
val cx = random.nextFloat() * parent.measuredWidth | |
val cy = marginTop + random.nextFloat() * (parent.measuredHeight - (20F.dp) - marginTop) | |
val angle = random.nextInt(40) - 20 + 90 | |
val vx = Math.cos(Math.PI / 180.0 * angle).toFloat() | |
val vy = Math.sin(Math.PI / 180.0 * angle).toFloat() | |
val newParticle: Particle | |
if (!freeParticles.isEmpty()) { | |
newParticle = freeParticles[0] | |
freeParticles.removeAt(0) | |
} else { | |
newParticle = Particle() | |
} | |
newParticle.x = cx | |
newParticle.y = cy | |
newParticle.vx = vx | |
newParticle.vy = vy | |
newParticle.alpha = 0.0f | |
newParticle.currentTime = 0f | |
newParticle.scale = random.nextFloat() * 1.2f | |
newParticle.type = random.nextInt(2) | |
newParticle.lifeTime = 2000 + random.nextInt(100).toFloat() | |
newParticle.velocity = 20.0f + random.nextFloat() * 4.0f | |
particles.add(newParticle) | |
} | |
val newTime = System.currentTimeMillis() | |
val dt = Math.min(17, newTime - lastAnimationTime) | |
updateParticles(dt) | |
lastAnimationTime = newTime | |
parent.invalidate() | |
} | |
fun updateValues(color: Int = Color.WHITE, marginTop: Int = 0) { | |
this.marginTop = marginTop | |
val color = color and -0x19191a | |
if (this.color != color) { | |
this.color = color | |
particlePaint.color = color | |
particleThinPaint.color = color | |
} | |
} | |
companion object { | |
private val random = SecureRandom() | |
var decelerateInterpolator = DecelerateInterpolator() | |
var accelerateInterpolator = AccelerateInterpolator() | |
} | |
init { | |
particlePaint.strokeWidth = (1.5F.dp).toFloat() | |
particlePaint.strokeCap = Paint.Cap.ROUND | |
particlePaint.style = Paint.Style.STROKE | |
particleThinPaint = Paint(Paint.ANTI_ALIAS_FLAG) | |
particleThinPaint.strokeWidth = (0.5F.dp).toFloat() | |
particleThinPaint.strokeCap = Paint.Cap.ROUND | |
particleThinPaint.style = Paint.Style.STROKE | |
updateValues() | |
for (a in 0..19) { | |
freeParticles.add(Particle()) | |
} | |
} | |
} | |
val Float.dp: Int | |
get() = TypedValue.applyDimension( | |
TypedValue.COMPLEX_UNIT_DIP, | |
this, | |
Resources.getSystem().displayMetrics | |
).toInt() | |
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 android.content.Context | |
import android.graphics.Canvas | |
import android.util.AttributeSet | |
import android.view.View | |
class SnowFlakeView @JvmOverloads constructor( | |
context: Context, | |
attrs: AttributeSet? = null, | |
defStyleAttr: Int = 0 | |
) : View(context, attrs, defStyleAttr) { | |
private val snowFlakesEffect = | |
SnowFlakesEffect() | |
init { | |
snowFlakesEffect.updateValues() | |
} | |
override fun onDraw(canvas: Canvas) { | |
super.onDraw(canvas) | |
snowFlakesEffect.onDraw(this, canvas) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment