共计 2151 个字符,预计需要花费 6 分钟才能阅读完成。
通过自定义 View+ 属性动画 实现一个会动鱼
剖析:
1. 画一条鱼
2. 鱼原地动
3. 鱼向点击处游动
画一条鱼
鱼分为:鱼头(圆)+ 身材(两条直线 + 两条贝塞尔曲线)+ 鱼鳍(一条直线 + 一个贝塞尔)+ 尾巴(两三角)+ 节肢 *2(梯形 + 两圆)
先把鱼程度朝右,画一个坐标系,鱼的重心为坐标系核心
- 先定下鱼的重心的坐标
头圆半径的 4.19 倍,这个其实是本人定的,5f,6f 都行,就是只扭转鱼的长度,用鱼头半径做初始单位有利于扭转整个鱼的大小。
- 因为重心坐标定下了,所以整个鱼的母布局 ImageView 的宽高,重心的两倍(鱼左右转都不会超出边界)
- 重点!求一个点的坐标。已知一个点、夹角、长度。求一个点的坐标
初中常识:
所以能够得出:入参一个点、两点长度、对于 x 轴的角度。返回值 一个坐标
- 依据这个方程求解各个鱼身材。
定义画笔
mPaint.setDither(true); 防抖动
mPaint.setAntiAlias(true); 抗锯齿
画鱼头:找到鱼头圆心,入参:重心、鱼身长一半、鱼的朝向(默认 180 跟重心一个方向)
画鱼鳍
鱼鳍是一个直线 + 一个二阶贝塞尔曲线,所以重点就是求出三个点:鱼鳍左点、右点、贝塞尔控制点
通过鱼头的圆心求,间隔 0.9 * R,角度 110。通过那个公式就能求进去 = 右鱼鳍点
左鱼鳍点 = 右鱼鳍点、间隔、角度 -180
贝塞尔控制点 =(这个齐全靠本人试,只影响鱼鳍的胖瘦)右鱼鳍点、间隔 * 1.8f、角度 115
试贝塞尔的网站:cubic-bezier.com/#.17,.67,.8…
三个点都有了,绘制鱼鳍:
绘制之前要将其余绘制重置:mPath.reset();
而后 mPath.moveTo()挪动到第一个点
mPath.lineTo() 画直线
mPath.quadTo() 画二阶贝塞尔曲线,入参第二个点、第三个点
最初 canvas.drawPath(mPath,mPaint); 间接画进去
最初一个点不必关闭,零碎主动会关闭。
别的局部也都差不多,依据那个公式,通过参考点求出另一个点,而后求出各个局部的点,最初连线画就行
求出各个点
而后
mPath.reset()、mPath.moveTo、mPath.lineTo、canvas.drawPath
画身材
求出这 6 个点就行
鱼原地摆动
鱼原地摆动须要属性动画 ValueAnimator
ValueAnimator
属性动画,给一个值,始终变,
如:ValueAnimator.ofFloat(0,1f);
就是将一个 float 值 从 0f 变到 1f
setRepeatCount 设置反复次数
setRepeatMode 设置反复模式
setInterpolator 设置插值器
Interpolator(插值器)零碎提供了很多插值器:先减速在加速、减速、匀速、周期静止、先回退再减速、最初弹一下等等。如果不够用能够自定义。
在监听外面能取到以后的值,而后通过invalidateSelf(); 刷新重绘
通过这个变动的值,去扭转鱼头的角度,这能实现鱼的摆动(比方鱼头摆动 5 度,节肢 1 摆动 10 度,节肢 2 摆动 20 度)这样不同的幅度就让鱼动起来。
鱼尾是通过扭转三角的大小来实现的,幅度法则应该跟节肢 2 一样。
鱼尾是上节(节肢 1)带动(节肢 2)来静止的,而且是周期规律性的静止
说到周期性静止,又很平滑。sin、cos 刚好是这样的
所以能够用 sin 代替 0~1f 的动画,这样更加平滑。cos 刚好比 sin 多一个象限,那么节肢 2 刚好能够被节肢 1 带着。
鱼游动
鱼整条都是定义在 FishDrawable 的,用 FishRelativeLayout,addView 去将 ImageView 加进来 FishDrawable
点击水波纹
在 onTouchEvent 里记录下点击(X,Y),而后依据属性动画去扭转圆半径和透明度
ObjectAnimator
ObjectAnimator extends ValueAnimator
成果是一样的,将一个属性从 X 值改到 Y 值。能够不必写监听了 addListener
入参 1. 要扭转的对象,2. 要扭转的值(必须实现 set 办法),3. 初始值,4. 起点值 —(或者门路 Path)
实质其实是利用反射将 第二个参数的 set、get 办法里的值给改了。所以肯定得实现 set、get,不然就报错
鱼的静止轨迹
鱼的运行轨迹为 3 阶贝塞尔曲线,所以要害就是确定 4 个点
已知三点求角度:
重点就是控制点 2 的角度
求出夹角,还得算出与 X 轴的夹角也就是 // AB 连线与 X 的夹角的 tan 值 - OB 与 x 轴的夹角的 tan 值 float direction = (A.y - B.y) / (A.x - B.x) - (O.y - B.y) / (O.x - B.x);
残缺各个点:
最初游动:
path.cubicTo()三阶贝塞尔曲线公式
通过 ObjectAnimator 属性动画扭转 ImageView 的 xy 坐标。
通过 fishDrawable.setFrequence(3f) 让鱼在游动的时候静止的更快
鱼头的朝向,转过来
鱼头的方向 = 静止轨迹的切线的方向,这样就能平滑的转过来了。
通过取到曲线执行的百分比失去
pathMeasure.getPosTan()
tanα 值,而后转换成角度
实现
视频:价值 100w+Android 我的项目实战大全:灵动的锦鲤
原文:https://juejin.cn/post/6976928056740937735