中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

怎么用Android貝塞爾曲線繪制一個波浪球

發布時間:2022-05-18 09:28:56 來源:億速云 閱讀:163 作者:iii 欄目:開發技術

本篇內容介紹了“怎么用Android貝塞爾曲線繪制一個波浪球”的有關知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領大家學習一下如何處理這些情況吧!希望大家仔細閱讀,能夠學有所成!

效果如下所示:

怎么用Android貝塞爾曲線繪制一個波浪球

先來總結下 WaveLoadingWidget 的特點,這樣才能歸納出實現該效果所需要的步驟:

  • widget 的主體是一個不規則的半圓形,頂部曲線以類似于波浪的形式從左往右上下起伏運行

  • 波浪球可以自定義顏色,此處以 waveColor 命名

  • 波浪球的起伏線將嵌入的文本分為上下兩種顏色,上半部分顏色以 backgroundColor 命名,下半部分顏色以 foregroundColor 命名,文本的整體顏色一直在根據波浪的運行而動態變化中

雖然文本的整體顏色是在不斷變化的,但只要能夠繪制出其中一幀的圖形,其動態效果就能通過不斷改變波浪曲線的位置參數來實現,所以這里先把該 widget 當成靜態的,先實現其靜態效果即可

將繪制步驟拆解為以下幾步:

  • 繪制顏色為 backgroundColor 的文本,將其繪制在 canvas 的最底層

  • 根據 widget 的寬高信息構建一個不超出范圍的最大圓形路徑 circlePath

  • 以 circlePath 的水平中間線作為波浪的基準起伏線,在起伏線的上邊和下邊分別用貝塞爾曲線繪制一段連續的波浪 path,將 path 的首尾兩端以矩形的方式連接在一起,構成 wavePath,wavePath 的底部會與 circlePath 的最底部相交

  • 取 circlePath 和 wavePath 的交集 combinePath,用 waveColor 填充, 此時就得到了半圓形的球形波浪了

  • 利用 canvas.clipPath(combinePath) 方法裁切畫布,再繪制顏色為 foregroundColor 的文本,此時繪制的 foregroundColor 文本只會顯示 combinePath 范圍內的部分,也即只會顯示下半部分,使得兩次不同時間繪制的文本重疊在了一起,從而得到了有不同顏色范圍的文本

  • 利用 AnimationController 不斷改變 wavePath 的起始點的 X 坐標,同時重新刷新 UI,從而得到波浪不斷從左往右起伏運行的動態效果

現在就來一步步實現以上的繪制步驟吧

一、繪制 backgroundColor 文本

flutter 通過 CustomPainter 為開發者提供了自繪 UI 的入口,其內部的 void paint(Canvas canvas, Size size) 方法提供了畫布 canvas 對象以及包含 widget 寬高信息的 size 對象

這里就來繼承 CustomPainter 類,在 paint 方法中先來繪制顏色為 backgroundColor 的文本。flutter 的 canvas 對象沒有提供直接 drawText 的 API,所以其繪制文本的步驟相對原生的自定義 View 要稍微麻煩一點

class _WaveLoadingPainter extends CustomPainter {
  final String text;

  final double fontSize;

  final double animatedValue;

  final Color backgroundColor;

  final Color foregroundColor;

  final Color waveColor;

  _WaveLoadingPainter({
    required this.text,
    required this.fontSize,
    required this.animatedValue,
    required this.backgroundColor,
    required this.foregroundColor,
    required this.waveColor,
  });

  @override
  void paint(Canvas canvas, Size size) {
    final side = min(size.width, size.height);
    _drawText(canvas: canvas, side: side, color: backgroundColor);
  }

  void _drawText(
      {required Canvas canvas, required double side, required Color color}) {
    ParagraphBuilder paragraphBuilder = ParagraphBuilder(ParagraphStyle(
      textAlign: TextAlign.center,
      fontStyle: FontStyle.normal,
      fontSize: fontSize,
    ));
    paragraphBuilder.pushStyle(ui.TextStyle(color: color));
    paragraphBuilder.addText(text);
    ParagraphConstraints pc = ParagraphConstraints(width: fontSize);
    Paragraph paragraph = paragraphBuilder.build()..layout(pc);
    canvas.drawParagraph(
      paragraph,
      Offset((side - paragraph.width) / 2.0, (side - paragraph.height) / 2.0),
    );
  }

  @override
  bool shouldRepaint(CustomPainter oldDelegate) {
    return animatedValue != (oldDelegate as _WaveLoadingPainter).animatedValue;
  }
}

怎么用Android貝塞爾曲線繪制一個波浪球

二、構建 circlePath

取 widget 的寬度和高度的最小值作為圓的直徑大小,以此構建出一個不超出 widget 范圍的最大圓形路徑 circlePath

  @override
  void paint(Canvas canvas, Size size) {
    final side = min(size.width, size.height);
    _drawText(canvas: canvas, side: side, color: backgroundColor);

    final circlePath = Path();
    circlePath.addArc(Rect.fromLTWH(0, 0, side, side), 0, 2 * pi);
  }

三、繪制波浪線

波浪的寬度和高度就根據一個固定的比例值來求值,以 circlePath 的中間分隔線作為水平線,在水平線的上下根據貝塞爾曲線繪制出連續的波浪線

  @override
  void paint(Canvas canvas, Size size) {
    final side = min(size.width, size.height);
    _drawText(canvas: canvas, side: side, color: backgroundColor);

    final circlePath = Path();
    circlePath.addArc(Rect.fromLTWH(0, 0, side, side), 0, 2 * pi);

    final waveWidth = side * 0.8;
    final waveHeight = side / 6;
    final wavePath = Path();
    final radius = side / 2.0;
    wavePath.moveTo(-waveWidth, radius);
    for (double i = -waveWidth; i < side; i += waveWidth) {
      wavePath.relativeQuadraticBezierTo(
          waveWidth / 4, -waveHeight, waveWidth / 2, 0);
      wavePath.relativeQuadraticBezierTo(
          waveWidth / 4, waveHeight, waveWidth / 2, 0);
    }
    //為了方便讀者理解,這里把 wavePath 繪制出來,實際上不需要
    final paint = Paint()
      ..isAntiAlias = true
      ..style = PaintingStyle.fill
      ..strokeWidth = 3
      ..color = waveColor;
    canvas.drawPath(wavePath, paint);
  }

怎么用Android貝塞爾曲線繪制一個波浪球

此時繪制的曲線還處于非閉合狀態,需要將 wavePath 的首尾兩端連接起來,這樣后面才可以和 circlePath 取交集

wavePath.relativeLineTo(0, radius);
wavePath.lineTo(-waveWidth, side);
wavePath.close();
//為了方便讀者理解,這里把 wavePath 繪制出來,實際上不需要
final paint = Paint()
  ..isAntiAlias = true
  ..style = PaintingStyle.fill
  ..strokeWidth = 3
  ..color = waveColor;
canvas.drawPath(wavePath, paint);

wavePath 閉合后,此時半圓的顏色就會鋪滿了

怎么用Android貝塞爾曲線繪制一個波浪球

四、取交集

取 circlePath 和 wavePath 的交集,就得到一個半圓形波浪球了

final paint = Paint()
  ..isAntiAlias = true
  ..style = PaintingStyle.fill
  ..strokeWidth = 3
  ..color = waveColor;
final combinePath = Path.combine(PathOperation.intersect, circlePath, wavePath);
canvas.drawPath(combinePath, paint);

怎么用Android貝塞爾曲線繪制一個波浪球

五、繪制 foregroundColor 文本

文本的顏色是分為上下兩部分的,上半部分顏色為 backgroundColor,下半部分為 foregroundColor。在第一步的時候已經繪制了顏色為 backgroundColor 的文本了,foregroundColor 文本不需要顯示上半部分,所以在繪制 foregroundColor 文本之前需要先把繪制區域限定在 combinePath 內,使得兩次不同時間繪制的文本重疊在了一起,從而得到有不同顏色范圍的文本

canvas.clipPath(combinePath);
_drawText(canvas: canvas, side: side, color: foregroundColor);

怎么用Android貝塞爾曲線繪制一個波浪球

六、添加動畫

現在已經繪制好靜態時的效果了,可以考慮如何使 widget 動起來了

要實現動態效果也很簡單,只要不斷改變貝塞爾曲線的起始點坐標,使之不斷從左往右移動,就可以營造出波浪從左往右前進的效果了。_WaveLoadingPainter 根據外部傳入的動畫值 animatedValue 來設置 wavePath 的起始坐標點即可,生成 animatedValue 的邏輯和其它繪制參數均由 _WaveLoadingState 來提供

class _WaveLoadingState extends State<WaveLoading>
    with SingleTickerProviderStateMixin {
  String get _text => widget.text;

  double get _fontSize => widget.fontSize;

  Color get _backgroundColor => widget.backgroundColor;

  Color get _foregroundColor => widget.foregroundColor;

  Color get _waveColor => widget.waveColor;

  late AnimationController _controller;

  late Animation<double> _animation;

  @override
  void initState() {
    super.initState();
    _controller = AnimationController(
        duration: const Duration(milliseconds: 700), vsync: this);
    _animation = Tween(
      begin: 0.0,
      end: 1.0,
    ).animate(_controller)
      ..addListener(() {
        setState(() => {});
      });
    _controller.repeat();
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return RepaintBoundary(
      child: CustomPaint(
        painter: _WaveLoadingPainter(
          text: _text,
          fontSize: _fontSize,
          animatedValue: _animation.value,
          backgroundColor: _backgroundColor,
          foregroundColor: _foregroundColor,
          waveColor: _waveColor,
        ),
      ),
    );
  }
}

_WaveLoadingPainter 根據 animatedValue 來設置 wavePath 的起始坐標點

wavePath.moveTo((animatedValue - 1) * waveWidth, radius);

七、使用

最后將 _WaveLoadingState 包裹到 StatefulWidget 中,在 StatefulWidget 中開放可以自定義配置的參數就可以了

class WaveLoading extends StatefulWidget {
  final String text;

  final double fontSize;

  final Color backgroundColor;

  final Color foregroundColor;

  final Color waveColor;

  WaveLoading({
    Key? key,
    required this.text,
    required this.fontSize,
    required this.backgroundColor,
    required this.foregroundColor,
    required this.waveColor,
  }) : super(key: key) {
    assert(text.isNotEmpty && fontSize > 0);
  }

  @override
  State<StatefulWidget> createState() {
    return _WaveLoadingState();
  }
}

使用方式:

SizedBox(
	width: 300,
	height: 300,
	child: WaveLoading(
  		text: "開",
  		fontSize: 210,
  		backgroundColor: Colors.lightBlue,
  		foregroundColor: Colors.white,
  		waveColor: Colors.lightBlue,
)

“怎么用Android貝塞爾曲線繪制一個波浪球”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識可以關注億速云網站,小編將為大家輸出更多高質量的實用文章!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

元谋县| 本溪市| 永丰县| 合阳县| 中超| 鲜城| 嘉义县| 彩票| 临潭县| 怀仁县| 东丽区| 呼图壁县| 三都| 忻城县| 东乡县| 保亭| 咸宁市| 彰武县| 且末县| 商水县| 石景山区| 屯昌县| 高淳县| 宣汉县| 武宁县| 同心县| 宁都县| 什邡市| 察雅县| 中宁县| 柯坪县| 沙湾县| 尼玛县| 黄平县| 寻甸| 贵州省| 永州市| 大丰市| 辽宁省| 文安县| 民乐县|