I forked a Flutter package to implement draggable widgets on a canvas to create layouts from images, text and stickers. I have been trying to implement a snapping behavior for translating and rotating the widgets so that the user can easily center or align the draggable widgets. But I am having a problem implementing the snapping behavior for the translation of the widget. The rotation works fine.
When the draggable widget is rotated, my snap to position algorithm doesn't work. The problem is that the calculated delta between the current position of the widget and the position the widget should snap to isn't the right value to translate the widget to this snap position.
Here is the function to calculate the translation:
Matrix4 _translate(Offset translation) {
var dx = translation.dx;
var dy = translation.dy;
double tx = matrix[12];
double ty = matrix[13];
Size size = context.size!;
double rotation = atan2(matrix[1], matrix[0]);
double preRotationCenterX = (tx - size.width / 2) / recordOldScale;
double preRotationCenterY = (ty - size.height / 2) / recordOldScale;
double centerX = cos(-rotation) * preRotationCenterX -
sin(-rotation) * preRotationCenterY +
(size.width / 2);
double centerY = sin(-rotation) * preRotationCenterX +
cos(-rotation) * preRotationCenterY +
(size.height / 2);
for (var snapPosition
in widget.stickerWidgetConfig.translationXSnapValues) {
if (absoluteError(centerX + dy, snapPosition) <=
widget.stickerWidgetConfig.translationSnapThreshold) {
dx = snapPosition - centerX;
}
}
for (var snapPosition
in widget.stickerWidgetConfig.translationYSnapValues) {
if (absoluteError(centerY + dy, snapPosition) <=
widget.stickerWidgetConfig.translationSnapThreshold) {
dy = snapPosition - centerY;
}
}
Matrix4 translationMatrix =
Matrix4(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, dx, dy, 0, 1);
return translationMatrix;
}
And the code for rotating the draggable widget:
Matrix4 _rotate(double angle, Offset focalPoint) {
double toBeRotated = 0;
var array = matrix.applyToVector3Array([0, 0, 0, 1, 0, 0]);
Offset delta = Offset(array[3] - array[0], array[4] - array[1]);
double rotation = delta.direction;
double deltaAngle = rotationUpdater.update(angle);
for (var snapPosition in widget.stickerWidgetConfig.rotationSnapValues) {
if (snapPosition > 180) {
snapPosition = 180 - snapPosition;
}
snapPosition = radians(snapPosition);
if (absoluteError((rotation + deltaAngle), snapPosition) >
widget.stickerWidgetConfig.rotationSnapThreshold) {
toBeRotated = deltaAngle;
} else if (rotation != snapPosition &&
absoluteError((rotation + deltaAngle), snapPosition) <=
widget.stickerWidgetConfig.rotationSnapThreshold) {
toBeRotated = snapPosition - rotation;
break;
} else {
toBeRotated = 0;
break;
}
}
double c = cos(toBeRotated);
double s = sin(toBeRotated);
double dx = (1 - c) * focalPoint.dx + s * focalPoint.dy;
double dy = (1 - c) * focalPoint.dy - s * focalPoint.dx;
return Matrix4(c, s, 0, 0, -s, c, 0, 0, 0, 0, 1, 0, dx, dy, 0, 1);
}
}
Both calculated matrices are getting multiplied to the old transformation matrix:
void onScaleUpdate(ScaleUpdateDetails details) {
// Handle translation.
if (widget.shouldTranslate) {
Offset translationDelta = translationUpdater.update(details.focalPoint);
matrix = _translate(translationDelta) * matrix;
}
final focalPointAlignment = widget.focalPointAlignment;
final focalPoint = focalPointAlignment == null
? details.localFocalPoint
: focalPointAlignment.alongSize(context.size!);
// Handle scaling.
if (widget.shouldScale && details.scale != 1.0) {
double sc = recordOldScale * details.scale;
if (sc > widget.minScale && sc < widget.maxScale) {
recordScale = sc;
double scaleDelta = scaleUpdater.update(details.scale);
matrix = _scale(scaleDelta, focalPoint) * matrix;
}
}
// Handle rotation.
if (widget.shouldRotate && details.rotation != 0.0) {
matrix = _rotate(details.rotation, focalPoint) * matrix;
}
}
To see the full source code, you can also check out the GitHub repo: sticker_gesture_detector.dart
I managed to fix it myself. For anyone interested, I have uploaded the working code to my GitHub repository: StickerWidget