1.gif
其實就實現(xiàn)這么一個旋轉(zhuǎn)動畫,一開始想到的是用AnimatedContainer的transform屬性來實現(xiàn)這個動畫,結(jié)果發(fā)現(xiàn)不行,三角形的旋轉(zhuǎn)是以左上角為中心的。
效果就不演示了,隨便試試就知道了
看下為什么以中心旋轉(zhuǎn),有什么辦法使中心旋轉(zhuǎn)
- transform 通過矩陣變換實現(xiàn),矩陣的變換是以canvas的(0,0)點作為起始點的,所以需要圖像中心變換的話,需要canvas先移動到(-width*0.5,-height*0.5),將圖像中心點移動到(0,0)位置,進行變換,然后再移動回來
顯然我們在AnimatedContainer里獲取不到canvas,所以這方案實現(xiàn)不了 - 分析源碼
@override
Widget build(BuildContext context) {
return Container(
child: widget.child,
alignment: _alignment?.evaluate(animation),
padding: _padding?.evaluate(animation),
decoration: _decoration?.evaluate(animation),
foregroundDecoration: _foregroundDecoration?.evaluate(animation),
constraints: _constraints?.evaluate(animation),
margin: _margin?.evaluate(animation),
transform: _transform?.evaluate(animation),
);
}
在AnimatedContainer的build中找到了Container,顯然是通過Animation改變Container的屬性,來實現(xiàn)動畫效果的
繼續(xù)追蹤Container的transform
@override
Widget build(BuildContext context) {
...
if (transform != null)
current = Transform(transform: transform, child: current);
return current;
}
build中找到了使用了Transform
const Transform({
Key key,
@required this.transform,
this.origin,
this.alignment,
this.transformHitTests = true,
Widget child,
}) : assert(transform != null),
super(key: key, child: child);
Transform中有一個alignment,這個可以移動到中心點,但是Container未設(shè)置,所以默認使用左上角為起始點
既然AnimatedContainer實現(xiàn)不了,那就自定義一個
我們使用Transform控件,使用其Transform.rotate構(gòu)造函數(shù)
Transform.rotate({
Key key,
@required double angle,
this.origin,
this.alignment = Alignment.center, //已經(jīng)設(shè)置為中心了
this.transformHitTests = true,
Widget child,
})
但是這個沒動畫效果,所以我們需要加上動畫
import 'dart:math';
import 'package:flutter/material.dart';
/*
* 中心處旋轉(zhuǎn)動畫,AnimatedContainer針對的是左上角的旋轉(zhuǎn),注意角度傳遞的是π
* */
class RotateContainer extends StatefulWidget {
final double endAngle; // 注意這個角度,π為180°
final bool rotated;
final Widget child;
@override
_RotateContainerState createState() => _RotateContainerState();
RotateContainer({this.endAngle, this.child, this.rotated = false});
}
class _RotateContainerState extends State<RotateContainer>
with SingleTickerProviderStateMixin {
AnimationController _controller;
Animation<double> _animation;
double angle = 0;
@override
void initState() {
_controller =
AnimationController(vsync: this, duration: Duration(milliseconds: duration));
_animation =
Tween(begin: 0.0, end: widget.endAngle).animate(_controller)
..addListener(() {
setState(() {
angle = _animation.value;
});
});
super.initState();
}
@override
void didUpdateWidget(RotateContainer oldWidget) {
if (oldWidget.rotated == widget.rotated) return; //防止多余刷新
if (!widget.rotated) {
_controller.reverse();
} else {
_controller.forward();
}
super.didUpdateWidget(oldWidget);
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Transform.rotate(
angle: angle,
child: widget.child,
);
}
}
附上其他代碼:
const int duration = 400;
class TagsButton extends StatelessWidget {
final String text;
final bool rotated;
final VoidCallback onPressed;
TagsButton({this.text, this.onPressed, this.rotated = false});
@override
Widget build(BuildContext context) {
return InkWell(
child: Container(
padding: EdgeInsets.all(10.0),
child: Row(
children: <Widget>[
Text(text),
RotateContainer(
endAngle: pi,
rotated: rotated,
child: CustomPaint( //自己畫個三角形,當然也可以使用Icon
painter: TrianglePainter(Colors.grey),
child: SizedBox(
width: 12,
height: 12,
),
),
),
],
),
),
onTap: onPressed,
);
}
}
/*
* 畫個三角形
* */
class TrianglePainter extends CustomPainter {
Color color;
Paint _paint;
Path _path;
double angle;
TrianglePainter(this.color) {
_paint = Paint()
..strokeWidth = 1.0
..color = color
..isAntiAlias = true;
_path = Path();
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) => false;
@override
void paint(Canvas canvas, Size size) {
final baseX = size.width * 0.5;
final baseY = size.height * 0.5;
//三角形
_path.moveTo(baseX - 0.86 * baseX, 0.5 * baseY);
_path.lineTo(baseX, 1.5 * baseY);
_path.lineTo(baseX + 0.86 * baseX, 0.5 * baseY);
canvas.drawPath(_path, _paint);
}
}
使用
bool tagsMenuOpen = false;
@override
Widget build(BuildContext context) {
return Center(
child: Container(
width: 80,
child: TagsButton(
text: "篩選",
rotated: tagsMenuOpen,
onPressed: () {
tagsMenuOpen = !tagsMenuOpen;
setState(() {});
},
),
),
);
}