Flutter貝塞爾曲線的有趣探索(上)

前沿

最近有看到不少貝塞爾曲線相關(guān)的文章,勾發(fā)了我對Flutter貝塞爾曲線探究的興趣,這里通過Flutter對貝塞爾曲線做幾個(gè)有趣的探索,希望對大家有所幫助。

貝塞爾曲線是一個(gè)大家比較熟悉的繪制曲線了,這里就不過多介紹。如果有不清楚原理的同學(xué),可以點(diǎn)擊這里了解。

探索

一、貝塞爾曲線實(shí)現(xiàn)自定義圖案

  1. 通過貝塞爾曲線可視化工具預(yù)先繪制圖案,這里以繪制愛心為例,我們先繪制如下形狀:
愛心繪制
  1. Flutter中繪制。Flutter的Path封裝了貝塞爾曲線的對應(yīng)api,我們只需要將上述工具繪制的坐標(biāo)點(diǎn)傳入。注意當(dāng)我們繪制完左半愛心后,右半愛心只需要根據(jù)中心點(diǎn)坐標(biāo)對稱繪制即可。
    // 繪制背景
    canvas.drawColor(const Color(0xFFF1F1F1), BlendMode.color);
    // 獲取屏幕中心點(diǎn)坐標(biāo)
    centerX = size.width * 0.5;
    // 左半愛心
    leftPath.moveTo(centerX, 400);
    leftPath.cubicTo(centerX - 207, 267, centerX - 107, 100, centerX, 194);
    // 右半愛心
    rightPath.moveTo(centerX, 400);
    rightPath.cubicTo(centerX + 207, 267, centerX + 107, 100, centerX, 194);
    // 繪制曲線
    canvas.drawPath(leftPath, _paint1);
    canvas.drawPath(rightPath, _paint1);

效果圖如下:

效果圖1

ok,我們完成了一個(gè)通過貝塞爾曲線繪制自定義圖形的探索,是不是感覺非常簡單!

二、繪制的曲線添加繪制動(dòng)畫

如果僅僅實(shí)現(xiàn)上面的實(shí)例,那太簡單了,我們做一點(diǎn)更有難度的探索。比如說在繪制的過程中添加動(dòng)畫,讓繪制過程動(dòng)起來。
剛開始有這個(gè)想法的時(shí)候,大腦毫無頭緒。因?yàn)槲覀兊膱D形是通過貝塞爾曲線畫出來的,我們沒有掌握到path軌跡繪制的細(xì)節(jié),也就無法通過動(dòng)畫來增量更新path的繪制...

那我們有沒有辦法獲取到Path的細(xì)節(jié)呢,結(jié)論是有的。經(jīng)過一系列對Flutter的Api探索(其實(shí)是百度、Google)后,Path.computeMetrics()給了我答案。我們可以通過PathMetric獲取到Path的測量位置,然后遍歷獲取到Path的所有Point,然后通過drawPoints()按照時(shí)間維度進(jìn)行繪制,最后實(shí)現(xiàn)我們想要的繪制動(dòng)畫效果。

好了,廢話不多說直接上代碼:

 // 獲取path上的點(diǎn)數(shù)據(jù)
    // 左邊
    PathMetrics leftPms = leftPath.computeMetrics();
    PathMetric leftPm = leftPms.elementAt(0);
    double leftLen = leftPm.length;

    double tmpStart = 0;
    double leftEnd = min(_fraction, leftLen);
    var isCompleted = leftEnd == leftLen;
    
    for (; tmpStart < leftEnd; tmpStart += 1) {
      Tangent? t = leftPm.getTangentForOffset(tmpStart);
      if (t != null) {
        _leftPoints.add(t.position);
      }
    }
    // 右邊
    PathMetrics rightPms = rightPath.computeMetrics();
    PathMetric rightPm = rightPms.elementAt(0);
    double rightLen = rightPm.length;
    tmpStart = 0;
    double rightEnd = min(_fraction, rightLen);
    for (; tmpStart < rightEnd; tmpStart += 1) {
      Tangent? t = rightPm.getTangentForOffset(tmpStart);
      if (t != null) {
        _rightPoints.add(t.position);
      }
    }

    // 繪制背景線
    canvas.drawPath(leftPath, _paint1);
    canvas.drawPath(rightPath, _paint1);
    // 繪制點(diǎn)
    canvas.drawPoints(PointMode.points, _leftPoints, _paint2);
    canvas.drawPoints(PointMode.points, _rightPoints, _paint2);

然后通過動(dòng)畫增量更新Points的繪制進(jìn)度

    _animation = Tween(begin: 0.0, end: len).animate(_controller)
      ..addListener(() {
        setState(() {
          _fraction = _animation.value;
        });
      });

當(dāng)動(dòng)畫執(zhí)行完成的時(shí)候,我們把愛心填充,這樣更加突出

    // 繪制愛心
    // 動(dòng)畫結(jié)束時(shí),填充愛心
    if (isCompleted) {
      _paint1.style = PaintingStyle.fill;
      _paint1.color = red;
    }

最后不要忘記了將CustomPainter的shouldRepaint設(shè)置為true,允許widget觸發(fā)build的時(shí)候重新繪制

  @override
  bool shouldRepaint(covariant CustomPainter oldDelegate) {
    return true;
  }

現(xiàn)在我們來看看最終實(shí)現(xiàn)的效果吧:

效果圖2

后續(xù)

目前,我們嘗試了:(1)通過貝塞爾曲線繪制自定義圖形;(2)為繪制的曲線添加繪制動(dòng)畫;2種曲線繪制的有趣探索,后續(xù)在《Flutter貝塞爾曲線的有趣探索(下)》文章中將嘗試更多有趣的探索:(3)通過手勢繪制自定義圖形;(4)將繪制的圖形生成個(gè)人作品并添加自定義特效;
歡迎大家點(diǎn)贊收藏持續(xù)關(guān)注我!最后貼上項(xiàng)目源碼的Github

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

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

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