老孟導讀:今天分享一下如何實現掘金點贊效果,這不單單是一篇技術文章,仍是一篇解決問題思路的文章,遇到一個需求時,如何拆分需求,而後一步一步實現,這個過程比單純的技術(此文)更有含金量。
先來看一下掘金點讚的效果:html
說點題外話,感謝一下二哥(沉默王二 ),給了我不少建議和幫助,公衆號搜索沉默王二便可關注。git
遇到組合動畫效果時,首先拆分一下這個動畫,以掘金點贊效果爲例,共分爲3個動畫效果:微信
拆分好了以後,就一步一步實現其效果。app
小手縮放效果須要2個圖標,選中和未選中兩種狀態,我從阿里的圖標庫中選了2個相似的圖標(未找到一摸同樣的)。兩種狀態的圖標定義以下:ide
/// /// 未點贊icon /// const Icon _unLikeIcon = Icon( IconData(0xe60a, fontFamily: 'appIconFonts'), ); /// /// 點贊icon /// const Icon _likeIcon = Icon( IconData(0xe60c, fontFamily: 'appIconFonts'), color: Color(0xFF1afa29), );
關於如何使用阿里的圖標庫中的圖標能夠查看此文章。佈局
因爲小手圖標的動畫效果是放大->還原,使用組合動畫實現其效果,代碼以下:動畫
@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(); }, ), ); }
添加按鈕點擊效果:ui
_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實現:this
_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:spa
_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個控件用法):http://laomengit.com
歡迎加入Flutter交流羣(微信:laomengit)、關注公衆號【老孟Flutter】:
![]() |
![]() |