Flutter仿真翻页效果
1-2 月份的闲暇时间,我自己就试着写过仿真翻页效果,但能力有限,写出来的效果不尽人意。可巧的是昨天我就找到别人写的仿真效果的 Flutter 源码。所以也想分享一下。但这个肯定还不是在项目中就直接用的那种,但也相差不多。
效果
源码地址
GitHub 仿真翻页源码地址
https://github.com/CarGuo/gsy_flutter_demo/tree/master/lib/widget/book_pages
全部代码
此处再贴一次,翻页源码。
BookPage
import 'package:flutter/material.dart';
import 'book_painter.dart';
import 'cal_point.dart';
class BookPage extends StatefulWidget {
@override
_BookPageState createState() => _BookPageState();
}
class _BookPageState extends State<BookPage>
with SingleTickerProviderStateMixin {
CalPoint curPoint = CalPoint.data(-1, -1);
CalPoint prePoint = CalPoint.data(-1, -1);
PositionStyle style = PositionStyle.STYLE_LOWER_RIGHT;
double width;
double height;
AnimationController animationController;
Animation cancelAnim;
Tween cancelValue;
bool needCancelAnim = true;
{
if (needCancelAnim) {
startCancelAnim();
else {
{
style = PositionStyle.STYLE_LOWER_RIGHT;
prePoint = CalPoint.data(-1, -1);
curPoint = CalPoint.data(-1, -1);
});
}
}
{
var x = d.localPosition.dx;
var y = d.localPosition.dy;
{
curPoint = CalPoint.data(x, y);
});
}
d) {
prePoint = CalPoint.data(-1, -1);
var dy = d.localPosition.dy;
var dx = d.localPosition.dx;
if (dx <= width / 3) {
//左
style = PositionStyle.STYLE_LEFT;
else if (dx > width / 3 && dy <= height / 3) {
//上
style = PositionStyle.STYLE_TOP_RIGHT;
else if (dx > width * 2 / 3 && dy > height / 3 && dy <= height * 2 / 3) {
//右
style = PositionStyle.STYLE_RIGHT;
else if (dx > width / 3 && dy > height * 2 / 3) {
//下
style = PositionStyle.STYLE_LOWER_RIGHT;
else if (dx > width / 3 &&
dx < width * 2 / 3 &&
dy > height / 3 &&
dy < height * 2 / 3) {
//中
style = PositionStyle.STYLE_MIDDLE;
}
var x = d.localPosition.dx;
var y = d.localPosition.dy;
{
curPoint = CalPoint.data(x, y);
});
}
{
double dx, dy;
if (style == PositionStyle.STYLE_TOP_RIGHT) {
dx = (width - 1 - prePoint.x);
dy = (1 - prePoint.y);
else if (style == PositionStyle.STYLE_LOWER_RIGHT) {
dx = (width - 1 - prePoint.x);
dy = (height - 1 - prePoint.y);
else {
dx = prePoint.x - width;
dy = -prePoint.y;
}
cancelValue =
Offset(prePoint.x, prePoint.y), end: Offset(dx, dy)); :
animationController.forward();
}
{
animationController = new AnimationController(
vsync: this, duration: Duration(milliseconds: 300));
cancelAnim = animationController.drive(CurveTween(curve: Curves.linear));
cancelAnim
{
if (animationController.isAnimating) {
{
var bdx = cancelValue.begin.dx;
var bdy = cancelValue.begin.dy;
var edx = cancelValue.end.dx;
var edy = cancelValue.end.dy;
curPoint = CalPoint.data(
bdx + edx * cancelAnim.value, bdy + edy * cancelAnim.value);
});
}
})
{
if (status == AnimationStatus.completed) {
print("object");
{
style = PositionStyle.STYLE_LOWER_RIGHT;
prePoint = CalPoint.data(-1, -1);
curPoint = CalPoint.data(-1, -1);
animationController.reset();
});
}
});
}
@override
void initState() {
super.initState();
_initCancelAnim();
}
@override
Widget build(BuildContext context) {
width = MediaQuery.of(context).size.width;
height = MediaQuery.of(context).size.height - kToolbarHeight;
Color bgColor = Colors.tealAccent;
return Scaffold(
appBar: AppBar(
title: new Text("BookPage"),
),
body: Container(
height: height,
width: width,
child: GestureDetector(
onTapDown: toDown,
onTapUp: toNormal,
onPanEnd: (detail) {
needCancelAnim = false;
if (IsRight) {
needCancelAnim = false;
} else {
needCancelAnim = true;
}
toNormal();
},
onPanCancel: toNormal,
onPanUpdate: (detail) {
toDragUpdate(detail);
},
child: CustomPaint(
painter: BookPainter(
text: content,
text2: content2,
viewWidth: width,
viewHeight: height,
cur: curPoint,
pre: prePoint,
style: style,
bgColor: bgColor,
frontColor: Colors.yellow,
limitAngle: true,
changedPoint: (pre) {
prePoint = pre;
},
),
),
),
),
);
}
}
const content = """林语堂\n
一、腰有十文钱必振衣作响;\n
二、每与人言必谈及贵戚;\n
三、遇美人必急索登床;\n
四、见到问路之人必作傲睨之态;\n
五、与朋友相聚便喋喋高吟其酸腐诗文;\n
六、头已花白却喜唱艳曲;\n
七、施人一小惠便广布于众;\n
八、与人交谈便借刁言以逞才;\n
九、借人之债时其脸如丐,被人索偿时则其态如王;\n
十、见人常多蜜语而背地却常揭人短处。""";
const content2 = """林语堂\n
1.人本过客来无处,休说故里在何方。随遇而安无不可,人间到处有芳香。——林语堂\n
2.人生不过如此,且行且珍惜。自己永远是自己的主角,不要总在别人的戏剧里充当着配角。——林语堂《人生不过如此》\n
3.最明亮时总是最迷茫,最繁华时也是最悲凉。——林语堂《京华烟云》\n
4.理想的人并不是完美的人,通常只是受人喜爱,并且通情达理的人,而我只是努力去接近于此罢了。——林语堂""";
BookPainter
import 'package:flutter/material.dart';
import 'dart:ui' as ui;
import 'cal_point.dart';
import 'dart:math' as Math;
///触摸类型
enum PositionStyle {
STYLE_TOP_RIGHT,
STYLE_LOWER_RIGHT,
STYLE_LEFT,
STYLE_RIGHT,
STYLE_MIDDLE,
}
///页面画笔
class BookPainter extends CustomPainter {
CalPoint a, f, g, e, h, c, j, b, k, d, i;
double viewWidth;
double viewHeight;
///顶部区域
Path pathA;
///折叠出来的区域
Path pathC;
///背部区域
Path pathB;
///背景画笔
Paint bgPaint;
///绘制区域画笔
Paint pathAPaint, pathCPaint, pathBPaint;
///触摸点的区域
PositionStyle style;
///回调数据外放
ValueChanged changedPoint;
///背景色
Color bgColor;
///前景色
Color frontColor;
///文本一
String text;
///文本二
String text2;
///A区域左阴影矩形短边长度参考值
double lPathAShadowDis = 0;
/// A区域右阴影矩形短边长度参考值
double rPathAShadowDis = 0;
BookPainter({
this.text,
this.text2,
this.viewWidth,
this.viewHeight,
this.frontColor,
this.bgColor,
CalPoint cur,
CalPoint pre,
this.changedPoint,
this.style,
bool limitAngle,
}) {
init(cur, pre, limitAngle);
}
bool shouldRepaint(CustomPainter oldDelegate) => true;
void paint(Canvas canvas, Size size) {
onDraw(canvas, size);
}
init(CalPoint cur, CalPoint pre, bool limitAngle) {
///初始化点
_initPoint();
///选择工作模型
_selectCalPoint(cur, pre, limitAngle: limitAngle);
///计算
_calcPointsXY(a, f);
///初始化
_initPaintAndPath();
}
_initPoint() {
///计算起始触摸点
a = CalPoint();
///计算的位置起点
f = CalPoint();
///其他坐标
g = new CalPoint();
e = new CalPoint();
h = new CalPoint();
c = new CalPoint();
j = new CalPoint();
b = new CalPoint();
k = new CalPoint();
d = new CalPoint();
i = new CalPoint();
pathB = new Path();
pathA = new Path();
pathC = new Path();
}
_selectCalPoint(CalPoint cur, CalPoint pre, {bool limitAngle = true}) {
a.x = cur.x;
a.y = cur.y;
doCalAngle() {
CalPoint touchPoint = CalPoint.data(cur.x, cur.y);
if (f.x != null &&
touchPoint.x != null &&
(limitAngle != null && limitAngle)) {
///如果大于0则设置a点坐标重新计算各标识点位置,否则a点坐标不变
if (_calcPointCX(touchPoint, f) > 0) {
changedPoint?.call(cur);
_calcPointsXY(a, f);
} else {
a.x = pre.x;
a.y = pre.y;
_calcPointsXY(a, f);
}
} else if (_calcPointCX(touchPoint, f) < 0) {
///如果c点x坐标小于0则重新测量a点坐标
_calcPointAByTouchPoint();
_calcPointsXY(a, f);
} else {
a.x = pre.x;
a.y = pre.y;
}
}
switch (style) {
case PositionStyle.STYLE_TOP_RIGHT:
f.x = viewWidth;
f.y = 0;
doCalAngle();
break;
case PositionStyle.STYLE_LOWER_RIGHT:
f.x = viewWidth;
f.y = viewHeight;
doCalAngle();
break;
case PositionStyle.STYLE_LEFT:
case PositionStyle.STYLE_RIGHT:
case PositionStyle.STYLE_MIDDLE:
a.y = viewHeight - 1;
f.x = viewWidth;
f.y = viewHeight;
_calcPointsXY(a, f);
break;
default:
break;
}
}
_initPaintAndPath() {
bgPaint = new Paint();
bgPaint.color = Colors.white;
pathAPaint = new Paint();
pathAPaint.color = bgColor;
pathAPaint.isAntiAlias = true;
pathCPaint = new Paint();
pathCPaint.color = frontColor;
pathCPaint.blendMode = BlendMode.dstATop;
pathCPaint.isAntiAlias = true;
pathBPaint = new Paint();
pathBPaint.color = Colors.tealAccent;
pathBPaint.blendMode = BlendMode.dstATop;
pathBPaint.isAntiAlias = true;
pathB = new Path();
}
void onDraw(Canvas canvas, Size size) async {
canvas.saveLayer(Rect.fromLTRB(0, 0, size.width, size.height), bgPaint);
if (a.x == -1 && a.y == -1) {
_drawPathAWithPic(canvas, _getPathDefault());
_drawPathCWithPic(canvas, _getPathDefault(), pathCPaint);
_drawPathBWithPic(canvas, _getPathDefault());
} else {
if (f.x == viewWidth && f.y == 0) {
_drawPathAWithPic(canvas, _getPathAFromTopRight());
_drawPathCWithPic(canvas, _getPathAFromTopRight(), pathCPaint);
_drawPathBWithPic(canvas, _getPathAFromTopRight());
} else if (f.x == viewWidth && f.y == viewHeight) {
_drawPathAWithPic(canvas, _getPathAFromLowerRight());
_drawPathCWithPic(canvas, _getPathAFromLowerRight(), pathCPaint);
_drawPathBWithPic(canvas, _getPathAFromLowerRight());
}
}
canvas.restore();
}
void _drawPathAWithPic(Canvas canvas, Path pp) {
canvas.save();
var pictureRecorder = ui.PictureRecorder();
var canvasBitmap = Canvas(pictureRecorder);
var paint = Paint();
paint.isAntiAlias = true;
canvasBitmap.drawPath(pp, pathAPaint);
_drawText(canvasBitmap, text, Colors.black, viewWidth, Offset.zero);
var pic = pictureRecorder.endRecording();
canvas.clipPath(pp);
canvas.drawPicture(pic);
if (style == PositionStyle.STYLE_LEFT ||
style == PositionStyle.STYLE_RIGHT) {
_drawPathAHorizontalShadow(canvas, pathA);
} else if (a.x != -1 && a.y != -1) {
_drawPathALeftShadow(canvas, pp);
_drawPathARightShadow(canvas, pp);
}
canvas.restore();
}
void _drawPathALeftShadow(Canvas canvas, Path pathA) {
canvas.restore();
canvas.save();
var gradientColors = [Color(0x01333333), Color(0x33333333)];
double left;
double right;
double top = e.y;
double bottom = (e.y + viewHeight);
ui.Gradient gradient;
if (style == PositionStyle.STYLE_TOP_RIGHT) {
left = (e.x - lPathAShadowDis / 2);
right = (e.x);
gradient = ui.Gradient.linear(
Offset(left, top), Offset(right, top), gradientColors);
} else {
left = (e.x);
right = (e.x + lPathAShadowDis / 2);
gradient = ui.Gradient.linear(
Offset(right, top), Offset(left, top), gradientColors);
}
Paint paint = new Paint()..shader = gradient;
//裁剪出我们需要的区域
Path mPath = new Path();
mPath.moveTo(a.x - Math.max(rPathAShadowDis, lPathAShadowDis) / 2, a.y);
mPath.lineTo(d.x, d.y);
mPath.lineTo(e.x, e.y);
mPath.lineTo(a.x, a.y);
mPath.close();
//canvas.clipPath(pathA);
var pn = Path.combine(PathOperation.intersect, pathA, mPath);
canvas.clipPath(pn);
canvas.translate(e.x, e.y);
canvas.rotate(Math.atan2(e.x - a.x, a.y - e.y));
canvas.translate(-e.x, -e.y);
var rect = Rect.fromLTRB(left, top, right, bottom);
canvas.drawRect(rect, paint);
}
void _drawPathARightShadow(Canvas canvas, Path pathA) {
canvas.restore();
canvas.save();
var gradientColors = [
Color(0x33333333),
Color(0x01333333),
];
double viewDiagonalLength = _hypot(viewWidth, viewHeight); //view对角线长度
double left = h.x;
double right = (h.x + viewDiagonalLength * 10); //需要足够长的长度
double top;
double bottom;
ui.Gradient gradient;
if (style == PositionStyle.STYLE_TOP_RIGHT) {
top = (h.y - rPathAShadowDis / 2);
bottom = h.y;
gradient = ui.Gradient.linear(
Offset(left, bottom), Offset(left, top), gradientColors);
} else {
top = h.y;
bottom = (h.y + rPathAShadowDis / 2);
gradient = ui.Gradient.linear(
Offset(left, top), Offset(left, bottom), gradientColors);
}
Paint paint = new Paint()..shader = gradient;
Path mPath = new Path();
mPath.moveTo(a.x - Math.max(rPathAShadowDis, lPathAShadowDis) / 2, a.y);
// mPath.lineTo(i.x,i.y);
mPath.lineTo(h.x, h.y);
mPath.lineTo(a.x, a.y);
mPath.close();
var pn = Path.combine(PathOperation.intersect, pathA, mPath);
canvas.clipPath(pn);
canvas.translate(h.x, h.y);
canvas.rotate(Math.atan2(a.y - h.y, a.x - h.x));
canvas.translate(-h.x, -h.y);
var rect = Rect.fromLTRB(left, top, right, bottom);
canvas.drawRect(rect, paint);
}
void _drawPathAHorizontalShadow(Canvas canvas, Path pathA) {
canvas.restore();
canvas.save();
var radientColors = [Color(0x01333333), Color(0x44333333)]; //渐变颜色数组
double maxShadowWidth = 30; //阴影矩形最大的宽度
double left = (a.x - Math.min(maxShadowWidth, (rPathAShadowDis / 2)));
double right = (a.x);
double top = 0;
double bottom = viewHeight;
ui.Gradient gradient = ui.Gradient.linear(
Offset(left, top), Offset(right, top), radientColors);
Paint paint = new Paint()..shader = gradient;
canvas.clipPath(pathA);
canvas.translate(a.x, a.y);
canvas.rotate(Math.atan2(f.x - a.x, f.y - h.y));
canvas.translate(-a.x, -a.y);
var rect = Rect.fromLTRB(left, top, right, bottom);
canvas.drawRect(rect, paint);
}
void _drawPathBWithPic(Canvas canvas, Path pa) {
canvas.save();
var pictureRecorder = ui.PictureRecorder();
var canvasBitmap = Canvas(pictureRecorder);
var paint = Paint();
paint.isAntiAlias = true;
canvasBitmap.drawPath(_getPathB(), pathBPaint);
_drawText(canvasBitmap, text2, Colors.black, viewWidth, Offset.zero);
var pic = pictureRecorder.endRecording();
var pn = Path.combine(PathOperation.reverseDifference, pa, _getPathB());
pn = Path.combine(PathOperation.reverseDifference, _getPathC(), pn);
canvas.clipPath(pn);
canvas.drawPicture(pic);
_drawPathBShadow(canvas); //调用阴影绘制方法
canvas.restore();
}
void _drawPathBShadow(Canvas canvas) {
var gradientColors = [Color(0xf0111111), Color(0x00000000)]; //渐变颜色数组
int elevation = 6;
int deepOffset = 0; //深色端的偏移值
int lightOffset = 0; //浅色端的偏移值
double aTof = _hypot((a.x - f.x), (a.y - f.y)); //a到f的距离
double viewDiagonalLength = _hypot(viewWidth, viewHeight); //对角线长度
double left;
double right;
double top = c.y;
double bottom = (viewDiagonalLength + c.y);
ui.Gradient gradient;
if (style == PositionStyle.STYLE_TOP_RIGHT) {
//f点在右上角
//从左向右线性渐变
left = (c.x - deepOffset); //c点位于左上角
right = (c.x + aTof / elevation + lightOffset);
gradient = ui.Gradient.linear(
Offset(left, top), Offset(right, top), gradientColors);
} else {
left = (c.x - aTof / elevation - lightOffset); //c点位于左下角
right = (c.x + deepOffset);
gradient = ui.Gradient.linear(
Offset(right, top), Offset(left, top), gradientColors);
}
Paint paint = new Paint()
//..color = Colors.black.withAlpha(80);
//..blendMode = BlendMode.srcOver
..shader = gradient;
canvas.translate(c.x, c.y);
canvas.rotate(Math.atan2(e.x - f.x, h.y - f.y));
canvas.translate(-c.x, -c.y);
var rect = Rect.fromLTRB(left, top, right, bottom);
canvas.drawRect(rect, paint);
}
void _drawPathCWithPic(Canvas canvas, Path pathA, Paint pathPaint) {
canvas.drawPath(_getPathC(), pathPaint);
canvas.save();
var pictureRecorder = ui.PictureRecorder();
var canvasBitmap = Canvas(pictureRecorder);
canvasBitmap.drawPath(_getPathB(), pathPaint);
var pic = pictureRecorder.endRecording();
var pn = Path.combine(PathOperation.reverseDifference, pathA, _getPathC());
canvas.clipPath(pn);
double eh = _hypot(f.x - e.x, h.y - f.y);
double sin0 = (f.x - e.x) / eh;
double cos0 = (h.y - f.y) / eh;
//设置翻转和旋转矩阵
var mMatrixArray = [
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.1,
0.0,
0.0,
0.0,
0.0,
0.1
];
mMatrixArray[0] = -(1 - 2 * sin0 * sin0);
mMatrixArray[1] = 2 * sin0 * cos0;
mMatrixArray[4] = 2 * sin0 * cos0;
mMatrixArray[5] = 1 - 2 * sin0 * sin0;
Matrix4 mMatrix = Matrix4.fromList(mMatrixArray);
mMatrix.leftTranslate(e.x, e.y);
mMatrix.translate(-e.x, -e.y);
canvas.transform(mMatrix.storage);
canvas.drawPicture(pic);
_drawPathCShadow(canvas); //调用阴影绘制方法
canvas.restore();
}
void _drawPathCShadow(Canvas canvas) {
var gradientColors = [Color(0x00333333), Color(0xff111111)]; //渐变颜色数组
int deepOffset = 1; //深色端的偏移值
int lightOffset = -30; //浅色端的偏移值
var viewDiagonalLength = _hypot(viewWidth, viewHeight); //view对角线长度
double midpointCe = (c.x + e.x) / 2; //ce中点
double midpointJh = (j.y + h.y) / 2; //jh中点
double minDisToControlPoint = Math.min(
(midpointCe - e.x).abs(), (midpointJh - h.y).abs()); //中点到控制点的最小值
double left;
double right;
double top = c.y;
double bottom = (viewDiagonalLength + c.y);
ui.Gradient gradient;
if (style == PositionStyle.STYLE_TOP_RIGHT) {
left = (c.x - lightOffset);
right = (c.x + minDisToControlPoint + deepOffset);
gradient = ui.Gradient.linear(
Offset(left, top), Offset(right, top), gradientColors);
} else {
left = (c.x - minDisToControlPoint - deepOffset);
right = (c.x + lightOffset);
gradient = ui.Gradient.linear(
Offset(right, top), Offset(left, top), gradientColors);
}
Paint paint = new Paint()..shader = gradient;
canvas.translate(c.x, c.y);
canvas.rotate(Math.atan2(e.x - f.x, h.y - f.y));
canvas.translate(-c.x, -c.y);
var rect = Rect.fromLTRB(left, top, right, bottom);
canvas.drawRect(rect, paint);
}
/// 计算各点坐标
void _calcPointsXY(CalPoint a, CalPoint f) {
g.x = (a.x + f.x) / 2;
g.y = (a.y + f.y) / 2;
e.x = g.x - (f.y - g.y) * (f.y - g.y) / (f.x - g.x);
e.y = f.y;
h.x = f.x;
h.y = g.y - (f.x - g.x) * (f.x - g.x) / (f.y - g.y);
c.x = e.x - (f.x - e.x) / 2;
c.y = f.y;
j.x = f.x;
j.y = h.y - (f.y - h.y) / 2;
b = _getInterPoint(a, e, c, j);
k = _getInterPoint(a, h, c, j);
d.x = (c.x + 2 * e.x + b.x) / 4;
d.y = (2 * e.y + c.y + b.y) / 4;
i.x = (j.x + 2 * h.x + k.x) / 4;
i.y = (2 * h.y + j.y + k.y) / 4;
double lA = a.y - e.y;
double lB = e.x - a.x;
double lC = a.x * e.y - e.x * a.y;
lPathAShadowDis = ((lA * d.x + lB * d.y + lC) / (_hypot(lA, lB)).abs());
double rA = a.y - h.y;
double rB = h.x - a.x;
double rC = a.x * h.y - h.x * a.y;
rPathAShadowDis = ((rA * i.x + rB * i.y + rC) / _hypot(rA, rB)).abs();
}
/// 计算两线段相交点坐标
CalPoint _getInterPoint(
CalPoint lineOneToPointOne,
CalPoint lineOneToPointTwo,
CalPoint lineTwoToPointOne,
CalPoint lineTwoToPointTwo) {
double x1, y1, x2, y2, x3, y3, x4, y4;
x1 = lineOneToPointOne.x;
y1 = lineOneToPointOne.y;
x2 = lineOneToPointTwo.x;
y2 = lineOneToPointTwo.y;
x3 = lineTwoToPointOne.x;
y3 = lineTwoToPointOne.y;
x4 = lineTwoToPointTwo.x;
y4 = lineTwoToPointTwo.y;
double pointX =
((x1 - x2) * (x3 * y4 - x4 * y3) - (x3 - x4) * (x1 * y2 - x2 * y1)) /
((x3 - x4) * (y1 - y2) - (x1 - x2) * (y3 - y4));
double pointY =
((y1 - y2) * (x3 * y4 - x4 * y3) - (x1 * y2 - x2 * y1) * (y3 - y4)) /
((y1 - y2) * (x3 - x4) - (x1 - x2) * (y3 - y4));
return CalPoint.data(pointX, pointY);
}
///获取f点在右下角的pathA
Path _getPathAFromLowerRight() {
pathA.reset();
pathA.lineTo(0, viewHeight); //移动到左下角
pathA.lineTo(c.x, c.y); //移动到c点
pathA.quadraticBezierTo(e.x, e.y, b.x, b.y); //从c到b画贝塞尔曲线,控制点为e
pathA.lineTo(a.x, a.y); //移动到a点
pathA.lineTo(k.x, k.y); //移动到k点
pathA.quadraticBezierTo(h.x, h.y, j.x, j.y); //从k到j画贝塞尔曲线,控制点为h
pathA.lineTo(viewWidth, 0); //移动到右上角
pathA.close(); //闭合区域
return pathA;
}
///获取f点在右上角的pathA
Path _getPathAFromTopRight() {
pathA.reset();
pathA.lineTo(c.x, c.y); //移动到c点
pathA.quadraticBezierTo(e.x, e.y, b.x, b.y); //从c到b画贝塞尔曲线,控制点为e
pathA.lineTo(a.x, a.y); //移动到a点
pathA.lineTo(k.x, k.y); //移动到k点
pathA.quadraticBezierTo(h.x, h.y, j.x, j.y); //从k到j画贝塞尔曲线,控制点为h
pathA.lineTo(viewWidth, viewHeight); //移动到右下角
pathA.lineTo(0, viewHeight); //移动到左下角
pathA.close();
return pathA;
}
///翻页折角区域
Path _getPathC() {
pathC.reset();
pathC.moveTo(i.x, i.y); //移动到i点
pathC.lineTo(d.x, d.y); //移动到d点
pathC.lineTo(b.x, b.y); //移动到b点
pathC.lineTo(a.x, a.y); //移动到a点
pathC.lineTo(k.x, k.y); //移动到k点
pathC.close(); //闭合区域
return pathC;
}
///翻页后剩余区域
Path _getPathB() {
pathB.reset();
pathB.lineTo(0, viewHeight); //移动到左下角
pathB.lineTo(viewWidth, viewHeight); //移动到右下角
pathB.lineTo(viewWidth, 0); //移动到右上角
pathB.close(); //闭合区域
return pathB;
}
///绘制默认的界面
Path _getPathDefault() {
pathA.reset();
pathA.lineTo(0, viewHeight);
pathA.lineTo(viewWidth, viewHeight);
pathA.lineTo(viewWidth, 0);
pathA.close();
return pathA;
}
///计算C点的X值
_calcPointCX(CalPoint a, CalPoint f) {
CalPoint g, e;
g = new CalPoint();
e = new CalPoint();
g.x = (a.x + f.x) / 2;
g.y = (a.y + f.y) / 2;
e.x = g.x - (f.y - g.y) * (f.y - g.y) / (f.x - g.x);
e.y = f.y;
return e.x - (f.x - e.x) / 2;
}
///如果c点x坐标小于0,根据触摸点重新测量a点坐标
_calcPointAByTouchPoint() {
double w0 = viewWidth - c.x;
double w1 = (f.x - a.x).abs();
double w2 = viewWidth * w1 / w0;
a.x = (f.x - w2).abs();
double h1 = (f.y - a.y).abs();
double h2 = w2 * h1 / w1;
a.y = (f.y - h2).abs();
}
///利用 Paragraph 实现 _drawText
_drawText(
Canvas canvas, String text, Color color, double width, Offset offset,
{TextAlign textAlign = TextAlign.start, double fontSize}) {
ui.ParagraphBuilder pb = ui.ParagraphBuilder(ui.ParagraphStyle(
textAlign: textAlign,
fontSize: fontSize,
));
pb.pushStyle(ui.TextStyle(color: color));
pb.addText(text);
ui.ParagraphConstraints pc = ui.ParagraphConstraints(width: width);
///这里需要先layout, 后面才能获取到文字高度
ui.Paragraph paragraph = pb.build()..layout(pc);
canvas.drawParagraph(paragraph, offset);
}
//_drawTestPoint() {
// _drawText(canvas, "a", Colors.red, size.width, Offset(a.x, a.y),
// textAlign: TextAlign.left, fontSize: 25);
// _drawText(canvas, "f", Colors.red, size.width, Offset(f.x, f.y),
// textAlign: TextAlign.left, fontSize: 25);
// _drawText(canvas, "g", Colors.red, size.width, Offset(g.x, g.y),
// textAlign: TextAlign.left, fontSize: 25);
//
// _drawText(canvas, "e", Colors.red, size.width, Offset(e.x, e.y),
// textAlign: TextAlign.left, fontSize: 25);
// _drawText(canvas, "h", Colors.red, size.width, Offset(h.x, h.y),
// textAlign: TextAlign.left, fontSize: 25);
//
// _drawText(canvas, "c", Colors.red, size.width, Offset(c.x, c.y),
// textAlign: TextAlign.left, fontSize: 25);
// _drawText(canvas, "j", Colors.red, size.width, Offset(j.x, j.y),
// textAlign: TextAlign.left, fontSize: 25);
//
// _drawText(canvas, "b", Colors.red, size.width, Offset(b.x, b.y),
// textAlign: TextAlign.left, fontSize: 25);
// _drawText(canvas, "k", Colors.red, size.width, Offset(k.x, k.y),
// textAlign: TextAlign.left, fontSize: 25);
//
// _drawText(canvas, "d", Colors.red, size.width, Offset(d.x, d.y),
// textAlign: TextAlign.left, fontSize: 25);
// _drawText(canvas, "i", Colors.red, size.width, Offset(i.x, i.y),
// textAlign: TextAlign.left, fontSize: 25);
// }
num _hypot(num x, num y) {
var first = x.abs();
var second = y.abs();
if (y > x) {
first = y.abs();
second = x.abs();
}
if (first == 0.0) {
return second;
}
final t = second / first;
return first * Math.sqrt(1 + t * t);
}
}
BookTextClip
import 'package:flutter/material.dart';
class BookTextClip extends CustomClipper<Path> {
final Path path;
BookTextClip(this.path);
Path getClip(Size size) {
return path;
}
bool shouldReclip(CustomClipper<Path> oldClipper) {
return true;
}
}
CalPoint
class CalPoint {
CalPoint();
CalPoint.data(this.x, this.y);
double x = -1;
double y = -1;
}
评论