一、前言
打开天猫随便搜索一下,就会出现一个天猫LOGO的加载图
模拟效果:
gif图的效果不是很好,手机上运行效果比较好。
二、实现思路
先推荐两篇文章了解一下贝塞尔曲线:
http://www.gcssloop.com/customview/Path_Bezier
http://blog.csdn.net/harvic880925/article/details/50995587
可以让美工做一个图,然后转换成SVG,使用一些svg -> path的库转换成Path,再使用动画展示出来。不过这LOGO看上去不是很复杂,就自己绘制,弧度都是使用贝塞尔曲线绘制。
1. 知道photoshop中的钢笔工具是用贝塞尔曲线实现的,那首先用PS来简单构建一个模型,确定控制点坐标。
2. 用path.lineTo、quadTo、cubicTo等方法绘制路径。
3. 用PathMeasure类来截取长度,达到闭合图形的缺口目的。
4. ValueAnimator属性动画,动起来。
三、开始绘制
1.用PS画出草图
经过调整,得到以下:
以一个栅格(cell)为基本单位,中心点表示整个图形的中央位置,大概得出整个图形:
长度:12 * cell,高度:8 * cell,为了方便计算和绘制图形,以黑色圆点为坐标原点。
红色原点:坐标点
黄色原点:贝塞尔曲线控制点
p1(cell, 0),p2(5 * cell, -2 * cell),p3(6 * cell, 0),p4(6 * cell,5 * cell),p5(5 * cell, 6 * cell)
c1(3 * cell, 0),c2(4 * cell, -2 * cell),c3(6 * cell, -2 * cell),c4(6 * cell,6 * cell)
左侧和右侧是镜像关系,所以左侧坐标只需要将右侧x坐标取反即可。
2.代码实现
private void initEarsPath() {
p1.x = cellSize ;
p1.y = 0;
p2.x = 5 * cellSize ;
p2.y = -2 * cellSize;
p3.x = 6 * cellSize;
p3.y = 0;
p4.x = 6 * cellSize ;
p4.y = 5 * cellSize ;
p5.x = 5 * cellSize ;
p5.y = 6 * cellSize ;
c1.x = 3 * p1.x ;
c1.y = 0;
c2.x = 4 *cellSize ;
c2.y = -2 * cellSize ;
c3.x = 6 * cellSize;
c3.y = -2 * cellSize ;
c4.x = 6 * cellSize ;
c4.y = 6 * cellSize ;
mPath.moveTo(p1.x, p1.y);
//三阶
mPath.cubicTo(c1.x, c1.y, c2.x,c2.y, p2.x, p2.y);
//二阶
mPath.quadTo(c3.x, c3.y, p3.x, p3.y);
//右侧直线
mPath.lineTo(p4.x, p4.y);
//右下角圆弧
mPath.quadTo(c4.x, c4.y, p5.x, p5.y);
//取反
p1.x = -p1.x;
p2.x = -p2.x;
p3.x = -p3.x;
p4.x = -p4.x;
p5.x = -p5.x;
c1.x = -c1.x;
c2.x = -c2.x;
c3.x = -c3.x;
c4.x = -c4.x;
mPath.lineTo(p5.x, p5.y);
//左下角圆弧
mPath.quadTo(c4.x, c4.y, p4.x, p4.y);
//左侧直线
mPath.lineTo(p3.x, p3.y);
//二阶
mPath.quadTo(c3.x, c3.y, p2.x, p2.y);
//三阶
mPath.cubicTo(c2.x, c2.y, c1.x,c1.y, p1.x, p1.y);
mPath.close();
mMeasure.setPath(mPath, false);
mPathLength = mMeasure.getLength();
}
3.缺口实现
将图形展开成一条直线好分析,开始点和终点其实是一个点,图中绿色表示缺口距离,A点加上间距等于B点,有两种情况。
1.A和B正好在线段的中间
2.B经过了终点(始点)而A还没经过终点
根据这两种情况从原path使用PathMeasure截取出红色部分的内容存放在新的path中,再将新的Path绘制出来就实现缺口。
4.动画
private void initListener() {
mAnimator = ValueAnimator.ofFloat(0.0f, 1.0f);
mAnimator.setDuration(2000);//一段path的时间
mAnimator.setRepeatCount(ValueAnimator.INFINITE);//无限循环
//加入线性插值器 计算返回的值均匀递增 如若不用 会出现不连续现象
mAnimator.setInterpolator(new LinearInterpolator());
mAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
mAnimatorValue = (float) animation.getAnimatedValue();
invalidate();
}
});
mAnimator.start();
}
5.绘制
@Override
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.translate(mViewWidth / 2, mViewHeight / 2 - 2 * cellSize);
mDstPath.reset();
//判断哪种缺口情况
if (mPathLength * mAnimatorValue + distance <= mPathLength) {
mMeasure.getSegment(0, mPathLength * mAnimatorValue, mDstPath, true);
mMeasure.getSegment(mPathLength * mAnimatorValue + distance, mPathLength, mDstPath, true);
} else {
mMeasure.getSegment(distance - mPathLength * (1 - mAnimatorValue), mPathLength * mAnimatorValue, mDstPath, true);
}
canvas.drawPath(mDstPath, mPaint);
}
三、总结
本次主要熟悉使用贝塞尔曲线来绘制路径,通过PS来辅助完成坐标点和控制点的选取,以及跟Path相关的类做出动画效果。