Browse Source

[Modify]使用AnimationController实现帧动画

zhipeng 1 year ago
parent
commit
3114f40b58
2 changed files with 29 additions and 24 deletions
  1. 5 0
      lib/module/record/view.dart
  2. 24 24
      lib/widget/frame_animation_view.dart

+ 5 - 0
lib/module/record/view.dart

@@ -125,6 +125,11 @@ class RecordPage extends BasePage<RecordController> {
         opacity:
             controller.currentStatus.value == RecordStatus.recording ? 1 : 0,
         duration: const Duration(milliseconds: 520),
+        onEnd: () {
+          if (controller.currentStatus.value != RecordStatus.recording) {
+            controller.frameAnimationController.stop();
+          }
+        },
         child: FrameAnimationView(
           controller: controller.frameAnimationController,
           framePath: 'assets/anim/anim_recording.zip',

+ 24 - 24
lib/widget/frame_animation_view.dart

@@ -37,16 +37,20 @@ class FrameAnimationView extends StatefulWidget {
   }
 }
 
-class FrameAnimationViewState extends State<FrameAnimationView> {
-  int _currentFrame = -1;
+class FrameAnimationViewState extends State<FrameAnimationView>
+    with SingleTickerProviderStateMixin {
+  late final AnimationController _frameAnimationController;
   List<File> imageFiles = [];
   List<ui.Image> images = [];
-  Timer? _timer;
   StreamSubscription? _precacheStreamSubscription;
 
   @override
   void initState() {
     super.initState();
+    _frameAnimationController = AnimationController(
+      vsync: this,
+    );
+    _frameAnimationController.addListener(() => setState(() {}));
     widget.controller?.bindState(this);
     loadFrameFromAssets()
         .then((_) => initializeImageFiles())
@@ -59,8 +63,8 @@ class FrameAnimationViewState extends State<FrameAnimationView> {
 
   @override
   void dispose() {
+    _frameAnimationController.dispose();
     super.dispose();
-    _timer?.cancel();
     _precacheStreamSubscription?.cancel();
     imageFiles.clear();
     for (var image in images) {
@@ -71,14 +75,15 @@ class FrameAnimationViewState extends State<FrameAnimationView> {
 
   @override
   Widget build(BuildContext context) {
-    if (_timer == null ||
-        _currentFrame < 0 ||
-        images.isEmpty ||
-        _currentFrame >= images.length) {
+    if (imageFiles.isEmpty || !_frameAnimationController.isAnimating) {
       return SizedBox(width: widget.width, height: widget.height);
     }
+    final currentFrame = (_frameAnimationController.value * imageFiles.length)
+        .floor()
+        .clamp(0, images.length - 1);
+    debugPrint('currentFrame: $currentFrame');
     return RawImage(
-      image: images[_currentFrame],
+      image: images[currentFrame],
       width: widget.width,
       height: widget.height,
     );
@@ -192,24 +197,19 @@ class FrameAnimationViewState extends State<FrameAnimationView> {
     if (imageFiles.isEmpty) {
       return;
     }
-
-    if (_timer != null && _timer!.isActive) {
+    if (_frameAnimationController.isAnimating) {
       return;
     }
 
     double speed = widget.speed ?? 1.0;
 
-    _timer = Timer.periodic(
-        Duration(milliseconds: 1000 ~/ (widget.frameRate * speed)), (_) {
-      if (images.isEmpty) {
-        return;
-      }
-      setState(() {
-        int targetFrame = (_currentFrame + 1) % imageFiles.length;
-        _currentFrame =
-            targetFrame >= images.length ? images.length - 1 : targetFrame;
-      });
-    });
+    int duration = (1000 ~/ widget.frameRate * imageFiles.length ~/ speed);
+
+    debugPrint('frame animation duration: $duration');
+
+    _frameAnimationController.duration = Duration(milliseconds: duration);
+
+    _frameAnimationController.repeat();
   }
 
   initializeImageFiles() async {
@@ -255,10 +255,10 @@ class FrameAnimationController {
   }
 
   void stop() {
-    _state?._timer?.cancel();
+    _state?._frameAnimationController.stop();
   }
 
-  bool get isPlaying => _state?._timer?.isActive ?? false;
+  bool get isPlaying => _state?._frameAnimationController.isAnimating ?? false;
 
   void bindState(FrameAnimationViewState state) {
     _state = state;