editor: improved scale on resize
This commit is contained in:
parent
8353064945
commit
b89db3bc9b
4 changed files with 98 additions and 114 deletions
|
@ -71,7 +71,7 @@ class _EditorImageState extends State<EditorImage> {
|
||||||
widget.actionNotifier.addListener(_onActionChanged);
|
widget.actionNotifier.addListener(_onActionChanged);
|
||||||
_subscriptions.add(widget.magnifierController.stateStream.listen(_onViewStateChanged));
|
_subscriptions.add(widget.magnifierController.stateStream.listen(_onViewStateChanged));
|
||||||
_subscriptions.add(widget.magnifierController.scaleBoundariesStream.listen(_onViewScaleBoundariesChanged));
|
_subscriptions.add(widget.magnifierController.scaleBoundariesStream.listen(_onViewScaleBoundariesChanged));
|
||||||
_subscriptions.add(widget.transformController.eventStream.listen(_onTransformEvent));
|
_subscriptions.add(widget.transformController.activityStream.listen(_onTransformActivity));
|
||||||
}
|
}
|
||||||
|
|
||||||
void _unregisterWidget(EditorImage widget) {
|
void _unregisterWidget(EditorImage widget) {
|
||||||
|
@ -193,7 +193,7 @@ class _EditorImageState extends State<EditorImage> {
|
||||||
|
|
||||||
void _onActionChanged() => _updateScrim();
|
void _onActionChanged() => _updateScrim();
|
||||||
|
|
||||||
void _onTransformEvent(TransformEvent event) => _updateScrim();
|
void _onTransformActivity(TransformActivity activity) => _updateScrim();
|
||||||
|
|
||||||
void _updateScrim() => _scrimOpacityNotifier.value = _getActionScrimOpacity(widget.actionNotifier.value, transformController.activity);
|
void _updateScrim() => _scrimOpacityNotifier.value = _getActionScrimOpacity(widget.actionNotifier.value, transformController.activity);
|
||||||
|
|
||||||
|
|
|
@ -24,9 +24,9 @@ class TransformController {
|
||||||
|
|
||||||
Stream<Transformation> get transformationStream => _transformationStreamController.stream;
|
Stream<Transformation> get transformationStream => _transformationStreamController.stream;
|
||||||
|
|
||||||
final StreamController<TransformEvent> _eventStreamController = StreamController.broadcast();
|
final StreamController<TransformActivity> _activityStreamController = StreamController.broadcast();
|
||||||
|
|
||||||
Stream<TransformEvent> get eventStream => _eventStreamController.stream;
|
Stream<TransformActivity> get activityStream => _activityStreamController.stream;
|
||||||
|
|
||||||
static const double straightenDegreesMin = -45;
|
static const double straightenDegreesMin = -45;
|
||||||
static const double straightenDegreesMax = 45;
|
static const double straightenDegreesMax = 45;
|
||||||
|
@ -90,7 +90,7 @@ class TransformController {
|
||||||
|
|
||||||
set activity(TransformActivity activity) {
|
set activity(TransformActivity activity) {
|
||||||
_activity = activity;
|
_activity = activity;
|
||||||
_eventStreamController.add(TransformEvent(activity: _activity));
|
_activityStreamController.add(_activity);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _onAspectRatioChanged() {
|
void _onAspectRatioChanged() {
|
||||||
|
|
|
@ -91,7 +91,7 @@ class _CropperState extends State<Cropper> with SingleTickerProviderStateMixin {
|
||||||
void _registerWidget(Cropper widget) {
|
void _registerWidget(Cropper widget) {
|
||||||
_subscriptions.add(widget.magnifierController.stateStream.listen(_onViewStateChanged));
|
_subscriptions.add(widget.magnifierController.stateStream.listen(_onViewStateChanged));
|
||||||
_subscriptions.add(widget.magnifierController.scaleBoundariesStream.map((v) => v.viewportSize).listen(_onViewportSizeChanged));
|
_subscriptions.add(widget.magnifierController.scaleBoundariesStream.map((v) => v.viewportSize).listen(_onViewportSizeChanged));
|
||||||
_subscriptions.add(widget.transformController.eventStream.listen(_onTransformEvent));
|
_subscriptions.add(widget.transformController.activityStream.listen(_onTransformActivity));
|
||||||
_subscriptions.add(widget.transformController.transformationStream.map((v) => v.orientation).distinct().listen(_onOrientationChanged));
|
_subscriptions.add(widget.transformController.transformationStream.map((v) => v.orientation).distinct().listen(_onOrientationChanged));
|
||||||
_subscriptions.add(widget.transformController.transformationStream.map((v) => v.straightenDegrees).distinct().listen(_onStraightenDegreesChanged));
|
_subscriptions.add(widget.transformController.transformationStream.map((v) => v.straightenDegrees).distinct().listen(_onStraightenDegreesChanged));
|
||||||
widget.transformController.aspectRatioNotifier.addListener(_onCropAspectRatioChanged);
|
widget.transformController.aspectRatioNotifier.addListener(_onCropAspectRatioChanged);
|
||||||
|
@ -153,73 +153,61 @@ class _CropperState extends State<Cropper> with SingleTickerProviderStateMixin {
|
||||||
margin: margin,
|
margin: margin,
|
||||||
getPosition: () => outline.topLeft,
|
getPosition: () => outline.topLeft,
|
||||||
setPosition: (v) => _handleOutline(
|
setPosition: (v) => _handleOutline(
|
||||||
topLeft: Offset(min(outline.right - minDimension, v.dx), min(outline.bottom - minDimension, v.dy)),
|
left: min(outline.right - minDimension, v.dx),
|
||||||
|
top: min(outline.bottom - minDimension, v.dy),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
_buildVertexHandle(
|
_buildVertexHandle(
|
||||||
margin: margin,
|
margin: margin,
|
||||||
getPosition: () => outline.topRight,
|
getPosition: () => outline.topRight,
|
||||||
setPosition: (v) => _handleOutline(
|
setPosition: (v) => _handleOutline(
|
||||||
topRight: Offset(max(outline.left + minDimension, v.dx), min(outline.bottom - minDimension, v.dy)),
|
right: max(outline.left + minDimension, v.dx),
|
||||||
|
top: min(outline.bottom - minDimension, v.dy),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
_buildVertexHandle(
|
_buildVertexHandle(
|
||||||
margin: margin,
|
margin: margin,
|
||||||
getPosition: () => outline.bottomRight,
|
getPosition: () => outline.bottomRight,
|
||||||
setPosition: (v) => _handleOutline(
|
setPosition: (v) => _handleOutline(
|
||||||
bottomRight: Offset(max(outline.left + minDimension, v.dx), max(outline.top + minDimension, v.dy)),
|
right: max(outline.left + minDimension, v.dx),
|
||||||
|
bottom: max(outline.top + minDimension, v.dy),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
_buildVertexHandle(
|
_buildVertexHandle(
|
||||||
margin: margin,
|
margin: margin,
|
||||||
getPosition: () => outline.bottomLeft,
|
getPosition: () => outline.bottomLeft,
|
||||||
setPosition: (v) => _handleOutline(
|
setPosition: (v) => _handleOutline(
|
||||||
bottomLeft: Offset(min(outline.right - minDimension, v.dx), max(outline.top + minDimension, v.dy)),
|
left: min(outline.right - minDimension, v.dx),
|
||||||
|
bottom: max(outline.top + minDimension, v.dy),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
_buildEdgeHandle(
|
_buildEdgeHandle(
|
||||||
margin: margin,
|
margin: margin,
|
||||||
getEdge: () => Rect.fromPoints(outline.bottomLeft, outline.topLeft),
|
getEdge: () => Rect.fromPoints(outline.bottomLeft, outline.topLeft),
|
||||||
setEdge: (v) {
|
setEdge: (v) => _handleOutline(
|
||||||
final left = min(outline.right - minDimension, v.left);
|
left: min(outline.right - minDimension, v.left),
|
||||||
return _handleOutline(
|
),
|
||||||
topLeft: Offset(left, outline.top),
|
|
||||||
bottomLeft: Offset(left, outline.bottom),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
_buildEdgeHandle(
|
_buildEdgeHandle(
|
||||||
margin: margin,
|
margin: margin,
|
||||||
getEdge: () => Rect.fromPoints(outline.topLeft, outline.topRight),
|
getEdge: () => Rect.fromPoints(outline.topLeft, outline.topRight),
|
||||||
setEdge: (v) {
|
setEdge: (v) => _handleOutline(
|
||||||
final top = min(outline.bottom - minDimension, v.top);
|
top: min(outline.bottom - minDimension, v.top),
|
||||||
return _handleOutline(
|
),
|
||||||
topLeft: Offset(outline.left, top),
|
|
||||||
topRight: Offset(outline.right, top),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
_buildEdgeHandle(
|
_buildEdgeHandle(
|
||||||
margin: margin,
|
margin: margin,
|
||||||
getEdge: () => Rect.fromPoints(outline.bottomRight, outline.topRight),
|
getEdge: () => Rect.fromPoints(outline.bottomRight, outline.topRight),
|
||||||
setEdge: (v) {
|
setEdge: (v) => _handleOutline(
|
||||||
final right = max(outline.left + minDimension, v.right);
|
right: max(outline.left + minDimension, v.right),
|
||||||
return _handleOutline(
|
),
|
||||||
topRight: Offset(right, outline.top),
|
|
||||||
bottomRight: Offset(right, outline.bottom),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
_buildEdgeHandle(
|
_buildEdgeHandle(
|
||||||
margin: margin,
|
margin: margin,
|
||||||
getEdge: () => Rect.fromPoints(outline.bottomLeft, outline.bottomRight),
|
getEdge: () => Rect.fromPoints(outline.bottomLeft, outline.bottomRight),
|
||||||
setEdge: (v) {
|
setEdge: (v) => _handleOutline(
|
||||||
final bottom = max(outline.top + minDimension, v.bottom);
|
bottom: max(outline.top + minDimension, v.bottom),
|
||||||
return _handleOutline(
|
),
|
||||||
bottomLeft: Offset(outline.left, bottom),
|
|
||||||
bottomRight: Offset(outline.right, bottom),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
@ -241,36 +229,38 @@ class _CropperState extends State<Cropper> with SingleTickerProviderStateMixin {
|
||||||
);
|
);
|
||||||
|
|
||||||
void _handleOutline({
|
void _handleOutline({
|
||||||
Offset? topLeft,
|
double? left,
|
||||||
Offset? topRight,
|
double? top,
|
||||||
Offset? bottomRight,
|
double? right,
|
||||||
Offset? bottomLeft,
|
double? bottom,
|
||||||
}) {
|
}) {
|
||||||
final currentOutline = _outlineNotifier.value;
|
final currentOutline = _outlineNotifier.value;
|
||||||
var targetOutline = Rect.fromLTRB(
|
var targetOutline = Rect.fromLTRB(
|
||||||
topLeft?.dx ?? bottomLeft?.dx ?? currentOutline.left,
|
left ?? currentOutline.left,
|
||||||
topLeft?.dy ?? topRight?.dy ?? currentOutline.top,
|
top ?? currentOutline.top,
|
||||||
topRight?.dx ?? bottomRight?.dx ?? currentOutline.right,
|
right ?? currentOutline.right,
|
||||||
bottomLeft?.dy ?? bottomRight?.dy ?? currentOutline.bottom,
|
bottom ?? currentOutline.bottom,
|
||||||
);
|
);
|
||||||
|
|
||||||
_RatioStrategy? ratioStrategy;
|
_RatioStrategy? ratioStrategy;
|
||||||
if (topLeft != null && topRight != null) {
|
if (left != null && top != null && right != null) {
|
||||||
ratioStrategy = _RatioStrategy.pinBottom;
|
ratioStrategy = _RatioStrategy.pinBottom;
|
||||||
} else if (topRight != null && bottomRight != null) {
|
} else if (top != null && right != null && bottom != null) {
|
||||||
ratioStrategy = _RatioStrategy.pinLeft;
|
ratioStrategy = _RatioStrategy.pinLeft;
|
||||||
} else if (bottomLeft != null && bottomRight != null) {
|
} else if (left != null && right != null && bottom != null) {
|
||||||
ratioStrategy = _RatioStrategy.pinTop;
|
ratioStrategy = _RatioStrategy.pinTop;
|
||||||
} else if (topLeft != null && bottomLeft != null) {
|
} else if (left != null && top != null && bottom != null) {
|
||||||
ratioStrategy = _RatioStrategy.pinRight;
|
ratioStrategy = _RatioStrategy.pinRight;
|
||||||
} else if (topLeft != null) {
|
} else if (left != null && top != null) {
|
||||||
ratioStrategy = _RatioStrategy.pinBottomRight;
|
ratioStrategy = _RatioStrategy.pinBottomRight;
|
||||||
} else if (topRight != null) {
|
} else if (left != null) {
|
||||||
ratioStrategy = _RatioStrategy.pinBottomLeft;
|
|
||||||
} else if (bottomRight != null) {
|
|
||||||
ratioStrategy = _RatioStrategy.pinTopLeft;
|
|
||||||
} else if (bottomLeft != null) {
|
|
||||||
ratioStrategy = _RatioStrategy.pinTopRight;
|
ratioStrategy = _RatioStrategy.pinTopRight;
|
||||||
|
} else if (top != null) {
|
||||||
|
ratioStrategy = _RatioStrategy.pinBottomLeft;
|
||||||
|
} else if (right != null) {
|
||||||
|
ratioStrategy = _RatioStrategy.pinTopLeft;
|
||||||
|
} else if (bottom != null) {
|
||||||
|
ratioStrategy = _RatioStrategy.pinTopLeft;
|
||||||
}
|
}
|
||||||
if (ratioStrategy != null) {
|
if (ratioStrategy != null) {
|
||||||
targetOutline = _applyCropRatioToOutline(targetOutline, ratioStrategy);
|
targetOutline = _applyCropRatioToOutline(targetOutline, ratioStrategy);
|
||||||
|
@ -289,19 +279,17 @@ class _CropperState extends State<Cropper> with SingleTickerProviderStateMixin {
|
||||||
final gestureRegion = _regionFromOutline(currentState, targetOutline);
|
final gestureRegion = _regionFromOutline(currentState, targetOutline);
|
||||||
final viewportSize = boundaries.viewportSize;
|
final viewportSize = boundaries.viewportSize;
|
||||||
|
|
||||||
final gestureOutline = _regionToContainedOutline(currentState, gestureRegion);
|
final gestureOutline = _containedOutlineFromRegion(currentState, gestureRegion);
|
||||||
final clampedOutline = Rect.fromLTRB(
|
final clampedOutline = Rect.fromLTRB(
|
||||||
max(gestureOutline.left, 0),
|
max(gestureOutline.left, 0),
|
||||||
max(gestureOutline.top, 0),
|
max(gestureOutline.top, 0),
|
||||||
min(gestureOutline.right, viewportSize.width),
|
min(gestureOutline.right, viewportSize.width),
|
||||||
min(gestureOutline.bottom, viewportSize.height),
|
min(gestureOutline.bottom, viewportSize.height),
|
||||||
);
|
);
|
||||||
_setOutline(clampedOutline);
|
var nextOutline = clampedOutline;
|
||||||
_updateCropRegion();
|
|
||||||
|
|
||||||
// zoom out when user gesture reaches outer edges
|
if (max(gestureOutline.width - clampedOutline.width, gestureOutline.height - clampedOutline.height) > precisionErrorTolerance) {
|
||||||
|
// zoom out when user gesture reaches outer edges
|
||||||
if (gestureOutline.width - clampedOutline.width > precisionErrorTolerance || gestureOutline.height - clampedOutline.height > precisionErrorTolerance) {
|
|
||||||
final targetOutline = Rect.lerp(clampedOutline, gestureOutline, overOutlineFactor)!;
|
final targetOutline = Rect.lerp(clampedOutline, gestureOutline, overOutlineFactor)!;
|
||||||
final targetRegion = _regionFromOutline(currentState, targetOutline);
|
final targetRegion = _regionFromOutline(currentState, targetOutline);
|
||||||
|
|
||||||
|
@ -312,9 +300,11 @@ class _CropperState extends State<Cropper> with SingleTickerProviderStateMixin {
|
||||||
scale: nextState.scale,
|
scale: nextState.scale,
|
||||||
source: ChangeSource.animation,
|
source: ChangeSource.animation,
|
||||||
);
|
);
|
||||||
_setOutline(_regionToContainedOutline(nextState, targetRegion));
|
nextOutline = _containedOutlineFromRegion(nextState, targetRegion);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_setOutline(nextOutline);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool _isOutlineContained(Rect outline) {
|
bool _isOutlineContained(Rect outline) {
|
||||||
|
@ -344,8 +334,8 @@ class _CropperState extends State<Cropper> with SingleTickerProviderStateMixin {
|
||||||
margin: margin,
|
margin: margin,
|
||||||
getPosition: getPosition,
|
getPosition: getPosition,
|
||||||
setPosition: setPosition,
|
setPosition: setPosition,
|
||||||
onDragStart: _onDragStart,
|
onDragStart: _onResizeStart,
|
||||||
onDragEnd: _onDragEnd,
|
onDragEnd: _onResizeEnd,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -358,16 +348,16 @@ class _CropperState extends State<Cropper> with SingleTickerProviderStateMixin {
|
||||||
margin: margin,
|
margin: margin,
|
||||||
getEdge: getEdge,
|
getEdge: getEdge,
|
||||||
setEdge: setEdge,
|
setEdge: setEdge,
|
||||||
onDragStart: _onDragStart,
|
onDragStart: _onResizeStart,
|
||||||
onDragEnd: _onDragEnd,
|
onDragEnd: _onResizeEnd,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _onDragStart() {
|
void _onResizeStart() {
|
||||||
transformController.activity = TransformActivity.resize;
|
transformController.activity = TransformActivity.resize;
|
||||||
}
|
}
|
||||||
|
|
||||||
void _onDragEnd() {
|
void _onResizeEnd() {
|
||||||
transformController.activity = TransformActivity.none;
|
transformController.activity = TransformActivity.none;
|
||||||
_showRegion();
|
_showRegion();
|
||||||
}
|
}
|
||||||
|
@ -384,7 +374,7 @@ class _CropperState extends State<Cropper> with SingleTickerProviderStateMixin {
|
||||||
scale: nextState.scale,
|
scale: nextState.scale,
|
||||||
source: ChangeSource.animation,
|
source: ChangeSource.animation,
|
||||||
);
|
);
|
||||||
_setOutline(_regionToContainedOutline(nextState, region));
|
_setOutline(_containedOutlineFromRegion(nextState, region));
|
||||||
}
|
}
|
||||||
|
|
||||||
ViewState _viewStateForContainedRegion(ScaleBoundaries boundaries, CropRegion imageRegion) {
|
ViewState _viewStateForContainedRegion(ScaleBoundaries boundaries, CropRegion imageRegion) {
|
||||||
|
@ -409,11 +399,10 @@ class _CropperState extends State<Cropper> with SingleTickerProviderStateMixin {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _onTransformEvent(TransformEvent event) {
|
void _onTransformActivity(TransformActivity activity) {
|
||||||
final activity = event.activity;
|
|
||||||
switch (activity) {
|
switch (activity) {
|
||||||
case TransformActivity.none:
|
case TransformActivity.none:
|
||||||
break;
|
_showRegion();
|
||||||
case TransformActivity.pan:
|
case TransformActivity.pan:
|
||||||
case TransformActivity.resize:
|
case TransformActivity.resize:
|
||||||
_gridDivisionNotifier.value = panResizeGridDivision;
|
_gridDivisionNotifier.value = panResizeGridDivision;
|
||||||
|
@ -449,30 +438,35 @@ class _CropperState extends State<Cropper> with SingleTickerProviderStateMixin {
|
||||||
|
|
||||||
void _onViewStateChanged(MagnifierState state) {
|
void _onViewStateChanged(MagnifierState state) {
|
||||||
final currentOutline = _outlineNotifier.value;
|
final currentOutline = _outlineNotifier.value;
|
||||||
|
|
||||||
|
// TODO TLAD [crop] use other strat
|
||||||
|
|
||||||
// switch (state.source) {
|
// switch (state.source) {
|
||||||
// case ChangeSource.internal:
|
// case ChangeSource.internal:
|
||||||
// case ChangeSource.animation:
|
// case ChangeSource.animation:
|
||||||
// _setOutline(currentOutline);
|
|
||||||
// case ChangeSource.gesture:
|
// case ChangeSource.gesture:
|
||||||
// TODO TLAD [crop] use other strat
|
|
||||||
_setOutline(_applyCropRatioToOutline(currentOutline, _RatioStrategy.contain));
|
|
||||||
_updateCropRegion();
|
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
switch (transformController.activity) {
|
||||||
|
case TransformActivity.none:
|
||||||
|
case TransformActivity.straighten:
|
||||||
|
case TransformActivity.pan:
|
||||||
|
_setOutline(_applyCropRatioToOutline(currentOutline, _RatioStrategy.contain));
|
||||||
|
case TransformActivity.resize:
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void _onViewportSizeChanged(Size viewportSize) {
|
void _onViewportSizeChanged(Size viewportSize) {
|
||||||
_initOutline(transformation.region);
|
|
||||||
|
|
||||||
final boundaries = magnifierController.scaleBoundaries;
|
final boundaries = magnifierController.scaleBoundaries;
|
||||||
if (boundaries != null) {
|
if (boundaries != null) {
|
||||||
final double xPadding = (viewportSize.width - minDimension) / 2;
|
|
||||||
final double yPadding = (viewportSize.height - minDimension) / 2;
|
|
||||||
magnifierController.setScaleBoundaries(
|
magnifierController.setScaleBoundaries(
|
||||||
boundaries.copyWith(
|
boundaries.copyWith(
|
||||||
padding: EdgeInsets.symmetric(horizontal: xPadding, vertical: yPadding),
|
padding: const EdgeInsets.all(double.infinity),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
_showRegion();
|
||||||
}
|
}
|
||||||
|
|
||||||
ViewState? _getViewState() {
|
ViewState? _getViewState() {
|
||||||
|
@ -488,14 +482,6 @@ class _CropperState extends State<Cropper> with SingleTickerProviderStateMixin {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _initOutline(CropRegion region) {
|
|
||||||
final viewState = _getViewState();
|
|
||||||
if (viewState != null) {
|
|
||||||
_setOutline(_regionToContainedOutline(viewState, region));
|
|
||||||
_updateCropRegion();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void _setOutline(Rect targetOutline) {
|
void _setOutline(Rect targetOutline) {
|
||||||
final viewState = _getViewState();
|
final viewState = _getViewState();
|
||||||
final viewportSize = viewState?.viewportSize;
|
final viewportSize = viewState?.viewportSize;
|
||||||
|
@ -503,7 +489,7 @@ class _CropperState extends State<Cropper> with SingleTickerProviderStateMixin {
|
||||||
|
|
||||||
// ensure outline is within content
|
// ensure outline is within content
|
||||||
final targetRegion = _regionFromOutline(viewState, targetOutline);
|
final targetRegion = _regionFromOutline(viewState, targetOutline);
|
||||||
var newOutline = _regionToContainedOutline(viewState, targetRegion);
|
var newOutline = _containedOutlineFromRegion(viewState, targetRegion);
|
||||||
|
|
||||||
// ensure outline is large enough to be handled
|
// ensure outline is large enough to be handled
|
||||||
newOutline = Rect.fromLTWH(
|
newOutline = Rect.fromLTWH(
|
||||||
|
@ -521,7 +507,20 @@ class _CropperState extends State<Cropper> with SingleTickerProviderStateMixin {
|
||||||
min(newOutline.bottom, viewportSize.height),
|
min(newOutline.bottom, viewportSize.height),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
final oldOutline = _outlineNotifier.value;
|
||||||
_outlineNotifier.value = newOutline;
|
_outlineNotifier.value = newOutline;
|
||||||
|
switch (transformController.activity) {
|
||||||
|
case TransformActivity.none:
|
||||||
|
if (oldOutline.isEmpty) {
|
||||||
|
_updateCropRegion();
|
||||||
|
}
|
||||||
|
case TransformActivity.pan:
|
||||||
|
case TransformActivity.resize:
|
||||||
|
_updateCropRegion();
|
||||||
|
break;
|
||||||
|
case TransformActivity.straighten:
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void _updateCropRegion() {
|
void _updateCropRegion() {
|
||||||
|
@ -549,29 +548,23 @@ class _CropperState extends State<Cropper> with SingleTickerProviderStateMixin {
|
||||||
final regionToOutlineMatrix = _getRegionToOutlineMatrix(viewState);
|
final regionToOutlineMatrix = _getRegionToOutlineMatrix(viewState);
|
||||||
final outlineToRegionMatrix = regionToOutlineMatrix..invert();
|
final outlineToRegionMatrix = regionToOutlineMatrix..invert();
|
||||||
|
|
||||||
final region = CropRegion(
|
|
||||||
topLeft: outlineToRegionMatrix.transformOffset(outline.topLeft),
|
|
||||||
topRight: outlineToRegionMatrix.transformOffset(outline.topRight),
|
|
||||||
bottomRight: outlineToRegionMatrix.transformOffset(outline.bottomRight),
|
|
||||||
bottomLeft: outlineToRegionMatrix.transformOffset(outline.bottomLeft),
|
|
||||||
);
|
|
||||||
|
|
||||||
final rect = Offset.zero & viewState.contentSize!;
|
final rect = Offset.zero & viewState.contentSize!;
|
||||||
double clampX(double dx) => dx.clamp(rect.left, rect.right);
|
double clampX(double dx) => dx.clamp(rect.left, rect.right);
|
||||||
double clampY(double dy) => dy.clamp(rect.top, rect.bottom);
|
double clampY(double dy) => dy.clamp(rect.top, rect.bottom);
|
||||||
Offset clampPoint(Offset v) => Offset(clampX(v.dx), clampY(v.dy));
|
Offset clampPoint(Offset v) => Offset(clampX(v.dx), clampY(v.dy));
|
||||||
|
Offset transform(Offset v) => clampPoint(outlineToRegionMatrix.transformOffset(v));
|
||||||
final clampedRegion = CropRegion(
|
final clampedRegion = CropRegion(
|
||||||
topLeft: clampPoint(region.topLeft),
|
topLeft: transform(outline.topLeft),
|
||||||
topRight: clampPoint(region.topRight),
|
topRight: transform(outline.topRight),
|
||||||
bottomRight: clampPoint(region.bottomRight),
|
bottomRight: transform(outline.bottomRight),
|
||||||
bottomLeft: clampPoint(region.bottomLeft),
|
bottomLeft: transform(outline.bottomLeft),
|
||||||
);
|
);
|
||||||
return clampedRegion;
|
return clampedRegion;
|
||||||
}
|
}
|
||||||
|
|
||||||
Rect _regionToContainedOutline(ViewState viewState, CropRegion region) {
|
Rect _containedOutlineFromRegion(ViewState viewState, CropRegion region) {
|
||||||
final matrix = _getRegionToOutlineMatrix(viewState);
|
final regionToOutlineMatrix = _getRegionToOutlineMatrix(viewState);
|
||||||
final points = region.corners.map(matrix.transformOffset).toSet();
|
final points = region.corners.map(regionToOutlineMatrix.transformOffset).toList();
|
||||||
final sortedX = points.map((v) => v.dx).toList()..sort();
|
final sortedX = points.map((v) => v.dx).toList()..sort();
|
||||||
final sortedY = points.map((v) => v.dy).toList()..sort();
|
final sortedY = points.map((v) => v.dy).toList()..sort();
|
||||||
final topLeft = Offset(sortedX[1], sortedY[1]);
|
final topLeft = Offset(sortedX[1], sortedY[1]);
|
||||||
|
|
|
@ -69,12 +69,3 @@ class Transformation extends Equatable {
|
||||||
|
|
||||||
Matrix4 get _straightenMatrix => Matrix4.rotationZ(degToRadian((orientation.isFlipped ? -1 : 1) * straightenDegrees));
|
Matrix4 get _straightenMatrix => Matrix4.rotationZ(degToRadian((orientation.isFlipped ? -1 : 1) * straightenDegrees));
|
||||||
}
|
}
|
||||||
|
|
||||||
@immutable
|
|
||||||
class TransformEvent {
|
|
||||||
final TransformActivity activity;
|
|
||||||
|
|
||||||
const TransformEvent({
|
|
||||||
required this.activity,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
Loading…
Reference in a new issue