init
This commit is contained in:
@@ -0,0 +1,475 @@
|
||||
import 'package:flutter/gestures.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:extended_image/extended_image.dart';
|
||||
|
||||
Map<Object?, GestureDetails?> _gestureDetailsCache =
|
||||
<Object?, GestureDetails?>{};
|
||||
|
||||
///clear the gesture details
|
||||
void clearGestureDetailsCache() {
|
||||
_gestureDetailsCache.clear();
|
||||
}
|
||||
|
||||
/// scale idea from https://github.com/flutter/flutter/blob/master/examples/layers/widgets/gestures.dart
|
||||
/// zoom image
|
||||
class GesturedImage extends ExtendedImageGesture {
|
||||
const GesturedImage(ExtendedImageState extendedImageState,
|
||||
{ImageBuilderForGesture? imageBuilder,
|
||||
CanScaleImage? canScaleImage,
|
||||
Key? key})
|
||||
: super(extendedImageState,
|
||||
imageBuilder: imageBuilder, canScaleImage: canScaleImage, key: key);
|
||||
|
||||
@override
|
||||
GesturedImageState createState() => GesturedImageState();
|
||||
}
|
||||
|
||||
class GesturedImageState extends ExtendedImageGestureState {
|
||||
///details for gesture
|
||||
GestureDetails? _gestureDetails;
|
||||
late Offset _normalizedOffset;
|
||||
double? _startingScale;
|
||||
late Offset _startingOffset;
|
||||
Offset? _pointerDownPosition;
|
||||
late GestureAnimation _gestureAnimation;
|
||||
GestureConfig? _gestureConfig;
|
||||
ExtendedImageGesturePageViewState? _pageViewState;
|
||||
@override
|
||||
ExtendedImageSlidePageState? get extendedImageSlidePageState =>
|
||||
widget.extendedImageState.slidePageState;
|
||||
|
||||
@override
|
||||
GestureDetails? get gestureDetails => _gestureDetails;
|
||||
|
||||
@override
|
||||
set gestureDetails(GestureDetails? value) {
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
_gestureDetails = value;
|
||||
_gestureConfig?.gestureDetailsIsChanged?.call(_gestureDetails);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
GestureConfig? get imageGestureConfig => _gestureConfig;
|
||||
|
||||
@override
|
||||
Offset? get pointerDownPosition => _pointerDownPosition;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (_gestureConfig!.cacheGesture) {
|
||||
_gestureDetailsCache[widget.extendedImageState.imageStreamKey] =
|
||||
_gestureDetails;
|
||||
}
|
||||
|
||||
Widget image = ExtendedRawImage(
|
||||
image: widget.extendedImageState.extendedImageInfo?.image,
|
||||
width: widget.extendedImageState.imageWidget.width,
|
||||
height: widget.extendedImageState.imageWidget.height,
|
||||
scale: widget.extendedImageState.extendedImageInfo?.scale ?? 1.0,
|
||||
color: widget.extendedImageState.imageWidget.color,
|
||||
colorBlendMode: widget.extendedImageState.imageWidget.colorBlendMode,
|
||||
fit: widget.extendedImageState.imageWidget.fit,
|
||||
alignment: widget.extendedImageState.imageWidget.alignment,
|
||||
repeat: widget.extendedImageState.imageWidget.repeat,
|
||||
centerSlice: widget.extendedImageState.imageWidget.centerSlice,
|
||||
matchTextDirection:
|
||||
widget.extendedImageState.imageWidget.matchTextDirection,
|
||||
invertColors: widget.extendedImageState.invertColors,
|
||||
filterQuality: widget.extendedImageState.imageWidget.filterQuality,
|
||||
beforePaintImage: widget.extendedImageState.imageWidget.beforePaintImage,
|
||||
afterPaintImage: widget.extendedImageState.imageWidget.afterPaintImage,
|
||||
gestureDetails: _gestureDetails,
|
||||
);
|
||||
|
||||
if (extendedImageSlidePageState != null) {
|
||||
image = widget.extendedImageState.imageWidget.heroBuilderForSlidingPage
|
||||
?.call(image) ??
|
||||
image;
|
||||
if (extendedImageSlidePageState!.widget.slideType ==
|
||||
SlideType.onlyImage) {
|
||||
image = Transform.translate(
|
||||
offset: extendedImageSlidePageState!.offset,
|
||||
child: Transform.scale(
|
||||
scale: extendedImageSlidePageState!.scale,
|
||||
child: image,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
image = widget.imageBuilder?.call(image) ?? image;
|
||||
|
||||
image = GestureDetector(
|
||||
onScaleStart: handleScaleStart,
|
||||
onScaleUpdate: handleScaleUpdate,
|
||||
onScaleEnd: handleScaleEnd,
|
||||
onDoubleTap: _handleDoubleTap,
|
||||
child: image,
|
||||
behavior: _gestureConfig?.hitTestBehavior,
|
||||
);
|
||||
|
||||
image = Listener(
|
||||
child: image,
|
||||
onPointerDown: _handlePointerDown,
|
||||
onPointerSignal: _handlePointerSignal,
|
||||
behavior: _gestureConfig!.hitTestBehavior,
|
||||
);
|
||||
|
||||
return image;
|
||||
}
|
||||
|
||||
@override
|
||||
void didChangeDependencies() {
|
||||
super.didChangeDependencies();
|
||||
_pageViewState = null;
|
||||
if (_gestureConfig!.inPageView) {
|
||||
_pageViewState =
|
||||
context.findAncestorStateOfType<ExtendedImageGesturePageViewState>();
|
||||
_pageViewState?.extendedImageGestureState = this;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void didUpdateWidget(GesturedImage oldWidget) {
|
||||
super.didUpdateWidget(oldWidget);
|
||||
_initGestureConfig();
|
||||
_pageViewState = null;
|
||||
if (_gestureConfig!.inPageView) {
|
||||
_pageViewState =
|
||||
context.findAncestorStateOfType<ExtendedImageGesturePageViewState>();
|
||||
_pageViewState?.extendedImageGestureState = this;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_gestureAnimation.stop();
|
||||
_gestureAnimation.dispose();
|
||||
_pageViewState?.extendedImageGestureStates.remove(this);
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
void handleDoubleTap({double? scale, Offset? doubleTapPosition}) {
|
||||
doubleTapPosition ??= _pointerDownPosition;
|
||||
scale ??= _gestureConfig!.initialScale;
|
||||
//scale = scale.clamp(_gestureConfig.minScale, _gestureConfig.maxScale);
|
||||
handleScaleStart(ScaleStartDetails(focalPoint: doubleTapPosition!));
|
||||
handleScaleUpdate(ScaleUpdateDetails(
|
||||
focalPoint: doubleTapPosition,
|
||||
scale: scale / _startingScale!,
|
||||
focalPointDelta: Offset.zero,
|
||||
));
|
||||
if (scale < _gestureConfig!.minScale || scale > _gestureConfig!.maxScale) {
|
||||
handleScaleEnd(ScaleEndDetails());
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_initGestureConfig();
|
||||
}
|
||||
|
||||
@override
|
||||
void reset() {
|
||||
_gestureConfig = widget
|
||||
.extendedImageState.imageWidget.initGestureConfigHandler
|
||||
?.call(widget.extendedImageState) ??
|
||||
GestureConfig();
|
||||
|
||||
gestureDetails = GestureDetails(
|
||||
totalScale: _gestureConfig!.initialScale,
|
||||
offset: Offset.zero,
|
||||
initialAlignment: _gestureConfig!.initialAlignment,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void slide() {
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
_gestureDetails!.slidePageOffset = extendedImageSlidePageState?.offset;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void _handleDoubleTap() {
|
||||
if (widget.extendedImageState.imageWidget.onDoubleTap != null) {
|
||||
widget.extendedImageState.imageWidget.onDoubleTap!(this);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!mounted) {
|
||||
return;
|
||||
}
|
||||
|
||||
gestureDetails = GestureDetails(
|
||||
offset: Offset.zero,
|
||||
totalScale: _gestureConfig!.initialScale,
|
||||
);
|
||||
}
|
||||
|
||||
void _handlePointerDown(PointerDownEvent pointerDownEvent) {
|
||||
_pointerDownPosition = pointerDownEvent.position;
|
||||
_gestureAnimation.stop();
|
||||
|
||||
_pageViewState?.extendedImageGestureState = this;
|
||||
}
|
||||
|
||||
void _handlePointerSignal(PointerSignalEvent event) {
|
||||
if (event is PointerScrollEvent && event.kind == PointerDeviceKind.mouse) {
|
||||
handleScaleStart(ScaleStartDetails(focalPoint: event.position));
|
||||
final double dy = event.scrollDelta.dy;
|
||||
final double dx = event.scrollDelta.dx;
|
||||
handleScaleUpdate(ScaleUpdateDetails(
|
||||
focalPoint: event.position,
|
||||
scale: 1.0 +
|
||||
_reverseIf((dy.abs() > dx.abs() ? dy : dx) *
|
||||
_gestureConfig!.speed /
|
||||
1000.0),
|
||||
focalPointDelta: Offset.zero));
|
||||
handleScaleEnd(ScaleEndDetails());
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void handleScaleEnd(ScaleEndDetails details) {
|
||||
if (extendedImageSlidePageState != null &&
|
||||
extendedImageSlidePageState!.isSliding) {
|
||||
extendedImageSlidePageState!.endSlide(details);
|
||||
return;
|
||||
}
|
||||
|
||||
if (_pageViewState != null && _pageViewState!.isDraging) {
|
||||
_pageViewState!.onDragEnd(DragEndDetails(
|
||||
velocity: details.velocity,
|
||||
primaryVelocity:
|
||||
_pageViewState!.widget.scrollDirection == Axis.horizontal
|
||||
? details.velocity.pixelsPerSecond.dx
|
||||
: details.velocity.pixelsPerSecond.dy,
|
||||
));
|
||||
return;
|
||||
}
|
||||
|
||||
//animate back to maxScale if gesture exceeded the maxScale specified
|
||||
if (_gestureDetails!.totalScale!.greaterThan(_gestureConfig!.maxScale)) {
|
||||
final double velocity =
|
||||
(_gestureDetails!.totalScale! - _gestureConfig!.maxScale) /
|
||||
_gestureConfig!.maxScale;
|
||||
|
||||
_gestureAnimation.animationScale(
|
||||
_gestureDetails!.totalScale, _gestureConfig!.maxScale, velocity);
|
||||
return;
|
||||
}
|
||||
|
||||
//animate back to minScale if gesture fell smaller than the minScale specified
|
||||
if (_gestureDetails!.totalScale!.lessThan(_gestureConfig!.minScale)) {
|
||||
final double velocity =
|
||||
(_gestureConfig!.minScale - _gestureDetails!.totalScale!) /
|
||||
_gestureConfig!.minScale;
|
||||
|
||||
_gestureAnimation.animationScale(
|
||||
_gestureDetails!.totalScale, _gestureConfig!.minScale, velocity);
|
||||
return;
|
||||
}
|
||||
|
||||
if (_gestureDetails!.actionType == ActionType.pan) {
|
||||
// get magnitude from gesture velocity
|
||||
final double magnitude = details.velocity.pixelsPerSecond.distance;
|
||||
|
||||
// do a significant magnitude
|
||||
if (magnitude.greaterThanOrEqualTo(minMagnitude)) {
|
||||
final Offset direction = details.velocity.pixelsPerSecond /
|
||||
magnitude *
|
||||
_gestureConfig!.inertialSpeed;
|
||||
|
||||
_gestureAnimation.animationOffset(
|
||||
_gestureDetails!.offset, _gestureDetails!.offset! + direction);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void handleScaleStart(ScaleStartDetails details) {
|
||||
_gestureAnimation.stop();
|
||||
_normalizedOffset = (details.focalPoint - _gestureDetails!.offset!) /
|
||||
_gestureDetails!.totalScale!;
|
||||
_startingScale = _gestureDetails!.totalScale;
|
||||
_startingOffset = details.focalPoint;
|
||||
}
|
||||
|
||||
@override
|
||||
void handleScaleUpdate(ScaleUpdateDetails details) {
|
||||
// 取消原组件对totalScale的判断。这样scale大于1也能执行slidePage的slide方法
|
||||
if (extendedImageSlidePageState != null &&
|
||||
_gestureDetails!.userOffset &&
|
||||
_gestureDetails!.actionType == ActionType.pan) {
|
||||
final Offset totalDelta = details.focalPointDelta;
|
||||
bool updateGesture = false;
|
||||
if (!extendedImageSlidePageState!.isSliding) {
|
||||
if (totalDelta.dx != 0 &&
|
||||
totalDelta.dx.abs().greaterThan(totalDelta.dy.abs())) {
|
||||
if (_gestureDetails!.computeHorizontalBoundary) {
|
||||
if (totalDelta.dx > 0) {
|
||||
updateGesture = _gestureDetails!.boundary.left;
|
||||
} else {
|
||||
updateGesture = _gestureDetails!.boundary.right;
|
||||
}
|
||||
} else {
|
||||
updateGesture = true;
|
||||
}
|
||||
}
|
||||
if (totalDelta.dy != 0 &&
|
||||
totalDelta.dy.abs().greaterThan(totalDelta.dx.abs())) {
|
||||
if (_gestureDetails!.computeVerticalBoundary) {
|
||||
if (totalDelta.dy < 0) {
|
||||
updateGesture = _gestureDetails!.boundary.bottom;
|
||||
} else {
|
||||
updateGesture = _gestureDetails!.boundary.top;
|
||||
}
|
||||
} else {
|
||||
updateGesture = true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
updateGesture = true;
|
||||
}
|
||||
|
||||
if (details.focalPointDelta.distance.greaterThan(minGesturePageDelta) &&
|
||||
updateGesture) {
|
||||
extendedImageSlidePageState!.slide(
|
||||
details.focalPointDelta,
|
||||
extendedImageGestureState: this,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (extendedImageSlidePageState != null &&
|
||||
extendedImageSlidePageState!.isSliding) {
|
||||
return;
|
||||
}
|
||||
|
||||
// totalScale > 1 and page view is starting to move
|
||||
if (_pageViewState != null) {
|
||||
final ExtendedImageGesturePageViewState pageViewState = _pageViewState!;
|
||||
|
||||
final Axis axis = pageViewState.widget.scrollDirection;
|
||||
final bool movePage = _pageViewState!.isDraging ||
|
||||
(details.pointerCount == 1 &&
|
||||
details.scale == 1 &&
|
||||
_gestureDetails!.movePage(details.focalPointDelta, axis));
|
||||
|
||||
if (movePage) {
|
||||
if (!pageViewState.isDraging) {
|
||||
pageViewState
|
||||
.onDragDown(DragDownDetails(globalPosition: details.focalPoint));
|
||||
pageViewState.onDragStart(
|
||||
DragStartDetails(globalPosition: details.focalPoint));
|
||||
//assert(!pageViewState.isDraging);
|
||||
}
|
||||
Offset delta = details.focalPointDelta;
|
||||
delta =
|
||||
axis == Axis.horizontal ? Offset(delta.dx, 0) : Offset(0, delta.dy);
|
||||
|
||||
pageViewState.onDragUpdate(DragUpdateDetails(
|
||||
globalPosition: details.focalPoint,
|
||||
delta: delta,
|
||||
primaryDelta: axis == Axis.horizontal ? delta.dx : delta.dy,
|
||||
));
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
final double? scale = widget.canScaleImage(_gestureDetails)
|
||||
? clampScale(
|
||||
_startingScale! * details.scale * _gestureConfig!.speed,
|
||||
_gestureConfig!.animationMinScale,
|
||||
_gestureConfig!.animationMaxScale)
|
||||
: _gestureDetails!.totalScale;
|
||||
|
||||
//Round the scale to three points after comma to prevent shaking
|
||||
//scale = roundAfter(scale, 3);
|
||||
//no more zoom
|
||||
if (details.scale != 1.0 &&
|
||||
((_gestureDetails!.totalScale!
|
||||
.equalTo(_gestureConfig!.animationMinScale) &&
|
||||
scale!.lessThanOrEqualTo(_gestureDetails!.totalScale!)) ||
|
||||
(_gestureDetails!.totalScale!
|
||||
.equalTo(_gestureConfig!.animationMaxScale) &&
|
||||
scale!.greaterThanOrEqualTo(_gestureDetails!.totalScale!)))) {
|
||||
return;
|
||||
}
|
||||
|
||||
final Offset offset = (details.scale == 1.0
|
||||
? details.focalPoint * _gestureConfig!.speed
|
||||
: _startingOffset) -
|
||||
_normalizedOffset * scale!;
|
||||
|
||||
if (mounted &&
|
||||
(offset != _gestureDetails!.offset ||
|
||||
scale != _gestureDetails!.totalScale)) {
|
||||
gestureDetails = GestureDetails(
|
||||
offset: offset,
|
||||
totalScale: scale,
|
||||
gestureDetails: _gestureDetails,
|
||||
actionType: details.scale != 1.0 ? ActionType.zoom : ActionType.pan);
|
||||
}
|
||||
}
|
||||
|
||||
void _initGestureConfig() {
|
||||
final double? initialScale = _gestureConfig?.initialScale;
|
||||
final InitialAlignment? initialAlignment = _gestureConfig?.initialAlignment;
|
||||
_gestureConfig = widget
|
||||
.extendedImageState.imageWidget.initGestureConfigHandler
|
||||
?.call(widget.extendedImageState) ??
|
||||
GestureConfig();
|
||||
|
||||
if (_gestureDetails == null ||
|
||||
initialScale != _gestureConfig!.initialScale ||
|
||||
initialAlignment != _gestureConfig!.initialAlignment) {
|
||||
_gestureDetails = GestureDetails(
|
||||
totalScale: _gestureConfig!.initialScale,
|
||||
offset: Offset.zero,
|
||||
initialAlignment: _gestureConfig!.initialAlignment,
|
||||
);
|
||||
}
|
||||
|
||||
if (_gestureConfig!.cacheGesture) {
|
||||
final GestureDetails? cache =
|
||||
_gestureDetailsCache[widget.extendedImageState.imageStreamKey];
|
||||
if (cache != null) {
|
||||
_gestureDetails = cache;
|
||||
}
|
||||
}
|
||||
_gestureDetails ??= GestureDetails(
|
||||
totalScale: _gestureConfig!.initialScale,
|
||||
offset: Offset.zero,
|
||||
);
|
||||
|
||||
_gestureAnimation = GestureAnimation(this, offsetCallBack: (Offset value) {
|
||||
gestureDetails = GestureDetails(
|
||||
offset: value,
|
||||
totalScale: _gestureDetails!.totalScale,
|
||||
gestureDetails: _gestureDetails);
|
||||
}, scaleCallBack: (double scale) {
|
||||
gestureDetails = GestureDetails(
|
||||
offset: _gestureDetails!.offset,
|
||||
totalScale: scale,
|
||||
gestureDetails: _gestureDetails,
|
||||
actionType: ActionType.zoom,
|
||||
userOffset: false);
|
||||
});
|
||||
}
|
||||
|
||||
double _reverseIf(double scaleDetal) {
|
||||
if (_gestureConfig?.reverseMousePointerScrollDirection ?? false) {
|
||||
return -scaleDetal;
|
||||
} else {
|
||||
return scaleDetal;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user