2019.05.30更新:
法海大佬在個人基礎上對點贊控件作了優化和封裝,API調用更加方便了。你們能夠看我這篇文章瞭解下繪製思路,實際使用推薦使用法海大佬的,用起來更方便:Flutter 仿掘金推特色贊按鈕git
此次依然是補做業,以前在寫仿「探探」左滑/右滑的效果的時候,設計稿底部的喜歡Icon實際上是有相似於Twitter點贊那種的動效的,可是由於時間緣由我偷懶沒寫。
慣例先上效果圖:github
GitHub地址:github.com/yumi0629/Fl…算法
總體算法是參照了GitHub上star最多的jd-alexander大佬寫的LikeButton,我進行了調整,並最終用Flutter實現。
一點小缺陷:如今的實現方式,icon大小無法自適應,須要初始化佈局的時候手動傳入一個size。canvas
咱們將動畫放慢,很明顯總體動畫由三部分組成:中間Icon的放大、底部圓環的交替和外部煙花散開的效果: bash
由於是層疊佈局,咱們依舊是使用Stack來實現,底部圓環的交替和外部煙花散開的效果是使用的CustomPaint
來繪製,而中間的小圖標則是使用的普通Widget實現:ide
Stack(
alignment: Alignment.center,
children: <Widget>[
CustomPaint(
size: Size(widget.width, widget.width),
painter: DotPainter(),
),
CustomPaint(
isComplex: true,
size: Size(widget.width * 0.35, widget.width * 0.35),
painter: CirclePainter(),
Container(
width: widget.width,
height: widget.width,
alignment: Alignment.center,
child: Transform.scale(
scale: isLiked ? scale.value : 1.0,
child: GestureDetector(
child: Icon(),
onTap: _onTap,
),
),
),
],
);
複製代碼
Paint的繪製在這裏就很少說了,由於基本都是數學問題。總體效果的實現主要是要學會用一個Controller來控制多個動畫。佈局
這一期主要是想跟你們講講如何用一個Controller來控制多個動畫同時進行,也就是Flutter中的Staggered Animation(交錯動畫)。
咱們能夠定義不少個Animation,將他們和同一個controller綁定:post
Animation<double> outerCircle = new Tween<double>(
begin: 0.1,
end: 1.0,
).animate(
new CurvedAnimation(
parent: _controller,
curve: new Interval(
0.0,
0.3,
curve: Curves.ease,
),
),
);
Animation<double> innerCircle = new Tween<double>(
begin: 0.2,
end: 1.0,
).animate(
new CurvedAnimation(
parent: _controller,
curve: new Interval(
0.2,
0.5,
curve: Curves.ease,
),
),
);Animation<double>
複製代碼
上面的例子中,outerCircle
和innerCircle
共享同一個_controller
,而各自的播放順序經過Interval
來控制:outerCircle
的動畫時間爲總體進度的0.0~0.3,innerCircle
的動畫時間爲總體進度的0.2~0.5。對於單個動畫的進度,咱們能夠經過outerCircle.value
和innerCircle.value
來獲取,單個動畫的進度範圍依然是0.0~1.0,因此繪製每一組動畫時,不須要去手動轉換。優化
此次碰到的主要問題是paint
的blendMode
屬性有坑,由於底部兩個圓環在繪製時的思路是:大圓環繪製到一半時,開始繪製小圓環將其遮蓋住,可是小圓環的顏色咱們無法設定,由於咱們不知道畫布是什麼顏色的,所以無法用相同的顏色去遮蓋住大圓環。這個問題能夠經過設置paint
的blendMode
屬性爲BlendMode.clear
解決,看名字就很好理解,就是清除以前的繪製區域,可是這個BlendMode.clear
有bug。
若是你直接像下面這樣寫,那麼你會發現,clear掉的部分會變成黑色:動畫
circlePaint..style = PaintingStyle.fill;
maskPaint..blendMode = BlendMode.clear;
@override
void paint(Canvas canvas, Size size) {
canvas.drawCircle(Offset(center, center), 20.0, circlePaint);
canvas.drawCircle(Offset(center, center), 10.0, maskPaint);
}
複製代碼
canvas.saveLayer(Offset.zero & size, Paint());
canvas.drawCircle(Offset(center, center), 20.0, circlePaint);
canvas.drawCircle(Offset(center, center), 10.0, maskPaint);
canvas.restore();
複製代碼