如何用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,提问作者نعمان منذر محمود الجميلي
相关产品推荐
相关产品推荐

