viewer: fixed focus & panning when scaling by pinch

This commit is contained in:
Thibault Deckers 2020-12-17 18:10:33 +09:00
parent 05496da344
commit b9e64b552a
4 changed files with 30 additions and 27 deletions

View file

@ -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;
}

View file

@ -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);

View file

@ -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);
}

View file

@ -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