viewer: fixed focus & panning when scaling by pinch
This commit is contained in:
parent
05496da344
commit
b9e64b552a
4 changed files with 30 additions and 27 deletions
|
@ -51,7 +51,6 @@ class MagnifierController {
|
|||
}
|
||||
|
||||
void setPosition(Offset position, ChangeSource source) {
|
||||
// debugPrint('$runtimeType setPosition position=$position, source=$source');
|
||||
if (value.position == position) return;
|
||||
|
||||
prevValue = value;
|
||||
|
@ -66,7 +65,6 @@ class MagnifierController {
|
|||
Offset get position => value.position;
|
||||
|
||||
void setScale(double scale, ChangeSource source) {
|
||||
// debugPrint('$runtimeType setScale scale=$scale source=$source');
|
||||
if (value.scale == scale) return;
|
||||
|
||||
prevValue = value;
|
||||
|
@ -86,7 +84,6 @@ class MagnifierController {
|
|||
double scale,
|
||||
@required ChangeSource source,
|
||||
}) {
|
||||
// debugPrint('$runtimeType updateMultiple position=$position scale=$scale, source=$source');
|
||||
prevValue = value;
|
||||
_setValue(MagnifierState(
|
||||
position: position ?? value.position,
|
||||
|
@ -99,7 +96,6 @@ class MagnifierController {
|
|||
MagnifierState get value => _valueNotifier.value;
|
||||
|
||||
void _setValue(MagnifierState newValue) {
|
||||
// debugPrint('$runtimeType setValue value=$newValue');
|
||||
if (_valueNotifier.value == newValue) return;
|
||||
_valueNotifier.value = newValue;
|
||||
}
|
||||
|
|
|
@ -51,8 +51,7 @@ mixin MagnifierControllerDelegate on State<MagnifierCore> {
|
|||
if (nextScaleState == ScaleState.covering || nextScaleState == ScaleState.originalSize) {
|
||||
final childFocalPoint = scaleStateChange.childFocalPoint;
|
||||
if (childFocalPoint != null) {
|
||||
final childCenter = scaleBoundaries.childSize.center(Offset.zero);
|
||||
nextPosition = (childCenter - childFocalPoint) * nextScale;
|
||||
nextPosition = scaleBoundaries.childToStatePosition(nextScale, childFocalPoint);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -99,7 +98,6 @@ mixin MagnifierControllerDelegate on State<MagnifierCore> {
|
|||
}
|
||||
|
||||
void updateScaleStateFromNewScale(double newScale, ChangeSource source) {
|
||||
// debugPrint('updateScaleStateFromNewScale scale=$newScale, source=$source');
|
||||
var newScaleState = ScaleState.initial;
|
||||
if (scale != scaleBoundaries.initialScale) {
|
||||
newScaleState = (newScale > scaleBoundaries.initialScale) ? ScaleState.zoomedIn : ScaleState.zoomedOut;
|
||||
|
@ -108,7 +106,6 @@ mixin MagnifierControllerDelegate on State<MagnifierCore> {
|
|||
}
|
||||
|
||||
void nextScaleState(ChangeSource source, {Offset childFocalPoint}) {
|
||||
// debugPrint('$runtimeType nextScaleState source=$source');
|
||||
final scaleState = scaleStateController.scaleState.state;
|
||||
if (scaleState == ScaleState.zoomedIn || scaleState == ScaleState.zoomedOut) {
|
||||
scaleStateController.setScaleState(scaleStateCycle(scaleState), source, childFocalPoint: childFocalPoint);
|
||||
|
|
|
@ -43,8 +43,8 @@ class MagnifierCore extends StatefulWidget {
|
|||
}
|
||||
|
||||
class MagnifierCoreState extends State<MagnifierCore> with TickerProviderStateMixin, MagnifierControllerDelegate, CornerHitDetector {
|
||||
Offset _normalizedPosition;
|
||||
double _scaleBefore;
|
||||
Offset _prevViewportFocalPosition;
|
||||
double _gestureStartScale;
|
||||
|
||||
AnimationController _scaleAnimationController;
|
||||
Animation<double> _scaleAnimation;
|
||||
|
@ -63,24 +63,27 @@ class MagnifierCoreState extends State<MagnifierCore> with TickerProviderStateMi
|
|||
}
|
||||
|
||||
void onScaleStart(ScaleStartDetails details) {
|
||||
_scaleBefore = scale;
|
||||
_normalizedPosition = details.focalPoint - controller.position;
|
||||
_gestureStartScale = scale;
|
||||
_prevViewportFocalPosition = details.localFocalPoint;
|
||||
|
||||
_scaleAnimationController.stop();
|
||||
_positionAnimationController.stop();
|
||||
}
|
||||
|
||||
void onScaleUpdate(ScaleUpdateDetails details) {
|
||||
final newScale = _scaleBefore * details.scale;
|
||||
final delta = details.focalPoint - _normalizedPosition;
|
||||
final newScale = _gestureStartScale * details.scale;
|
||||
final panPositionDelta = details.focalPoint - _prevViewportFocalPosition;
|
||||
final scalePositionDelta = scaleBoundaries.viewportToStatePosition(controller, details.focalPoint) * (scale / newScale - 1);
|
||||
final newPosition = position + panPositionDelta + scalePositionDelta;
|
||||
|
||||
updateScaleStateFromNewScale(newScale, ChangeSource.gesture);
|
||||
|
||||
//
|
||||
updateMultiple(
|
||||
scale: newScale,
|
||||
position: clampPosition(position: delta * details.scale),
|
||||
position: newPosition,
|
||||
source: ChangeSource.gesture,
|
||||
);
|
||||
|
||||
_prevViewportFocalPosition = details.focalPoint;
|
||||
}
|
||||
|
||||
void onScaleEnd(ScaleEndDetails details) {
|
||||
|
@ -118,7 +121,7 @@ class MagnifierCoreState extends State<MagnifierCore> with TickerProviderStateMi
|
|||
final magnitude = details.velocity.pixelsPerSecond.distance;
|
||||
|
||||
// animate velocity only if there is no scale change and a significant magnitude
|
||||
if (_scaleBefore / _scale == 1.0 && magnitude >= 400.0) {
|
||||
if (_gestureStartScale / _scale == 1.0 && magnitude >= 400.0) {
|
||||
final direction = details.velocity.pixelsPerSecond / magnitude;
|
||||
animatePosition(
|
||||
_position,
|
||||
|
@ -131,13 +134,13 @@ class MagnifierCoreState extends State<MagnifierCore> with TickerProviderStateMi
|
|||
if (widget.onTap == null) return;
|
||||
|
||||
final viewportTapPosition = details.localPosition;
|
||||
final childTapPosition = scaleBoundaries.toChildPosition(controller, viewportTapPosition);
|
||||
final childTapPosition = scaleBoundaries.viewportToChildPosition(controller, viewportTapPosition);
|
||||
widget.onTap.call(context, details, controller.value, childTapPosition);
|
||||
}
|
||||
|
||||
void onDoubleTap(TapDownDetails details) {
|
||||
final viewportTapPosition = details?.localPosition;
|
||||
final childTapPosition = scaleBoundaries.toChildPosition(controller, viewportTapPosition);
|
||||
final childTapPosition = scaleBoundaries.viewportToChildPosition(controller, viewportTapPosition);
|
||||
nextScaleState(ChangeSource.gesture, childFocalPoint: childTapPosition);
|
||||
}
|
||||
|
||||
|
|
|
@ -40,13 +40,20 @@ class ScaleBoundaries {
|
|||
|
||||
double get initialScale => _scaleForLevel(_initialScale).clamp(minScale, maxScale);
|
||||
|
||||
Offset toChildPosition(MagnifierController controller, Offset viewportPosition) {
|
||||
final position = controller.position;
|
||||
final scale = controller.scale;
|
||||
final viewportCenter = viewportSize.center(Offset.zero);
|
||||
final childCenter = childSize.center(Offset.zero);
|
||||
final childPosition = (viewportPosition - viewportCenter) / scale - position / scale + childCenter;
|
||||
return childPosition;
|
||||
Offset get _viewportCenter => viewportSize.center(Offset.zero);
|
||||
|
||||
Offset get _childCenter => childSize.center(Offset.zero);
|
||||
|
||||
Offset viewportToStatePosition(MagnifierController controller, Offset viewportPosition) {
|
||||
return viewportPosition - _viewportCenter - controller.position;
|
||||
}
|
||||
|
||||
Offset viewportToChildPosition(MagnifierController controller, Offset viewportPosition) {
|
||||
return viewportToStatePosition(controller, viewportPosition) / controller.scale + _childCenter;
|
||||
}
|
||||
|
||||
Offset childToStatePosition(double scale, Offset childPosition) {
|
||||
return (_childCenter - childPosition) * scale;
|
||||
}
|
||||
|
||||
@override
|
||||
|
|
Loading…
Reference in a new issue