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

????小菜想自定義一個(gè)水波紋按鈕,即默認(rèn)向外擴(kuò)散的水波樣式;實(shí)現(xiàn)方式有很多種,小菜嘗試最基本的 AnimationController 逐層繪制來處理,小菜簡單記錄一下嘗試過程;

ACEWaterButton

????小菜畫了一個(gè)簡單的圖如下,預(yù)期的水波紋按鈕包括兩層,以中心圓(藍(lán)色)為基礎(chǔ)逐步向外圍擴(kuò)散至(綠色),并循環(huán)重復(fù);

1. 內(nèi)置圓

????小菜以此分為兩步,第一步先繪制內(nèi)置圓和內(nèi)置圖標(biāo),小菜提供了 innerSizeinnerIcon 屬性以方便內(nèi)置圓的樣式自定義;通過 ClipOval 裁切一個(gè)完整的內(nèi)置圓;

????其中需要注意的是,內(nèi)置圓應(yīng)置于外圍圓的中心,因此小菜添加一個(gè) outSize 屬性限制外圍圓尺寸,同時(shí)默認(rèn)設(shè)置 innerSize = 48.0,若未設(shè)置 outSize,則以 innerSize * 2 為默認(rèn)值;

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. 水波紋

????小菜預(yù)想實(shí)現(xiàn)水波紋效果則必然離不開 Animation 動(dòng)畫,使用動(dòng)畫方式也有多種,可以繼承 AnimatedWidget 也可以使用 AnimationController 自定義動(dòng)畫樣式;

????小菜預(yù)期水波紋不僅范圍逐漸變大,并且在擴(kuò)散過程中透明度逐漸降低,至外圍最大范圍為止消失;小菜采用最基本的 CustomPainter 自定義 Canvas.drawCircle,根據(jù)時(shí)間進(jìn)度來逐層繪制水波紋;

2.1 透明度

????小菜使用 Paint 繪制時(shí)根據(jù) AnimationController.value 進(jìn)度逐步設(shè)置 color.withOpacity 透明度逐漸變低;

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

2.2 外圍圓

????外圍圓主要是根據(jù) AnimationController.value 進(jìn)度逐步進(jìn)行半徑的更新;小菜預(yù)期的水波紋范圍只有默認(rèn)的內(nèi)置圓到外圍圓的范圍漸變,因此變動(dòng)范圍為 (outSize - innerSize) * 0.5 * progress,同時(shí)起始位置為內(nèi)置圓,因此初始半徑應(yīng)再加上內(nèi)置圓半徑;

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);

????小菜在測試過程中也嘗試了其他的擴(kuò)展范圍,若起始位置為中心則無需添加內(nèi)置圓半徑;若想增大或見效水波紋范圍可以自由調(diào)整 AnimationController.value 進(jìn)度范圍;

// 中心點(diǎn)擴(kuò)展
double _radius = innerSize * 0.5 * progress;
// 增大擴(kuò)展范圍
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 內(nèi)置圓是否可缺?。?/h4>

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

????暫時(shí)先不缺省,因?yàn)樾〔嗽谠O(shè)置水波紋擴(kuò)散過程中,同時(shí)設(shè)置了透明度的漸變,若缺省內(nèi)置圓會(huì)影響 innerIcon 的展示效果;但內(nèi)置圓繪制位置可以調(diào)整,也可以在 ACEWaterPainter 中進(jìn)行繪制;

3.2 shouldRepaint 是否需要一直重繪?

????ACEWaterPainter 中是否需要一直重繪;在使用自定義 Paint 委托類創(chuàng)建新的 CustomPaint 對象時(shí)若新實(shí)例與舊實(shí)例不同,則應(yīng)返回 true,否則應(yīng)返回 false;因此在水波紋過程中,小菜默認(rèn)設(shè)置為 true 進(jìn)行重繪;


????ACEWaterButton 案例源碼


????小菜對 ACEWaterButton 水波紋按鈕的簡單效果已滿足,但還不夠完善,對于重繪的機(jī)制還需要優(yōu)化;如有錯(cuò)誤,請多多指導(dǎo)!

來源: 阿策小和尚

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容