用Flutter實現一個仿Twitter的點贊效果

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來控制多個動畫。佈局

動畫控制 Staggered Animation

  這一期主要是想跟你們講講如何用一個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> 

複製代碼

  上面的例子中,outerCircleinnerCircle共享同一個_controller,而各自的播放順序經過Interval來控制:outerCircle的動畫時間爲總體進度的0.0~0.3,innerCircle的動畫時間爲總體進度的0.2~0.5。對於單個動畫的進度,咱們能夠經過outerCircle.valueinnerCircle.value來獲取,單個動畫的進度範圍依然是0.0~1.0,因此繪製每一組動畫時,不須要去手動轉換。優化

繪製中的一些坑

  此次碰到的主要問題是paintblendMode屬性有坑,由於底部兩個圓環在繪製時的思路是:大圓環繪製到一半時,開始繪製小圓環將其遮蓋住,可是小圓環的顏色咱們無法設定,由於咱們不知道畫布是什麼顏色的,所以無法用相同的顏色去遮蓋住大圓環。這個問題能夠經過設置paintblendMode屬性爲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);
  }
複製代碼

  解決方法就是在繪製前保存一下當前layer:

canvas.saveLayer(Offset.zero & size, Paint());
canvas.drawCircle(Offset(center, center), 20.0, circlePaint);
canvas.drawCircle(Offset(center, center), 10.0, maskPaint);
canvas.restore();
複製代碼
相關文章
相關標籤/搜索