前段时间根据朋友的一个提议做了个翻页动画。效果跟水果忍者切割相似。如图
效果过程大致如下:
1)根据手势痕迹将当前图片分割为两部分;
2)将分割后的图片拉开一段距离,并适度放大图片,以模拟向屏幕外弹起效果,同时露出底层图片;
3)错位移动分割后的图片;
首先遇到的问题是如何对图片进行分割。我选择采用canvas.clipPath来实现;采用这种方法就要先确定PATH中各个节点的坐标;
起初我尝试利用分割线与图片边的交点,以及图片的顶点来确定Path;
如下图:
通过观察,却发现交点可以落在图片任意两条不同的边上,而且落点不同与其构成Path的顶点也不同,情况复杂,于是我果断放弃,另谋他路。一番尝试后,我决定采用以下方式;
设mHW为图片对角线的斜率,mSlope分割线的斜率;;
将切割情况分为,mSlope大于还是小于mHW两种情况进行处理;
先看mSlope大于mHW的情况,如图:
点mDown 和mUp分别表示手指按下和弹起时的位置,两点所在直线即为分割线。分割线与x=-mWidth,x=2mWidth分别交于mX0 和mXw;显然两点坐标可以轻松得到:
private void calcPoints() { mSlope = (mDown.y - mUp.y) / (mDown.x - mUp.x); mX0.x = -mWidth; mX0.y = (-mWidth - mUp.x) * mSlope + mUp.y; mXw.x = 2 * mWidth; mXw.y = mSlope * (2 * mWidth - mUp.x) + mUp.y; }
这样,被分割的两部分路径就总是可以用图中Part1,Part2 两个三角形确定。
Part1的路径为:
if (Math.abs(mSlope) > mHW) { if (mSlope < 0) { path.moveTo(-mWidth, mXw.y); path.lineTo(mXw.x, mXw.y); path.lineTo(mX0.x, mX0.y); path.close(); } else { path.moveTo(-mWidth, mXw.y); path.lineTo(mX0.x, mX0.y); path.lineTo(mXw.x, mXw.y); path.close(); } }
Part2的路径为:
if (Math.abs(mSlope) > mHW) { if (mSlope < 0) { path.moveTo(2 * mWidth, mX0.y); path.lineTo(mXw.x, mXw.y); path.lineTo(mX0.x, mX0.y); path.close(); } else { path.moveTo(2 * mWidth, mX0.y); path.lineTo(mX0.x, mX0.y); path.lineTo(mXw.x, mXw.y); path.close(); } }
而当切割线斜率小于mSlope时,情况与此相似,就不一一赘述了。