老孟導讀:今天分享一下如何實現掘金點贊效果,這不單單是一篇技術文章,仍是一篇解決問題思路的文章,遇到一個需求時,如何拆分需求,而後一步一步實現,這個過程比單純的技術(此文)更有含金量。html
先來看一下掘金點讚的效果:git
說點題外話,感謝一下二哥(沉默王二 ),給了我不少建議和幫助,公衆號搜索沉默王二便可關注。微信
遇到組合動畫效果時,首先拆分一下這個動畫,以掘金點贊效果爲例,共分爲3個動畫效果:app
拆分好了以後,就一步一步實現其效果。ide
小手縮放效果須要2個圖標,選中和未選中兩種狀態,我從阿里的圖標庫中選了2個相似的圖標(未找到一摸同樣的)。兩種狀態的圖標定義以下:佈局
///
/// 未點贊icon
///
const Icon _unLikeIcon = Icon(
IconData(0xe60a, fontFamily: 'appIconFonts'),
);
///
/// 點贊icon
///
const Icon _likeIcon = Icon(
IconData(0xe60c, fontFamily: 'appIconFonts'),
color: Color(0xFF1afa29),
);複製代碼
關於如何使用阿里的圖標庫中的圖標能夠查看此文章。動畫
因爲小手圖標的動畫效果是放大->還原,使用組合動畫實現其效果,代碼以下:ui
@override
initState() {
_animationController =
AnimationController(duration: Duration(milliseconds: 300), vsync: this);
_iconAnimation = Tween(begin: 1.0, end: 1.3).animate(_animationController);
_iconAnimation = TweenSequence([
TweenSequenceItem(
tween: Tween(begin: 1.0, end: 1.3)
.chain(CurveTween(curve: Curves.easeIn)),
weight: 50),
TweenSequenceItem(tween: Tween(begin: 1.3, end: 1.0), weight: 50),
]).animate(_animationController);
}
@override
Widget build(BuildContext context) {
return _buildLikeIcon();
}
_buildLikeIcon() {
return ScaleTransition(
scale: _iconAnimation,
child: widget.like
? IconButton(
padding: EdgeInsets.all(0),
icon: _likeIcon,
onPressed: () {
_clickIcon();
},
)
: IconButton(
padding: EdgeInsets.all(0),
icon: _unLikeIcon,
onPressed: () {
_clickIcon();
},
),
);
}複製代碼
添加按鈕點擊效果:this
_clickIcon() {
if (_iconAnimation.status == AnimationStatus.forward ||
_iconAnimation.status == AnimationStatus.reverse) {
return;
}
setState(() {
widget.like = !widget.like;
});
if (_iconAnimation.status == AnimationStatus.dismissed) {
_animationController.forward();
} else if (_iconAnimation.status == AnimationStatus.completed) {
_animationController.reverse();
}
}複製代碼
圓環的動畫效果是線條寬度逐漸變爲0,透明度逐漸變爲0,相對簡單,使用AnimatedBuilder實現:spa
_buildCircle() {
return !widget.like
? Container()
: AnimatedBuilder(
animation: _circleAnimation,
builder: (BuildContext context, Widget child) {
return Container(
decoration: BoxDecoration(
shape: BoxShape.circle,
border: Border.all(
color: Color(0xFF5FA0EC)
.withOpacity(_circleAnimation.value),
width: _circleAnimation.value * 8)),
);
},
);
}複製代碼
定義_circleAnimation:
_circleAnimation =
Tween(begin: 1.0, end: 0.0).animate(_animationController);複製代碼
最外圈的小點點動畫效果是最簡單的,透明度逐漸變爲0,但佈局相對複雜,圍繞小手造成一個圓形,使用Flow實現此佈局,Flow是一個很是酷炫的佈局組件,更多用法查看此文。
構建單個小圓點
_buildCirclePoint(double radius, Color color) {
return !widget.like
? Container()
: AnimatedBuilder(
animation: _circleAnimation,
builder: (BuildContext context, Widget child) {
return Container(
width: radius,
height: radius,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: color.withOpacity(_circleAnimation.value)),
);
},
);
}複製代碼
構建圍繞小手的多個點:
_buildCirclePoints() {
return Flow(
delegate: CirclePointFlowDelegate(),
children: <Widget>[
_buildCirclePoint(2, Color(0xFF97B1CE)),
_buildCirclePoint(5, Color(0xFF4AC6B7)),
_buildCirclePoint(2, Color(0xFF97B1CE)),
_buildCirclePoint(5, Color(0xFF4AC6B7)),
_buildCirclePoint(2, Color(0xFF97B1CE)),
_buildCirclePoint(5, Color(0xFF4AC6B7)),
_buildCirclePoint(2, Color(0xFF97B1CE)),
_buildCirclePoint(5, Color(0xFF4AC6B7)),
_buildCirclePoint(2, Color(0xFF97B1CE)),
_buildCirclePoint(5, Color(0xFF4AC6B7)),
_buildCirclePoint(2, Color(0xFF97B1CE)),
_buildCirclePoint(5, Color(0xFF4AC6B7)),
_buildCirclePoint(2, Color(0xFF97B1CE)),
_buildCirclePoint(5, Color(0xFF4AC6B7)),
_buildCirclePoint(2, Color(0xFF97B1CE)),
_buildCirclePoint(5, Color(0xFF4AC6B7)),
],
);
}複製代碼
CirclePointFlowDelegate 定義以下:
class CirclePointFlowDelegate extends FlowDelegate {
CirclePointFlowDelegate();
@override
void paintChildren(FlowPaintingContext context) {
var radius = min(context.size.width, context.size.height) / 2.0;
//中心點
double rx = radius;
double ry = radius;
for (int i = 0; i < context.childCount; i++) {
if (i % 2 == 0) {
double x =
rx + (radius - 5) * cos(i * 2 * pi / (context.childCount - 1));
double y =
ry + (radius - 5) * sin(i * 2 * pi / (context.childCount - 1));
context.paintChild(i, transform: Matrix4.translationValues(x, y, 0));
} else {
double x = rx +
(radius - 5) *
cos((i - 1) * 2 * pi / (context.childCount - 1) +
2 * pi / ((context.childCount - 1) * 3));
double y = ry +
(radius - 5) *
sin((i - 1) * 2 * pi / (context.childCount - 1) +
2 * pi / ((context.childCount - 1) * 3));
context.paintChild(i, transform: Matrix4.translationValues(x, y, 0));
}
}
}
@override
bool shouldRepaint(FlowDelegate oldDelegate) => true;
}複製代碼
老孟Flutter博客地址(近200個控件用法):laomengit.com
歡迎加入Flutter交流羣(微信:laomengit)、關注公衆號【老孟Flutter】:
![]() |
![]() |