You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用Flutter与Dart实现示例中的鼠标悬停3D动画?现有代码效果不符

修复Flutter鼠标悬停3D旋转动画问题

问题分析

原代码核心问题是用details.delta(鼠标移动增量)累积旋转角度,导致鼠标进入区域时的动画逻辑和示例不符。示例效果是根据鼠标在Widget内的相对位置,让Widget围绕中心做对应角度的3D旋转,而非依赖鼠标移动的增量累积。

解决方案

以下是修改后的代码,可实现和示例一致的效果:

import 'package:flutter/material.dart';

class Hover3DWidget extends StatefulWidget {
  const Hover3DWidget({Key? key}) : super(key: key);

  @override
  State<Hover3DWidget> createState() => _Hover3DWidgetState();
}

class _Hover3DWidgetState extends State<Hover3DWidget> with TickerProviderStateMixin {
  // 记录鼠标在Widget内的相对位置(0-1范围)
  Offset _mousePosition = Offset(0.5, 0.5);
  // 动画控制器,用于平滑过渡旋转角度
  late AnimationController _controller;
  // 动画值,控制旋转的平滑变化
  late Animation<double> _animation;

  @override
  void initState() {
    super.initState();
    _controller = AnimationController(
      duration: const Duration(milliseconds: 200),
      vsync: this,
    );
    _animation = CurvedAnimation(
      parent: _controller,
      curve: Curves.easeOut,
    );
    _controller.forward();
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return MouseRegion(
      cursor: SystemMouseCursors.cell,
      // 获取鼠标在Widget内的绝对位置,转换为相对比例
      onHover: (details) {
        final RenderBox box = context.findRenderObject() as RenderBox;
        final Offset localPosition = box.globalToLocal(details.position);
        setState(() {
          _mousePosition = Offset(
            localPosition.dx / box.size.width,
            localPosition.dy / box.size.height,
          );
        });
        _controller.forward(from: 0);
      },
      // 鼠标退出时重置位置
      onExit: (event) {
        setState(() {
          _mousePosition = Offset(0.5, 0.5);
        });
        _controller.forward(from: 0);
      },
      child: AnimatedBuilder(
        animation: _animation,
        builder: (context, child) {
          // 计算旋转角度:基于中心偏移,最大旋转角度±0.1弧度(约5.7度)
          final rotateX = (_mousePosition.dy - 0.5) * -0.2;
          final rotateY = (_mousePosition.dx - 0.5) * 0.2;
          return Transform(
            transform: Matrix4.identity()
              ..setEntry(3, 2, 0.001) // 透视效果
              ..rotateX(rotateX * _animation.value)
              ..rotateY(rotateY * _animation.value),
            alignment: FractionalOffset.center,
            child: child,
          );
        },
        child: Container(
          width: 100,
          height: 100,
          // 可选:添加阴影增强3D效果
          decoration: BoxDecoration(
            color: Colors.red,
            boxShadow: [
              BoxShadow(
                color: Colors.black.withOpacity(0.3),
                blurRadius: 10,
                offset: Offset(
                  (_mousePosition.dx - 0.5) * 5,
                  (_mousePosition.dy - 0.5) * 5,
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

关键改进点

  • 改用鼠标相对位置计算旋转:通过globalToLocal获取鼠标在Widget内的绝对位置,转换为0-1的相对比例,基于中心偏移计算旋转角度,和示例逻辑一致。
  • 添加平滑动画过渡:使用AnimationController和AnimatedBuilder实现旋转角度的平滑变化,避免生硬跳变。
  • 增强3D透视效果:通过setEntry(3, 2, 0.001)设置透视参数,让旋转更有立体感;可选添加跟随鼠标偏移的阴影,进一步贴近示例效果。
  • 退出复位逻辑:鼠标退出时将位置重置为中心,平滑过渡到初始状态。

内容的提问来源于stack exchange,提问作者نعمان منذر محمود الجميلي

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.05 01:50:21