perlatus / flutter_zoomable_image

A plugin for panning and zooming images by touch.
ISC License
119 stars 43 forks source link

Missing physics for flick motions #1

Open perlatus opened 7 years ago

perlatus commented 7 years ago

No physics at the moment, so the moment your finger comes off the screen the image stops moving.

BenSower commented 7 years ago

Heyho, I started working on it and it works when you are flinging down, but I think there is a problem with the small-box/big-box logic. I think I am still missing something and haven't understood everything properly. If you want to, take a look at it!

import 'package:flutter/material.dart';
import 'package:zoomable_image/zoomable_image.dart';
import '../common.dart';

const double _kMinFlingVelocity = 300.0;

class PhotoViewer extends StatefulWidget {
  const PhotoViewer({Key key, this.imageURL}) : super(key: key);

  final String imageURL;

  @override
  _PhotoViewerState createState() => new _PhotoViewerState(imageURL);
}

class _PhotoViewerState extends State<PhotoViewer> with SingleTickerProviderStateMixin {
  AnimationController _controller;
  Animation<Offset> _flingAnimation;
  Offset _offset = Offset.zero;
  double _scale = 1.0;
  Offset _normalizedOffset;
  double _previousScale;

  final String _imageURL;

  _PhotoViewerState(this._imageURL);

  @override
  void initState() {
    super.initState();
    _controller = new AnimationController(vsync: this)..addListener(_handleFlingAnimation);
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  // The maximum offset value is 0,0. If the size of this renderer's box is w,h
  // then the minimum offset value is w - _scale * w, h - _scale * h.
  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));
  }

  void _handleFlingAnimation() {
    setState(() {
      _offset = _flingAnimation.value;
    });
  }

  void _handleDoubleTap() {
    //TODO: animate!
    setState(() {
      if (_scale > 1.0) {
        _scale = 1.0;
        _offset = Offset.zero;
      } else {
        _scale = 3.0;
        _offset = _clampOffset(new Offset(context.size.width, context.size.height) / -2.0);
      }
    });
  }

  void _handleOnScaleStart(ScaleStartDetails details) {
    setState(() {
      _previousScale = _scale;
      _normalizedOffset = (details.focalPoint - _offset) / _scale;
      // The fling animation stops if an input gesture starts.
      _controller.stop();
    });
  }

  void _handleOnScaleUpdate(ScaleUpdateDetails details) {
    setState(() {
      _scale = (_previousScale * details.scale).clamp(1.0, 4.0);
      // Ensure that image location under the focal point stays in the same place despite scaling.
      _offset = _clampOffset(details.focalPoint - _normalizedOffset * _scale);
    });
  }

  void _handleOnScaleEnd(ScaleEndDetails details) {
    final double magnitude = details.velocity.pixelsPerSecond.distance;
    if (magnitude < _kMinFlingVelocity) return;
    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)).animate(_controller);
    _controller
      ..value = 0.0
      ..fling(velocity: magnitude / 1000.0);
  }

  @override
  Widget build(BuildContext context) {
    Widget image = getProductImage(_imageURL);
    return new ZoomableImage(new NetworkImage(_imageURL), scale: 16.0, backgroundColor: Colors.white);

    // return new GestureDetector(
    //     //TODO: add proper double tab zoom in/zoom out
    //     onScaleStart: _handleOnScaleStart,
    //     onScaleUpdate: _handleOnScaleUpdate,
    //     onScaleEnd: _handleOnScaleEnd,
    //     onDoubleTap: _handleDoubleTap,
    //     child: new ClipRect(
    //         child: new Transform(
    //       transform: new Matrix4.identity()
    //         ..translate(_offset.dx, _offset.dy)
    //         ..scale(_scale),
    //       child: image,
    //     )));
  }
}