刷抖音的时候,无意间刷到 心形函数的动画,觉得很有意思, 就简单的用Android的方式实现了一下.
心形函数公式
公式 :
心形公式
X的取值范围:[-1.81,1.81],该取值范围是保证正弦函数有效取值范围。
我们可以通过参数 a 的取值, 来形成不同的心形轮廓, 进而形成动画效果.
有了以上的认识,我们就是可开始编写Android代码了.
Android实现
代码使用 Kotlin来编写自定义View.个人觉得 只有心还不够, 动画结束需要逐渐显示文字,来达到表白的效果.
先看动画效果:
heart.gif
然后贴上代码实现:
class HeartView(context: Context, attrs: AttributeSet) : BaseView(context, attrs),
ValueAnimator.AnimatorUpdateListener, Animator.AnimatorListener {
var text = "Hello Android"
private val animator = ValueAnimator.ofInt(3_000)
private val path = Path()
private var heartDrawing = false
private var heart = 0.1f
private var pct = 0f
init {
paint.strokeWidth = 4 * dp
paint.textSize = 30 * dp
paint.textAlign = Paint.Align.CENTER
animator.interpolator = LinearInterpolator()
animator.addUpdateListener(this)
animator.addListener(this)
}
fun startHeart() {
heartDrawing = true
animator.cancel()
animator.duration = 3_000
animator.start()
}
override fun onDraw(canvas: Canvas) {
path.reset()
paint.color = Color.RED
paint.style = Paint.Style.STROKE
val padding = 20
val halfWidth = width / 2 - padding
val halfHeight = height / 2
// 根据X坐标,计算出Y坐标, 将其映射到屏幕坐标后,用path连接
for (index in 0..(width - padding * 2)) {
val x = (index - halfWidth) * 1.81 / halfWidth
val y = -getHeartY(x, heart) * height / 6 + halfHeight
if (index == 0) {
path.moveTo(index.toFloat() + padding, y.toFloat())
} else {
path.lineTo(index.toFloat() + padding, y.toFloat())
}
}
canvas.drawPath(path, paint)
// 结束后写文字
if (!heartDrawing) {
val textWidth = paint.measureText(text)
val left = width / 2f - textWidth / 2f
val top = height * 0.4f - 0.9f * paint.textSize
canvas.clipRect(left, top, left + textWidth * pct, top + 1.2f * paint.textSize)
paint.color = Color.WHITE
paint.style = Paint.Style.FILL
canvas.drawText(text, width / 2f, height * 0.4f, paint)
}
}
private fun getHeartY(x: Double, a: Float): Double {
return Math.pow(x * x, 1.0 / 3) + 0.9 * Math.sqrt(3.3 - x * x) * Math.sin(a * Math.PI * x)
}
override fun onAnimationUpdate(animation: ValueAnimator) {
val value = animation.animatedValue as Int
if (value == 0) return
if (heartDrawing) heart = value * 0.01f
else {
pct = value / 3000f
}
invalidate()
}
override fun onDetachedFromWindow() {
animator.cancel()
animator.removeAllUpdateListeners()
animator.removeAllListeners()
super.onDetachedFromWindow()
}
override fun onAnimationEnd(animation: Animator?) {
if (heartDrawing) {
heartDrawing = false
animation?.duration = 3_000
animation?.start()
}
}
override fun onAnimationStart(animation: Animator?) {
}
override fun onAnimationRepeat(animation: Animator?) {
}
override fun onAnimationCancel(animation: Animator?) {
}
}
这样就实现了, 是不是觉得很简单.