大家可以找本书翻页到一半看下效果,从右下角翻到一半时,我们可以将可视区域分为下图A、B、C三部分区域。
a点和f已知,连接af,我们令g点为af的中点,过g点连接eh垂直af,为af中垂线,可得g=Point((a.x+f.x)/2,(a.y+f.y)/2);
并且知道△egf、△emg、△mfg为三个直角三角形,由直角三角形相似原理可知这三个三角型两两相似,所以,△emg相似△mfg,可知:em/gm=gm/mf;em=gm*gm/mf;因为:gm=f.y-g.y;mf=f.x-g.x;可得e=Point(g.x-(pow(f.y-g.y,2)/(f.x-g.x)),f.y);
同理过g点做fh垂直线可得h点坐标。略...
从上方理论图可知,cdb是一条二阶贝塞尔曲线,控制点为e点,ab和ak为直线线段,接下来我们令n为ag的中点,同理过n点垂直于af连接cj,可知ce等于ef的一半;(可以画辅助线过gf中点垂直af得出)。所以可得c=Point(e.x-(f.x-e.x)/2,f.y);j点坐标同理。略...
接下来我们看下b点,目前我们已知a、e、c、j点坐标,现在b点就是ae和cj的相交点。
那么问题来了:用我们九年义务教育学的数学知识解决以下两个问题。
1、在坐标系中,已知两点(x1,y1)、(x2,y2)坐标,求过这两点直线函数?
2、已知两条直线函数求两条直线的相交点?
我们知道直线函数表达式为:y=kx+b;,假设k为正常值,我们可求得k和b的值,
k相同b不同:平行无交点。k相同b相同:重合。k不同无论b相不相同,相交必有一交点。
那么就可得出b点坐标:(假设k永不相等)
b=Point((b2-b1)/(k1-k2),(b2-b1)/(k1-k2)*k1+b1);k点坐标同理。略...
以上AB区域的关键点已经全部得到了,我们将辅助线去掉将这些点连接起来看下效果。
得到AB区域的同时,我们间接的就得到了C区域,
//mPath为书籍矩形区域PathmPathC=Path.combine(PathOperation.reverseDifference,mPathAB,mPath);接下来将AB区域进行区分,再回到上方,坐标图黄色线条部分,我们可以看到d点和i点坐标。通过原理解析我们可知d点为pe的中点,而p点为cb的中点,那么就可以得出:p.x=(c.x+b.x)/2;,d.x=(p.x+e.x)/2;p.y=(c.y+b.y)/2;,d.y=(p.y+e.y)/2;
同理通过路径联合我们就可以将AB区域进行分开,
PathmPath1=Path();mPath1.moveTo(p.value.d.x,p.value.d.y);mPath1.lineTo(p.value.a.x,p.value.a.y);mPath1.lineTo(p.value.i.x,p.value.i.y);mPath1.close();PathmPathB=Path.combine(PathOperation.intersect,mPathAB,mPath1);得到以下图形,
到这里梳理一下,目前我们A、B、C三个path路径区域已经全部得到,剩下的就是填充书籍颜色,接下来我们将画笔设置为填充不同颜色,通过手势不断变化a点坐标看下效果。
是不是有点翻书的意思了,这里有一个问题,书籍的左下角也就是c点坐标在我们翻页的过程中会跑到页面之外,一般书籍都是左侧装订,这里我们希望达到一个真实的翻页效果就需要将c点的x轴最小值设置为书籍最左侧0。
从而得到,fb1=fb*fc1/fc;,
已知:fb=f.x-a.x;fc1=size.width;fc=f.x-c.x;
同理fd1/fd=fb1/fb;得到,fd1=fb1*fd/fb;即可得到a1点坐标。
计算代码:
doublefc=f.x-cx;doublefa=f.x-a.x;doublebb1=size.width*fa/fc;doublefd1=f.y-a.y;doublefd=bb1*fd1/fa;a1=Point(f.x-bb1,f.y-fd);这时候我们再来看下效果,
c点坐标被我们设定最小值为书籍最左侧,所以左侧不会被翻出区域,看起来更像真实的翻页效果。
我们可以在灯光下找本书翻页看下阴影效果,差不多是这个样子,这里我将阴影分为三个部分,A区域两个和C区域一个。
我们先添加A左区域的阴影,A左区域的阴影可以认为是从ha方向由h向a进行色值渐变,所以这里我们需要得到A左阴影区域左上角坐标点,也就是ha直线向外延伸固定数值的坐标。
已知ha直线方程式和a点坐标,以a为圆心,画半径为r(r>0)的圆,
求:此圆和ha直线的相交的坐标。
设交点为坐标xy,可得x2+y2=r2;y=kx+b;(k、b、r)已知,最终我们得到一个一元二次方程。会解出两个坐标点,这里我们只需要往外延伸的坐标点就行,具体可以跟a点坐标判断得出,之后我们令doublem1=a.x-p1.x;doublen1=a.y-p1.y;
那么阴影外部曲线就可以用下方代码表示。
这里我设置了由black26,向透明渐变。延伸长度为10的效果,这里可以根据半径和色值调整影深。
A右同理,略...
接下来我们绘制C区域的阴影,C区域可以看到他是跟eh是平行的,那么我们连接c、j、h、e点,
继续与AB区域进行路径联合,
Pathp1=Path.combine(PathOperation.intersect,pr,mPathAB);得到下面效果:
继续与B区域再次联合,
Pathp2=Path.combine(PathOperation.difference,p1,mPathB);最终得到我们想要的阴影区域。
接下来就是跟A区域操作一样了,设置线性渐变色和渐变方向,这里渐变方向的坐标点我们为u点和g点,g点已知,主要求u点坐标,u点坐标为af和di直线的相交点。
通过两条直线方程求相交点,得到u点以后,设置渐变色和渐变方向。
核心代码:
//右下Pathpc=Path();pc.moveTo(p.value.c.x,p.value.c.y);pc.lineTo(p.value.j.x,p.value.j.y);pc.lineTo(p.value.h.x,p.value.h.y);pc.lineTo(p.value.e.x,p.value.e.y);pc.close();Pathp1=Path.combine(PathOperation.intersect,pc,mPathA);Pathp2=Path.combine(PathOperation.difference,p1,mPathB);Offsetu=Offset(PaperPoint.toTwoPoint(p.value.a,p.value.f,p.value.d,p.value.i).x,PaperPoint.toTwoPoint(p.value.a,p.value.f,p.value.d,p.value.i).y);canvas.drawPath(p2,paint..style=PaintingStyle.fill..shader=ui.Gradient.linear(u,Offset(p.value.g.x,p.value.g.y),[Colors.black26,Colors.transparent]));最后得到我们最终的效果。
目的:我们希望可以滑动过程中页码可以自动翻过去,并且误触的情况下不要翻页。
这里我简单的判断当翻过去书籍宽度的3/1就理解为用户想翻页,当手势松开时自动翻过去;当翻过去书籍宽度小于1/3,理解为用户误触并不想翻页,当手势松开自动回弹回去。
初始化动画
回弹动画,我们希望松开手指时,a点坐标回到和f点重合,这里我们需要在点击或移动的过程中保存当前手指触摸的坐标a,
最后一步,填充内容,模拟书籍嘛,当然不能是这些纯色翻页了,上面我们有了ABC三个路径的区域,接下来就需要对书籍内容Widget进行裁剪,这里我们需要路径裁剪类ClipPath类,
ok,有了方法,接下来我们开始实现,首先我们将之前A区域的Path路径拿出来,裁剪当前页,通过Stack帧布局加载当前页和下一页内容,下一页内容永远在第一页内容下面,当翻过去动画结束时将下方页置位当前页,刷新第二页数据。
翻页动画结束当前页index+1;
if(status==AnimationStatus.completed){if(!isNext){setState((){currentIndex++;});}}填充内容布局代码:
上面只有翻页,没有返回上一页,其实返回上一页也很简单,上面我们实现了回弹动画,这里只需要修改当前a点坐标为为书籍左侧外面,之后调用回弹动画,当前页面-1即可。非常简单。
ElevatedButton(onPressed:(){setState((){//表示从页面左侧外面开始回弹currentA=Point(-100,size.height-100);currentIndex--;//回弹动画isNext=false;});//_p.value=PaperPoint(currentA,size);_controller.forward(from:0,);},child:Text("上一页"))下面再看下最终效果:
这里示例只是简单的填充了一个Text文本,更多内容也是可以的,毕竟裁剪的是个Widget。