• Flutter-实现头像叠加动画效果


    实现头像叠加动画效果

    在这篇文章中,我们将介绍如何使用 Flutter 实现一个带有透明度渐变效果和过渡动画的头像叠加列表。通过这种效果,可以在图片切换时实现平滑的动画,使 UI 更加生动和吸引人。

    需求

    我们的目标是实现一个头像叠加列表,在每隔 2 秒时切换头像,并且在切换过程中,前一个头像逐渐消失,新进入的头像逐渐显示,同时有一个从右向左的移动过渡效果。

    具体需求包括:

    1. 支持头像圆形显示。
    2. 支持设置头像重叠比例。
    3. 支持配置间隔时间切换一次头像。
    4. 切换时,前一个头像透明度渐变消失,后一个头像透明度渐变显示。
    5. 切换时,有平滑的移动动画。

    效果

    在这里插入图片描述

    实现思路

    为了实现这个效果,我们将使用 Flutter 的 AnimatedBuilderAnimationControllerTween 来实现过渡动画和透明度渐变效果。主要步骤包括:

    1. 创建一个 CircularImageList 组件,用于显示头像列表。
    2. 使用 AnimationController 控制动画的执行。
    3. 使用 AnimatedBuilderOpacity 实现透明度渐变效果。
    4. 使用 PositionedAnimatedBuilder 实现位置移动过渡效果。
    5. 每隔 2 秒触发一次动画,并更新显示的头像列表。

    实现代码

    下面是实现上述需求的完整代码:

    import 'package:cached_network_image/cached_network_image.dart';
    import 'package:flutter/material.dart';
    
    class CircularImageList extends StatefulWidget {
      final List<String> imageUrls;
      final int maxDisplayCount;
      final double overlapRatio;
      final double height;
      final Duration animDuration;
      final Duration delayedDuration;
    
      const CircularImageList({
        super.key,
        required this.imageUrls,
        required this.maxDisplayCount,
        required this.overlapRatio,
        required this.height,
        this.animDuration = const Duration(milliseconds: 500),
        this.delayedDuration = const Duration(seconds: 1),
      });
    
      
      CircularImageListState createState() => CircularImageListState();
    }
    
    class CircularImageListState extends State<CircularImageList>
        with SingleTickerProviderStateMixin {
      int _currentIndex = 0;
      List<String> _currentImages = [];
      late AnimationController _animationController;
      late Animation<double> _animation;
    
      int get maxDisplayCount {
        return widget.maxDisplayCount + 1;
      }
    
      double get circularImageWidth {
        var realCount = maxDisplayCount - 1;
        return realCount * widget.height -
            widget.height * (1 - widget.overlapRatio) * (realCount - 1);
      }
    
      
      void initState() {
        super.initState();
        _currentImages = widget.imageUrls.take(maxDisplayCount).toList();
        _animationController = AnimationController(
          duration: widget.animDuration,
          vsync: this,
        );
    
        _animation = Tween<double>(begin: 0, end: 1).animate(_animationController)
          ..addStatusListener((status) {
            if (status == AnimationStatus.completed) {
              setState(() {
                _currentIndex = (_currentIndex + 1) % widget.imageUrls.length;
                _currentImages.removeAt(0);
                _currentImages.add(widget.imageUrls[_currentIndex]);
              });
              _animationController.reset();
              Future.delayed(widget.delayedDuration, () {
                _animationController.forward();
              });
            }
          });
    
        Future.delayed(widget.delayedDuration, () {
          _animationController.forward();
        });
      }
    
      
      void dispose() {
        _animationController.dispose();
        super.dispose();
      }
    
      
      Widget build(BuildContext context) {
        return Container(
          clipBehavior: Clip.none,
          width: circularImageWidth,
          height: widget.height,
          child: Stack(
            clipBehavior: Clip.none,
            children: _buildImageStack(),
          ),
        );
      }
    
      double _opacity(int index) {
        if (index == 0) {
          return 1 - _animation.value;
        } else if (index == _currentImages.length - 1) {
          return _animation.value;
        } else {
          return 1;
        }
      }
    
      List<Widget> _buildImageStack() {
        List<Widget> stackChildren = [];
        for (int i = 0; i < _currentImages.length; i++) {
          double leftOffset = i * (widget.height * widget.overlapRatio);
          stackChildren.add(
            AnimatedBuilder(
              animation: _animation,
              builder: (context, child) {
                return Positioned(
                  left: leftOffset -
                      (_animation.value * widget.height * widget.overlapRatio),
                  child: Opacity(
                    opacity: _opacity(i),
                    child: child!,
                  ),
                );
              },
              child: ClipOval(
                key: ValueKey<String>(_currentImages[i]),
                child: CachedNetworkImage(
                  imageUrl: _currentImages[i],
                  width: widget.height,
                  height: widget.height,
                  fit: BoxFit.cover,
                ),
              ),
            ),
          );
        }
        return stackChildren;
      }
    }
    

    结束语

    通过上述代码,我们实现了一个带有透明度渐变效果和过渡动画的头像叠加列表。在实际开发中,可以根据需求对动画的时长、重叠比例等进行调整,以达到最佳效果。希望这篇文章对您有所帮助,如果有任何问题或建议,详情见:github.com/yixiaolunhui/flutter_xy

  • 相关阅读:
    SpringBoot基础
    Gitcode 创建仓库并且 提交代码
    Linux本地部署1Panel现代化运维管理面板并实现公网访问
    Mysql主从复制、读写分离
    Spring总结
    时间是主观的,每个人对时间的感受都不同#Exploration of Time - 时间的探索
    Docker Machine简介
    Python 获取旅游景点信息及评论并作词云、数据可视化
    Nginx一主一从配置Keepalive
    JVM系列一
  • 原文地址:https://blog.csdn.net/u014741977/article/details/139886984