Skip to content

Instantly share code, notes, and snippets.

@pskink
Created July 10, 2020 13:22
Show Gist options
  • Select an option

  • Save pskink/91a7579ee2915a3cbaf83c22ecd7afb7 to your computer and use it in GitHub Desktop.

Select an option

Save pskink/91a7579ee2915a3cbaf83c22ecd7afb7 to your computer and use it in GitHub Desktop.
class CustomImageScroller extends StatefulWidget {
final String asset;
CustomImageScroller({Key key, this.asset}) : super(key: key);
@override
_CustomImageScrollerState createState() => _CustomImageScrollerState();
}
class _CustomImageScrollerState extends State<CustomImageScroller> with TickerProviderStateMixin {
AnimationController xc;
AnimationController yc;
ValueNotifier<ui.Image> imageNotifier;
double dx = 0.0;
double dy = 0.0;
@override
void initState() {
super.initState();
xc = AnimationController.unbounded(vsync: this);
yc = AnimationController.unbounded(vsync: this);
imageNotifier = ValueNotifier(null);
Future(() => rootBundle.load(widget.asset).then(_decodeAssetData));
}
@override
Widget build(BuildContext context) {
print('_CustomImageScrollerState.build');
return GestureDetector(
onPanUpdate: (d) {
_handlePanUpdate(xc, dx, d.delta.dx);
_handlePanUpdate(yc, dy, d.delta.dy);
},
onPanEnd: (d) {
_handlePanEnd(xc, dx, d.velocity.pixelsPerSecond.dx);
_handlePanEnd(yc, dy, d.velocity.pixelsPerSecond.dy);
},
child: CustomPaint(
painter: ImagePainter(xc, yc, imageNotifier),
child: SizedBox.expand(),
),
);
}
void _decodeAssetData(data) async {
print('_decodeAssetData');
var image = await decodeImageFromList(data.buffer.asUint8List());
imageNotifier.value = image;
RenderBox rb = context.findRenderObject();
dx = rb.size.width - image.width;
dy = rb.size.height - image.height;
if (dx > 0) xc.value = dx / 2;
if (dy > 0) yc.value = dy / 2;
}
void _handlePanUpdate(AnimationController controller, double sizeDifference, double delta) {
if (sizeDifference < 0) {
controller.value += delta;
}
}
final sd = SpringDescription.withDampingRatio(
mass: 0.5,
stiffness: 300.0,
ratio: 0.85,
);
void _handlePanEnd(AnimationController controller, double sizeDifference, double velocity) {
if (sizeDifference < 0) {
var simulation = (sizeDifference <= controller.value && controller.value <= 0)?
ClampedSimulation(FrictionSimulation(0.001, controller.value, velocity), xMin: sizeDifference, xMax: 0) :
SpringSimulation(sd, controller.value, controller.value.clamp(sizeDifference, 0.0), 0);
controller.animateWith(simulation);
}
}
}
class ImagePainter extends CustomPainter {
final AnimationController xc, yc;
final ValueNotifier<ui.Image> imageNotifier;
ImagePainter(this.xc, this.yc, this.imageNotifier) : super(repaint: Listenable.merge([xc, yc, imageNotifier]));
@override
void paint(ui.Canvas canvas, ui.Size size) {
// print('$xc $yc $imageNotifier');
canvas.clipRect(Offset.zero & size);
if (imageNotifier.value != null) {
canvas.drawImage(imageNotifier.value, Offset(xc.value, yc.value), Paint());
}
}
@override
bool shouldRepaint(CustomPainter oldDelegate) => true;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment