在上文「手绘板的制作——画布缩放(4)」中,我们学会了画布的缩放,这节我们学习下画布的移动,毕竟放大的画布不能移动的话,那放大还有什么意义。=_=
既然要移动,那当然需要检测手势,由于单指的移动我们已经处理成手绘了,所以,我们把双指的移动理解为真正的画布移动。
首先,我们需要两个值来保存 x、y 的偏移量:
class PaintedBoardProvider extends ChangeNotifier {
// 偏移量
double translationX = 0;
double translationY = 0;
然后我们需要在 onScaleStart 中存储当前的焦点位置以及当前的偏移量:
class _HandPaintedBoardState extends State {
// 记录缩放开始的坐标
double _startX = 0; // <- 新增
double _startY = 0; // <- 新增
// 记录缩放开始的偏移量
double _startTranslationX = 0; // <- 新增
double _startTranslationY = 0; // <- 新增
@override
Widget build(BuildContext context) {
return GestureDetector(
onScaleStart: (details) {
if (details.pointerCount > 1) { // 双指
_gestureType = GestureType.scale;
_startScale = _paintedBoardProvider.scale;
_startX = details.localFocalPoint.dx; // <- 新增
_startY = details.localFocalPoint.dy; // <- 新增
_startTranslationX = _paintedBoardProvider.translationX; // <- 新增
_startTranslationY = _paintedBoardProvider.translationY; // <- 新增
} else { // 单指
_gestureType = GestureType.translate;
_paintedBoardProvider.onStart(details.localFocalPoint);
}
},
由于我们的画布偏移是跟缩放共用 onScaleUpdate 的,所以,我们需要进行判断,当实际的缩放系数小于等于 0.1 的时候,我们理解为用户想要偏移,当实际的缩放系数大于 0.1 的时候,我们理解为用户想要缩放画布:
onScaleUpdate: (details) {
switch (_gestureType) {
case GestureType.translate:
_paintedBoardProvider.onUpdate(details.localFocalPoint);
break;
case GestureType.scale:
if ((details.scale - 1).abs() > 0.1) {
setState(() {
_paintedBoardProvider.scale = _startScale + details.scale - 1;
});
} else {
setState(() {
_paintedBoardProvider.translationX = _startTranslationX +
details.localFocalPoint.dx - _startX;
_paintedBoardProvider.translationY = _startTranslationY +
details.localFocalPoint.dy - _startY;
});
}
break;
}
},
在上文「手绘板的制作——画布缩放(4)」中,我们的缩放是通过 Transform.scale
来解决的,虽然也有 Transform.translate
来解决偏移的问题,但是由于我们是缩放和偏移都需要的,所以我们直接使用 Transform 来实现:
const Transform({
Key? key,
required this.transform,
this.origin,
this.alignment,
this.transformHitTests = true,
this.filterQuality,
Widget? child,
}) : assert(transform != null),
super(key: key, child: child);
最主要的参数为 transform:
final Matrix4 transform;
Matrix4 是一个 4 * 4 的矩阵,用于控制缩放、移动、旋转什么的,具体这里就不细说,不是这节的重要内容,有需要后续再补一篇相关的内容,目前,我们只需要知道以下几点即可:
具体要填写的代码就是这样:
child: Transform(
alignment: Alignment.center,
transform: Matrix4(
_paintedBoardProvider.scale,
0,
0,
0,
0,
_paintedBoardProvider.scale,
0,
0,
0,
0,
1,
0,
_paintedBoardProvider.translationX,
_paintedBoardProvider.translationY,
0,
1,
),
child: CustomPaint(
painter: MyPainter(_paintedBoardProvider),
size: Size.infinite,
),
),
这里有一点要注意,之前我们的缩放流程是基于中心点进行缩放的,所以 alignment 填 Alignment.center
。
剩下就是差坐标点的偏移了:
/// 移动开始时
void onStart(Offset localPosition) {
double startX = localPosition.dx;
double startY = localPosition.dy;
final newStroke = Stroke(
color: isClear ? Colors.transparent : color,
width: paintWidth,
isClear: isClear,
);
newStroke.path.moveTo( // <- 调整
(startX + (scale - 1) * realCanvasSize.width / 2 - translationX) /
scale,
(startY + (scale - 1) * realCanvasSize.height / 2 - translationY) /
scale);
_strokes.add(newStroke);
}
/// 移动
void onUpdate(Offset localPosition) {
_strokes.last.path.lineTo( // <- 调整
(localPosition.dx +
(scale - 1) * realCanvasSize.width / 2 -
translationX) /
scale,
(localPosition.dy +
(scale - 1) * realCanvasSize.height / 2 -
translationY) /
scale);
notifyListeners();
}
注意,要在除以 scale 之前减去偏移量,为什么要除以 scale?这个上文已经讲过,这里就不重复说明了。
至此,移动功能就完成了。
若有需要,后续再把整个项目的代码贴下,今天先到这里了。