持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第1天,点击查看活动详情
牙叔教程 简单易懂
正常动画速度500ms
慢速动画3秒
思路
- 绘制背景, 白色的圆角矩形
- 绘制爱心, 红色的心形
- 添加点击动画
绘制白色的矩形
drawBg(that.roundRectPaint, that.fraction, canvas, that.width, that.height);
是已经封装好的代码, 就是画个圆角矩形
function drawBg(paint, fraction, canvas, width, height) {
let left = 0;
let top = 0;
let right = width;
let bottom = height;
// scale
let scaleX = leap(1, 0.9, 1, fraction);
let scaleY = scaleX;
let pivotX = width / 2;
let pivotY = height / 2;
canvas.scale(scaleX, scaleY, pivotX, pivotY);
let radius = 100;
canvas.drawRoundRect(left, top, right, bottom, radius, radius, paint);
}
其中有5个参数
function drawBg(paint, fraction, canvas, width, height)
paint是画笔
fraction是控制Canvas缩放的
canvas是画板
width, height是矩形的宽高
fraction是这里面最关键的, 因为我们这个教程主要就是写动画;
动画的主要形式就是控制canvas的缩放
fraction主要用在leap函数里面, 我们看看leap是什么?
leap
function leap(a, b, c, fraction) {
if (fraction <= 0.5) {
return MathUtils.lerp(a, b, fraction * 2);
} else {
let tempFraction = fraction - 0.5;
return MathUtils.lerp(b, c, tempFraction * 2);
}
}
leap返回一个数字, 用这个数字来控制动画;
数字是用lerp计算出来的
lerp
com.google.android.material.math.MathUtils.lerp
我们去安卓官网看看这个方法
public static float lerp (float start,
float stop,
float amount)
Returns the linear interpolation of amount between start and stop.
返回两个数字时间的线性插值
Canvas
canvas从哪里来的?
canvas是自定义控件的参数, 是onDraw的参数
CustomView.prototype.render = function () {
let that = this;
return JavaAdapter(
android.view.View,
{
onDraw: function (canvas) {...}
我们绘制矩形和心形都是在 onDraw 方法中绘制;
绘制矩形的时候, 要知道矩形的宽高;
宽高要测量, 测量方法是 onMeasure
onMeasure: function (widthMeasureSpec, heightMeasureSpec) {
this.super$onMeasure(heightMeasureSpec, widthMeasureSpec);
this.setMeasuredDimension(this.getMeasuredHeight(), this.getMeasuredWidth());
that.width = this.getMeasuredWidth();
that.height = this.getMeasuredHeight();
},onMeasure: function (widthMeasureSpec, heightMeasureSpec) {
心形
绘制完矩形, 我们要绘制心形;
心形使用的是内置icon,
let drawableId = "ic_favorite_black_48dp";
let imgId = resources.getIdentifier(drawableId, "drawable", context.getPackageName());
let drawable = resources.getDrawable(imgId);
let bitmap = drawable.getBitmap();
这时候的bitmap不能直接使用, 我们要复制一下bitmap
bitmap = bitmap.copy(Bitmap.Config.ARGB_8888, true);
放大心形图片
内置的icon一般都很小, 我们要把心形放大到矩形大小
var img = com.stardust.autojs.core.image.ImageWrapper.ofBitmap(bitmap);
let width = that.width;
let height = that.height;
let fx = 1;
if (width > height) {
fx = height / img.width;
} else {
fx = width / img.width;
}
let fy = fx;
let img2 = images.scale(img, fx, fy);
要及时回收图片
events.on("exit", function () {
img2.recycle();
});
setTimeout(() => {
img.recycle();
}, 666);
图片搞好以后, 我们还要给图片设置颜色
图片着色
用放大后的心形图片, 创建一个canvas实例,
let srcInMode = PorterDuffXfermode(PorterDuff.Mode.SRC_IN);
// let drawableCanvas = Canvas(iconBitmap);
let drawableCanvas = null;
function tintBitmap(paint, iconBitmap) {
// change to src in
paint.xfermode = srcInMode;
drawableCanvas.drawRect(0, 0, iconBitmap.width, iconBitmap.height, paint);
paint.xfermode = null;
}
这里用的模式是 SRC_IN , 只显示两者相交的部分, 且显示前景色, 也就是我们设置的颜色
动画控制
使用的是 ValueAnimator
// 创建动画
function createAnimator(that) {
//动画
let animator = ValueAnimator.ofFloat(0, 1);
animator.setDuration(config.duration);
animator.setInterpolator(new BounceInterpolator());
animator.addListener(
new AnimatorListenerAdapter({
onAnimationStart: function (animation) {
that.uiState = UIState.Animating;
},
onAnimationEnd: function (animation) {
that.uiState = that.uiStateStart == UIState.Like ? UIState.UnLike : UIState.Like;
},
onAnimationCancel: function (animation) {},
})
);
animator.addUpdateListener(
new ValueAnimator.AnimatorUpdateListener({
onAnimationUpdate: function (valueAnimator) {
let value = valueAnimator.getAnimatedValue();
that.fraction = value;
that.view.invalidate();
},
})
);
return animator;
}
ValueAnimator始终是从0变化到1, 然后赋值给 fraction ,
fraction 变化, 又会引起 scaleX 的变化, scaleX又是 canvas.scale 的参数
canvas.scale(scaleX, scaleY, pivotX, pivotY);
每次绘制矩形和心形之前, 都会先缩放canvas
并且修改了默认的差值器, 使用的是 BounceInterpolator , 弹跳插值器, 模拟自由落体后的回弹动画
animator.setInterpolator(new BounceInterpolator());
自定义控件结构
(function () {
util.extend(CustomView, ui.Widget);
function CustomView() {
ui.Widget.call(this);
}
CustomView.prototype.render = function () {
return JavaAdapter(
android.view.View,
{
onDraw: function (canvas) {
this.super$onDraw(canvas);
...
},
onMeasure: function (widthMeasureSpec, heightMeasureSpec) {
this.super$onMeasure(heightMeasureSpec, widthMeasureSpec);
this.setMeasuredDimension(this.getMeasuredHeight(), this.getMeasuredWidth());
},
onSizeChanged: function (w, h, oldW, oldH) {
this.super$onSizeChanged(w, h, oldW, oldH);
},
},
activity
);
};
CustomView.prototype.onViewCreated = function (view) {};
CustomView.prototype.onFinishInflation = function (view) {};
ui.registerWidget("like-button", CustomView);
return CustomView;
})();
以上, 就是一个点赞动画的关键知识
测试环境
手机: Mi 11 Pro
Android版本: 12
Autojs版本: 9.1.17
名人名言
思路是最重要的, 其他的百度, bing, stackoverflow, github, 安卓文档, autojs文档, 最后才是群里问问 --- 牙叔教程
声明
部分内容来自网络 本教程仅用于学习, 禁止用于其他用途\
微信公众号 牙叔教程