You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何实现支持手动上下拖动字母块的类老虎机动画?

实现手动拖动式老虎机的思路提示

首先,咱们可以把每个老虎机卷轴拆成独立的可垂直拖动列表组件,这是核心。下面是具体的拆解思路:

  • 单个卷轴的基础实现
    用可滚动的列表(比如ListView.builder或者自定义的SingleChildScrollView)来承载所有字母项。为了让拖动更贴合老虎机的硬朗手感,建议给列表设置physics: ClampingScrollPhysics(),避免默认的弹性滚动效果干扰体验。
    每个字母项的高度必须统一,这是后续实现精准吸附定位的前提。

  • 拖动结束后的自动吸附定位
    这是让体验更专业的关键步骤:用户拖动松手后,卷轴需要自动滚动到最近的字母项位置,确保选中的字母正好落在视觉焦点上。可以通过监听滚动控制器的滚动结束事件来实现:

    1. 获取当前滚动的偏移量
    2. 用偏移量除以单个字母项的高度,算出当前最接近的目标索引
    3. 调用scrollController.animateTo()滚动到对应索引的位置,搭配Curves.easeOut这类平缓动画,让吸附过程更自然
  • 手势交互的精细化优化
    如果觉得默认的滚动拖动不够顺手,也可以用GestureDetector包裹卷轴,手动监听手势事件来控制滚动:

    • onPanUpdate时,根据手指垂直移动的距离实时更新滚动偏移
    • onPanEnd时,计算手指的滑动速度,给卷轴添加一段惯性滚动的效果,再触发吸附逻辑,还原真实老虎机的拖动手感
  • 多卷轴的状态联动管理
    如果需要多个卷轴配合(比如全部拖动完成后判断结果),可以用状态管理工具(比如Provider、Riverpod,甚至简单的setState)来统一记录每个卷轴的选中索引,方便后续快速获取所有选中的字母组合。

  • 视觉效果增强细节

    • 在卷轴的视觉中心位置添加高亮提示(比如半透明的上下渐变遮罩,或者一个固定的选中框),让用户一眼看清当前选中的字母
    • 拖动过程中给字母项添加轻微的缩放或透明度变化,增强交互反馈感

举个单个卷轴的核心逻辑示例:

class SlotReel extends StatefulWidget {
  final List<String> items;
  const SlotReel({super.key, required this.items});

  @override
  State<SlotReel> createState() => _SlotReelState();
}

class _SlotReelState extends State<SlotReel> {
  final ScrollController _scrollController = ScrollController();
  final double _itemHeight = 60; // 统一字母项高度

  @override
  void initState() {
    super.initState();
    // 监听滚动结束事件
    _scrollController.addListener(_handleScrollEnd);
  }

  void _handleScrollEnd() {
    if (!_scrollController.position.isScrollingNotifier.value) {
      final currentOffset = _scrollController.offset;
      final targetIndex = (currentOffset / _itemHeight).round();
      final targetOffset = targetIndex * _itemHeight;
      _scrollController.animateTo(
        targetOffset,
        duration: const Duration(milliseconds: 300),
        curve: Curves.easeOut,
      );
    }
  }

  @override
  Widget build(BuildContext context) {
    return Container(
      width: 80,
      height: _itemHeight * 3, // 固定显示3个字母的高度
      clipBehavior: Clip.hardEdge,
      child: ListView.builder(
        controller: _scrollController,
        physics: const ClampingScrollPhysics(),
        itemCount: widget.items.length,
        itemBuilder: (context, index) {
          return SizedBox(
            height: _itemHeight,
            child: Center(
              child: Text(
                widget.items[index],
                style: const TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
              ),
            ),
          );
        },
      ),
    );
  }
}

这个思路的优势在于完全自定义,能灵活调整拖动手感、吸附速度、视觉样式,完美匹配你手动拖动的需求。

内容的提问来源于stack exchange,提问作者Dave

火山引擎 最新活动