Flutter 133: 圖解自定義 ACEWaterButton 水波紋按鈕

    小菜想自定義一個水波紋按鈕,即默認向外擴散的水波樣式;實現方式有很多種,小菜嘗試最基本的 AnimationController 逐層繪製來處理,小菜簡單記錄一下嘗試過程;

ACEWaterButton

    小菜畫了一個簡單的圖如下,預期的水波紋按鈕包括兩層,以中心圓(藍色)爲基礎逐步向外圍擴散至(綠色),並循環重複;

1. 內置圓

    小菜以此分爲兩步,第一步先繪製內置圓和內置圖標,小菜提供了 innerSizeinnerIcon 屬性以方便內置圓的樣式自定義;通過 ClipOval 裁切一個完整的內置圓;

    其中需要注意的是,內置圓應置於外圍圓的中心,因此小菜添加一個 outSize 屬性限制外圍圓尺寸,同時默認設置 innerSize = 48.0,若未設置 outSize,則以 innerSize * 2 爲默認值;

Container(
    width: widget.outSize ?? widget.innerSize * 2,
    height: widget.outSize ?? widget.innerSize * 2,
    child: widget.innerIcon == null
        ? Container() : Center(child: ClipOval(
                child: Container(
                    width: widget.innerSize,
                    height: widget.innerSize,
                    color: widget.color,
                    child: widget.innerIcon))))

2. 水波紋

    小菜預想實現水波紋效果則必然離不開 Animation 動畫,使用動畫方式也有多種,可以繼承 AnimatedWidget 也可以使用 AnimationController 自定義動畫樣式;

    小菜預期水波紋不僅範圍逐漸變大,並且在擴散過程中透明度逐漸降低,至外圍最大範圍爲止消失;小菜採用最基本的 CustomPainter 自定義 Canvas.drawCircle,根據時間進度來逐層繪製水波紋;

2.1 透明度

    小菜使用 Paint 繪製時根據 AnimationController.value 進度逐步設置 color.withOpacity 透明度逐漸變低;

Paint _paint = Paint()..style = PaintingStyle.fill;
_paint..color = color.withOpacity(1.0 - progress);

2.2 外圍圓

    外圍圓主要是根據 AnimationController.value 進度逐步進行半徑的更新;小菜預期的水波紋範圍只有默認的內置圓到外圍圓的範圍漸變,因此變動範圍爲 (outSize - innerSize) * 0.5 * progress,同時起始位置爲內置圓,因此初始半徑應再加上內置圓半徑;

double _radius = ((outSize ?? innerSize * 2) * 0.5 - innerSize * 0.5) * progress + innerSize * 0.5;
canvas.drawCircle(Offset(size.width * 0.5, size.height * 0.5), _radius, _paint);

    小菜在測試過程中也嘗試了其他的擴展範圍,若起始位置爲中心則無需添加內置圓半徑;若想增大或見效水波紋範圍可以自由調整 AnimationController.value 進度範圍;

// 中心點擴展
double _radius = innerSize * 0.5 * progress;
// 增大擴展範圍
double _radius = innerSize * 2 * progress;
class ACEWaterPainter extends CustomPainter {
  final double progress;
  final Color color;
  final double innerSize;
  final double outSize;

  Paint _paint = Paint()..style = PaintingStyle.fill;

  ACEWaterPainter(this.progress, this.color, this.innerSize, this.outSize);

  @override
  void paint(Canvas canvas, Size size) {
    _paint..color = color.withOpacity(1.0 - progress);

    double _radius =
        ((outSize ?? innerSize * 2) * 0.5 - innerSize * 0.5) * progress + innerSize * 0.5;

    canvas.drawCircle(Offset(size.width * 0.5, size.height * 0.5), _radius, _paint);
  }

  @override
  bool shouldRepaint(CustomPainter oldDelegate) => true;
}

3. 小反思

3.1 內置圓是否可缺省?

    小菜在通過 ACEWaterPainter 繪製水波紋過程中,起始位置從內置圓開始,那是否可以省略第一步的內置圓呢?

    暫時先不缺省,因爲小菜在設置水波紋擴散過程中,同時設置了透明度的漸變,若缺省內置圓會影響 innerIcon 的展示效果;但內置圓繪製位置可以調整,也可以在 ACEWaterPainter 中進行繪製;

3.2 shouldRepaint 是否需要一直重繪?

    ACEWaterPainter 中是否需要一直重繪;在使用自定義 Paint 委託類創建新的 CustomPaint 對象時若新實例與舊實例不同,則應返回 true,否則應返回 false;因此在水波紋過程中,小菜默認設置爲 true 進行重繪;


    ACEWaterButton 案例源碼


    小菜對 ACEWaterButton 水波紋按鈕的簡單效果已滿足,但還不夠完善,對於重繪的機制還需要優化;如有錯誤,請多多指導!

來源: 阿策小和尚

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章