opt: matrix anim (#1829)

This commit is contained in:
My-Responsitories
2026-02-08 15:27:03 +08:00
committed by GitHub
parent 4ac855d393
commit 8234b7ac92
6 changed files with 156 additions and 111 deletions

View File

@@ -1,5 +1,8 @@
import 'dart:ui' as ui;
import 'package:PiliPlus/common/widgets/interactiveviewer_gallery/interactive_viewer.dart'
as custom;
import 'package:PiliPlus/common/widgets/only_layout_widget.dart';
import 'package:flutter/material.dart';
/// https://github.com/qq326646683/interactiveviewer_gallery
@@ -53,15 +56,16 @@ class InteractiveViewerBoundary extends StatefulWidget {
class InteractiveViewerBoundaryState extends State<InteractiveViewerBoundary>
with SingleTickerProviderStateMixin {
late TransformationController _controller;
late AnimationController _animateController;
late Animation<Offset> _slideAnimation;
late Animation<double> _scaleAnimation;
late Animation<Decoration> _opacityAnimation;
late final TransformationController _controller;
late final AnimationController _animateController;
late final Animation<Decoration> _opacityAnimation;
double dx = 0, dy = 0;
Offset _offset = Offset.zero;
bool _dragging = false;
late Size _size;
bool get _isActive => _dragging || _animateController.isAnimating;
@override
@@ -74,35 +78,42 @@ class InteractiveViewerBoundaryState extends State<InteractiveViewerBoundary>
vsync: this,
);
_updateMoveAnimation();
_scaleAnimation = _animateController.drive(
Tween<double>(begin: 1.0, end: 0.25),
);
_opacityAnimation = _animateController.drive(
DecorationTween(
begin: const BoxDecoration(color: Colors.black),
end: const BoxDecoration(color: Colors.transparent),
),
);
_animateController.addListener(_updateTransformation);
}
@override
void dispose() {
_animateController.dispose();
super.dispose();
void _updateTransformation() {
final val = _animateController.value;
final scale = ui.lerpDouble(1.0, 0.25, val)!;
// Matrix4.identity()
// ..translateByDouble(size.width / 2, size.height / 2, 0, 1)
// ..translateByDouble(size.width * val * dx, size.height * val * dy, 0, 1)
// ..scaleByDouble(scale, scale, 1, 1)
// ..translateByDouble(-size.width / 2, -size.height / 2, 0, 1);
final tmp = (1.0 - scale) / 2.0;
_controller.value = Matrix4.diagonal3Values(scale, scale, scale)
..setTranslationRaw(
_size.width * (val * dx + tmp),
_size.height * (val * dy + tmp),
0,
);
}
void _updateMoveAnimation() {
final double endX = _offset.dx.sign * (_offset.dx.abs() / _offset.dy.abs());
final double endY = _offset.dy.sign;
_slideAnimation = _animateController.drive(
Tween<Offset>(
begin: Offset.zero,
end: Offset(endX, endY),
),
);
dy = _offset.dy.sign;
if (dy == 0) {
dx = 0;
} else {
dx = _offset.dx / _offset.dy.abs();
}
}
void _handleDragStart(ScaleStartDetails details) {
@@ -114,7 +125,7 @@ class InteractiveViewerBoundaryState extends State<InteractiveViewerBoundary>
_offset = Offset.zero;
_animateController.value = 0.0;
}
setState(_updateMoveAnimation);
_updateMoveAnimation();
}
void _handleDragUpdate(ScaleUpdateDetails details) {
@@ -123,11 +134,10 @@ class InteractiveViewerBoundaryState extends State<InteractiveViewerBoundary>
}
_offset += details.focalPointDelta;
setState(_updateMoveAnimation);
_updateMoveAnimation();
if (!_animateController.isAnimating) {
_animateController.value = _offset.dy.abs() / context.size!.height;
_animateController.value = _offset.dy.abs() / _size.height;
}
}
@@ -153,29 +163,32 @@ class InteractiveViewerBoundaryState extends State<InteractiveViewerBoundary>
}
}
Widget get content => DecoratedBoxTransition(
decoration: _opacityAnimation,
child: SlideTransition(
position: _slideAnimation,
child: ScaleTransition(
scale: _scaleAnimation,
child: widget.child,
),
),
);
@override
void dispose() {
_animateController
..removeListener(_updateTransformation)
..dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return custom.InteractiveViewer(
maxScale: widget.maxScale,
minScale: widget.minScale,
transformationController: _controller,
onPanStart: _handleDragStart,
onPanUpdate: _handleDragUpdate,
onPanEnd: _handleDragEnd,
onInteractionEnd: widget.onInteractionEnd,
isAnimating: () => _animateController.value != 0,
child: content,
return LayoutSizeWidget(
onPerformLayout: (size) => _size = size,
child: DecoratedBoxTransition(
decoration: _opacityAnimation,
child: custom.InteractiveViewer(
maxScale: widget.maxScale,
minScale: widget.minScale,
transformationController: _controller,
onPanStart: _handleDragStart,
onPanUpdate: _handleDragUpdate,
onPanEnd: _handleDragEnd,
onInteractionEnd: widget.onInteractionEnd,
isAnimating: () => _animateController.value != 0,
child: widget.child,
),
),
);
}
}