老孟導讀:此文講解3個酷炫的3D動畫效果。git
下面是要實現的效果:微信
Flutter 中3D效果是經過 Transform 組件實現的,沒有變換效果的實現:app
class TransformDemo extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('3D 變換Demo'), ), body: Container( alignment: Alignment.center, color: Colors.white, child: Text('3D 變換Demo'), ), ); } }
經過 GestureDetector 組件添加滑動事件監聽:less
@override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('3D 變換Demo'), ), body: GestureDetector( onPanUpdate: (details) { print('$details'); }, child: Container( alignment: Alignment.center, color: Colors.white, child: Text('3D 變換Demo'), ), ), ); }
添加 Transform 對組件進入旋轉:ide
@override Widget build(BuildContext context) { return Transform( transform: Matrix4.identity() ..setEntry(3, 2, 0.001) ..rotateX(pi/6) ..rotateY(pi/6), alignment: Alignment.center, child: Scaffold( appBar: AppBar( title: Text('3D 變換Demo'), ), body: GestureDetector( onPanUpdate: (details) { }, child: Container( alignment: Alignment.center, color: Colors.white, child: Text('3D 變換Demo'), ), ), )); }
將滑動的偏移和旋轉進行關聯:動畫
class TransformDemo extends StatefulWidget { @override _TransformDemoState createState() => _TransformDemoState(); } class _TransformDemoState extends State<TransformDemo> { double _rotateX = .0; double _rotateY = .0; @override Widget build(BuildContext context) { return Transform( transform: Matrix4.identity() ..rotateX(_rotateX) ..rotateY(_rotateY), alignment: Alignment.center, child: Scaffold( appBar: AppBar( title: Text('3D 變換Demo'), ), body: GestureDetector( onPanUpdate: (details) { setState(() { _rotateX += details.delta.dy * .01; _rotateY += details.delta.dx * -.01; }); }, child: Container( alignment: Alignment.center, color: Colors.white, child: Text('3D 變換Demo'), ), ), )); } }
基本已經實現了3D效果,但效果比較生硬,尤爲垂直方向旋轉的時候遠點和近點在屏幕上的寬度是同樣,ui
添加近大遠小的效果:this
Transform( transform: Matrix4.identity() ..setEntry(3, 2, 0.001) ..rotateX(_rotateX) ..rotateY(_rotateY), ...
上面的效果相似於翻書的效果。3d
實現的原理:code
將圖片左右切割爲兩部分,兩張圖片共分割爲4個新的組件,以下圖,分別爲一、二、三、4
代碼實現:
_child1 = ClipRect( child: Align( alignment: Alignment.centerLeft, widthFactor: 0.5, child: child1, ), ); _child2 = ClipRect( child: Align( alignment: Alignment.centerRight, widthFactor: 0.5, child: child1, ), ); _child3 = ClipRect( child: Align( alignment: Alignment.centerLeft, widthFactor: 0.5, child: child2, ), ); _child4 = ClipRect( child: Align( alignment: Alignment.centerRight, widthFactor: 0.5, child: child2, ), );
將第一張圖片放在第二種圖片的上面,先旋轉 組件2 從 0度到 90度,而後再旋轉 組件3 從 -90度到0度,代碼實現:
Row( mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ Stack( children: [ _child1, Transform( alignment: Alignment.centerRight, transform: Matrix4.identity() ..setEntry(3, 2, 0.001) ..rotateY(_animation1.value), child: _child3, ), ], ), Container( width: 3, color: Colors.white, ), Stack( children: [ _child4, Transform( alignment: Alignment.centerLeft, transform: Matrix4.identity() ..setEntry(3, 2, 0.001) ..rotateY(_animation.value), child: _child2, ) ], ) ], )
動畫控制器設置:
@override void initState() { init(); _controller = AnimationController(vsync: this, duration: Duration(seconds: 5)) ..addListener(() { setState(() {}); }); _animation = Tween(begin: .0, end: pi / 2) .animate(CurvedAnimation(parent: _controller, curve: Interval(.0, .5))); _animation1 = Tween(begin: -pi / 2, end: 0.0).animate( CurvedAnimation(parent: _controller, curve: Interval(.5, 1.0))); _controller.forward(); super.initState(); }
其中 child1, child2爲兩種圖片,代碼以下:
_FlipUpDemoState( Container( width: 300, height: 400, child: Image.asset( 'assets/images/b.jpg', fit: BoxFit.cover, ), ), Container( width: 300, height: 400, child: Image.asset( 'assets/images/c.jpeg', fit: BoxFit.cover, ), ))
最後生成的效果就是開始的翻書效果。
上面是左右翻頁效果,同理換成上下翻頁效果:
@override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(), body: Column( mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ Stack( children: [ _upperChild1, Transform( alignment: Alignment.bottomCenter, transform: Matrix4.identity() ..setEntry(3, 2, 0.003) ..rotateX(_animation1.value), child: _upperChild2, ), ], ), SizedBox( height: 2, ), Stack( children: [ _lowerChild2, Transform( alignment: Alignment.topCenter, transform: Matrix4.identity() ..setEntry(3, 2, 0.003) ..rotateX(_animation.value), child: _lowerChild1, ) ], ) ], ), ); }
老孟Flutter博客地址(330個控件用法):http://laomengit.com
歡迎加入Flutter交流羣(微信:laomengit)、關注公衆號【老孟Flutter】:
![]() |
![]() |