|
|
|
|
@ -1,3 +1,5 @@
|
|
|
|
|
import 'dart:math';
|
|
|
|
|
|
|
|
|
|
import 'package:flutter/material.dart';
|
|
|
|
|
import './custom_gesture_detector.dart' as gd;
|
|
|
|
|
|
|
|
|
|
@ -51,7 +53,7 @@ class _TouchableContainerState extends State<TouchableContainer>
|
|
|
|
|
@override
|
|
|
|
|
void initState() {
|
|
|
|
|
super.initState();
|
|
|
|
|
_controller = new AnimationController(vsync: this)..addListener(_handleFlingAnimation);
|
|
|
|
|
_controller = AnimationController(vsync: this)..addListener(_handleFlingAnimation);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@override
|
|
|
|
|
@ -65,8 +67,30 @@ class _TouchableContainerState extends State<TouchableContainer>
|
|
|
|
|
//也就是最小值是原点0,0,点从最大值到0的区间,也就是这个图可以从最大值移动到原点
|
|
|
|
|
Offset _clampOffset(Offset offset) {
|
|
|
|
|
final Size size = context.size; //容器的大小
|
|
|
|
|
final Offset minOffset = new Offset(size.width, size.height) * (1.0 - _scale);
|
|
|
|
|
return new Offset(offset.dx.clamp(minOffset.dx, 0.0), offset.dy.clamp(minOffset.dy, 0.0));
|
|
|
|
|
// print("context.size = ${context.size}"); // context.size = Size(298.0, 298.0)
|
|
|
|
|
final Offset minOffset = Offset(size.width, size.height) * (1.0 - _scale);
|
|
|
|
|
return Offset(offset.dx.clamp(minOffset.dx, 0.0), offset.dy.clamp(minOffset.dy, 0.0));
|
|
|
|
|
// return Offset(minOffset.dx / 2, minOffset.dy / 2);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 实现双击图片放大后,点击位置位于容器中央
|
|
|
|
|
Offset _clampOffset2(Offset position) {
|
|
|
|
|
final Size size = context.size; //容器的大小
|
|
|
|
|
// print("context.size = ${context.size}"); // context.size = Size(298.0, 298.0)
|
|
|
|
|
RenderBox renderBox = context.findRenderObject();
|
|
|
|
|
Offset offsetTopLeft = renderBox.localToGlobal(Offset.zero);
|
|
|
|
|
print("offsetTopLeft = $offsetTopLeft"); // offsetTopLeft = Offset(31.0, 111.0)
|
|
|
|
|
|
|
|
|
|
double x = position.dx - offsetTopLeft.dx;
|
|
|
|
|
x = max(x * _scale - size.width * _scale / 4, 0);
|
|
|
|
|
x = min(x, size.width * _scale / 2);
|
|
|
|
|
|
|
|
|
|
double y = position.dy - offsetTopLeft.dy;
|
|
|
|
|
y = max(y * _scale - size.height * _scale / 4, 0);
|
|
|
|
|
y = min(y, size.height * _scale / 2);
|
|
|
|
|
print("Offset(x, y) = ${Offset(-x, -y)}");
|
|
|
|
|
|
|
|
|
|
return Offset(-x, -y);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void _handleFlingAnimation() {
|
|
|
|
|
@ -92,7 +116,7 @@ class _TouchableContainerState extends State<TouchableContainer>
|
|
|
|
|
// Ensure that image location under the focal point stays in the same place despite scaling.
|
|
|
|
|
_offset = _clampOffset(details.focalPoint - _normalizedOffset * _scale);
|
|
|
|
|
});
|
|
|
|
|
ScaleChangedModel model = new ScaleChangedModel(scale: _scale, offset: _offset);
|
|
|
|
|
ScaleChangedModel model = ScaleChangedModel(scale: _scale, offset: _offset);
|
|
|
|
|
if (widget.scaleChangedCallback != null) widget.scaleChangedCallback(model);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@ -102,7 +126,7 @@ class _TouchableContainerState extends State<TouchableContainer>
|
|
|
|
|
final Offset direction = details.velocity.pixelsPerSecond / magnitude;
|
|
|
|
|
final double distance = (Offset.zero & context.size).shortestSide;
|
|
|
|
|
_flingAnimation =
|
|
|
|
|
new Tween<Offset>(begin: _offset, end: _clampOffset(_offset + direction * distance))
|
|
|
|
|
Tween<Offset>(begin: _offset, end: _clampOffset(_offset + direction * distance))
|
|
|
|
|
.animate(_controller);
|
|
|
|
|
_controller
|
|
|
|
|
..value = 0.0
|
|
|
|
|
@ -111,33 +135,41 @@ class _TouchableContainerState extends State<TouchableContainer>
|
|
|
|
|
|
|
|
|
|
void _onDoubleTap(gd.DoubleDetails details) {
|
|
|
|
|
_normalizedOffset = (details.pointerEvent.position - _offset) / _scale;
|
|
|
|
|
print("_scale = ${_scale}, _offset = ${_offset}");
|
|
|
|
|
print("position = ${details.pointerEvent.position}, _normalizedOffset = ${_normalizedOffset}");
|
|
|
|
|
// _scale = 1.0, _offset = Offset(0.0, 0.0)
|
|
|
|
|
// position = Offset(178.0, 260.7), _normalizedOffset = Offset(178.0, 260.7)
|
|
|
|
|
if (!widget.doubleTapStillScale && _scale != 1.0) {
|
|
|
|
|
setState(() {
|
|
|
|
|
_scale = 1.0;
|
|
|
|
|
_offset = Offset.zero;
|
|
|
|
|
});
|
|
|
|
|
ScaleChangedModel model = new ScaleChangedModel(scale: _scale, offset: _offset);
|
|
|
|
|
ScaleChangedModel model = ScaleChangedModel(scale: _scale, offset: _offset);
|
|
|
|
|
if (widget.scaleChangedCallback != null) widget.scaleChangedCallback(model);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
setState(() {
|
|
|
|
|
if (widget.doubleTapStillScale) {
|
|
|
|
|
_scale *= (1 + 0.5);
|
|
|
|
|
// Ensure that image location under the focal point stays in the same place despite scaling.
|
|
|
|
|
// _offset = doubleDownPositon;
|
|
|
|
|
_offset = _clampOffset(details.pointerEvent.position - _normalizedOffset * _scale);
|
|
|
|
|
} else {
|
|
|
|
|
_scale *= (2);
|
|
|
|
|
_offset = _clampOffset2(details.pointerEvent.position);
|
|
|
|
|
// _offset = Offset.zero; // 对齐左上角
|
|
|
|
|
print("_scale = ${_scale}, _offset = ${_offset}");
|
|
|
|
|
// _scale = 2.0, _offset = Offset(-175.3, -270.0)
|
|
|
|
|
}
|
|
|
|
|
// Ensure that image location under the focal point stays in the same place despite scaling.
|
|
|
|
|
// _offset = doubleDownPositon;
|
|
|
|
|
_offset = _clampOffset(details.pointerEvent.position - _normalizedOffset * _scale);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
ScaleChangedModel model = new ScaleChangedModel(scale: _scale, offset: _offset);
|
|
|
|
|
ScaleChangedModel model = ScaleChangedModel(scale: _scale, offset: _offset);
|
|
|
|
|
if (widget.scaleChangedCallback != null) widget.scaleChangedCallback(model);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@override
|
|
|
|
|
Widget build(BuildContext context) {
|
|
|
|
|
return new gd.GestureDetector(
|
|
|
|
|
return gd.GestureDetector(
|
|
|
|
|
// onPanDown: _onPanDown,
|
|
|
|
|
onDoubleTap: _onDoubleTap,
|
|
|
|
|
onScaleStart: _handleOnScaleStart,
|
|
|
|
|
@ -151,8 +183,8 @@ class _TouchableContainerState extends State<TouchableContainer>
|
|
|
|
|
minWidth: double.maxFinite,
|
|
|
|
|
minHeight: double.infinity,
|
|
|
|
|
),
|
|
|
|
|
child: new Transform(
|
|
|
|
|
transform: new Matrix4.identity()
|
|
|
|
|
child: Transform(
|
|
|
|
|
transform: Matrix4.identity()
|
|
|
|
|
..translate(_offset.dx, _offset.dy)
|
|
|
|
|
..scale(_scale, _scale, 1.0),
|
|
|
|
|
child: widget.child),
|
|
|
|
|
|