2022年10月20日 星期四

Flutter學習-Path 路徑

 https://iter01.com/503194.html

https://www.programminghunter.com/article/66202389391/

https://juejin.cn/post/7066707431971094542

https://blog.csdn.net/wangzhongshun/article/details/96110665


Path是flutter的路徑元件,可以透過這個來儲存要繪製的圖案。

Path有很多繪製路徑的方法,如

(一)、移動到某位置 moveTo

moveTo(x,y) 默認的點為(0,0),使用此方法會移動到(x,y)的位置上。

Path path=Path();

..moveTo(100,100);  移動到x軸100,y軸100的位置

(二)、畫線到某位置 lineTo

lineTo(x,y) 由移動的上一點,畫到下一點(x,y)的位置。

Path path=Path();

..moveTo(100,100);  移動到x軸100,y軸100的位置

..lineTo(100,100);  由上面x軸100,y軸100的位置,至x軸150,y軸150的路徑


(三)、畫貝茲曲線 quadraticBezierTo(從當前位置出發,以)

quadTo(float x1, float y1, float x2, float y2)

quadraticBezierTo(控制點x, 控制點y, 終點x,終點y);

(四)、畫三階貝兹曲線
 cubicTocubicTo(控制點1x, 控制點1y,控制點2x, 控制點2y, 終點x,終點y);

(五)、畫曲線 conicTo

(六)繪製矩形 addRect
addRect(Rect.fromLTWH(矩形左邊距離,矩形上邊矩離,矩形寬度,矩形高度));

(七)、畫弧線  arcTo
透過矩形繪製弧線
arcTo(Rect.fromLTWH(矩形左邊距離,矩形上邊矩離,矩形寬度,矩形高度),開始弧度, 繪製的弧形大小, bool forceMoveTo);

bool forceMoveTo 屬性為真或假,true的話會從最後一個位置連接一條線到那個弧線,false不會連接會重新開始一個新的弧線

(八) 繪製橢圓 addOval
透過矩形繪製橢圓

addOval(Rect.fromLTWH(矩形左邊距離,矩形上邊矩離,矩形寬度,矩形高度));

(八之一)繪製圓形
addOval(Rect.fromCircle( center: 圓心位置, radius:半徑長, ));
圓心位置需給Offset(x,y)

(九)繪製弧線 addArc
addArc(Rect.fromLTWH(矩形左邊距離,矩形上邊矩離,矩形寬度,矩形高度), 開始弧度, 繪製的弧形大小);

(十)繪製多邊形 addPolygon
addPolygon([ 第一點位置, 第二點位置, 第三點位置 ], false);

位置的寫法用Offset(x偏移量,y偏移量);
最一個參數決定是否閉合,false為假,true為真。

(十一)繪製有圓角的矩形 addRRect

addRRect(Rect.fromLTWH(矩形左邊距離,矩形上邊矩離,矩形寬度,矩形高度),圓角的弧度);

圓角的弧度: Radius.circular(16)







在使用Canvas畫布時,都需給予畫筆的屬性,Paint的設定方法如下:

Paint paint = Paint()

..color = Colors.blue //繪圖中使用的畫筆顏色

..strokeWidth = 3.0; //畫筆的寬度

..style = PaintingStyle.fill //繪畫風格,默認為填充 nono為不填充

..strokeCap = StrokeCap.round //畫筆筆觸類型

..isAntiAlias = true //是否啟動抗鋸齒 false會有鋸齒

..blendMode = BlendMode.exclusion //顏色混合的方法

..colorFilter = ColorFilter.mode(Colors.blueAccent, BlendMode.exclusion) //顏色渲染模式

..maskFilter = MaskFilter.blur(BlurStyle.inner, 4.0) //模糊遮罩效果



..filterQuality = FilterQuality.high //顏色渲染模式時的品質



使用path.computeMetrics() 獲取到 PathMetrics 這個對象的列表,因為繪製的路徑可能有很多組,而透過.computeMetrics() 這個方法,可以將這些路徑存在一個列表中,可依順序讀取出來。

(1)length 為取出列表的長度(由幾個路徑圖形組成)

(2)取出path中的其中一個路徑

若是只有一個路徑可以透過single取出。

若是有很多路徑,要取出第一個可使用.first,要取出最後一個可使用.last。

若要取得其它特定第幾個的路徑,可用.elementAt(index)。

若要取得其它部份,因為.computeMetrics()的列表為iterate,因此要使用path.computeMetrics().toList()先轉成List,再用List的方法取出裡面的路徑。

如:path.computeMetrics().toList()[2]

  (3)取出其中一個路徑後,可用.extractPath,節取這個路徑由某一個點到另一個點的路徑。如下:

extractPath(double start, double end, {bool startWithMoveTo = true})


(4).getTangentForOffset()

當用上面的path.computeMetrics().single或是其它屬性,取出某一個路徑後,可用.getTangentForOffset()來取出這個路徑某一個切點,再利用.position()。可以獲得這個切點的座標。


(1)繪製圓角方形


class ButtonPaint2 extends CustomPainter {
final Animation<double> animation;
final double buttonWidth;
final double buttonHeight;

Paint p = Paint()
..style = PaintingStyle.stroke
..color = Colors.red
..strokeWidth = 5;

ButtonPaint2({
required this.animation,
required this.buttonWidth,
required this.buttonHeight,
}) : super(repaint: animation);

@override
void paint(Canvas canvas, Size size) {
final Path path = new Path()
..moveTo(buttonWidth / 2, 0)
..relativeLineTo(buttonWidth / 2 - 16, 0)
..relativeArcToPoint(Offset(16, 16),radius: Radius.circular(16))
..relativeLineTo(0,buttonHeight-32)
..relativeArcToPoint(Offset(-16, 16),radius: Radius.circular(16))
..relativeLineTo(16-buttonWidth,0)
..relativeArcToPoint(Offset(-16, -16),radius: Radius.circular(16))
..relativeLineTo(0,32-buttonHeight)
..relativeArcToPoint(Offset(16, -16),radius: Radius.circular(16))
..close();

canvas.drawPath(path, p);
}

@override
bool shouldRepaint(covariant CustomPainter oldDelegate) {
// TODO: implement shouldRepaint
throw UnimplementedError();
}
}


PathMetric 是一個對 Path 進行測量並且能夠提取 Path資訊 的類。


(2)透過pathMetric與動畫結合,製作緣路徑跑動的圓點

import 'package:flutter/material.dart';
import 'dart:ui';

void main() {
runApp(const MyApp());
}

class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);

// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}

class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key, required this.title}) : super(key: key);

final String title;

@override
State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage>
with SingleTickerProviderStateMixin {
late AnimationController _controller;

@override
void initState() {
super.initState();
_controller =
AnimationController(duration: Duration(seconds: 2), vsync: this);
// TODO
}

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
// Here we take the value from the MyHomePage object that was created by
// the App.build method, and use it to set our appbar title.
title: Text(widget.title),
),
body: AnimatedBuilder(
animation: _controller,
builder: (BuildContext context, Widget? child) {
return CustomPaint(
foregroundPainter: custompainter(_controller.value),
);

},
),
floatingActionButton: FloatingActionButton(
child: Icon(Icons.add),
onPressed: () {
_controller.reset();
_controller.forward();
},
),

);
}
}

class custompainter extends CustomPainter {
var tweenTime;
custompainter(this.tweenTime);

@override
void paint(Canvas canvas, Size size) {
Paint paint = Paint()
..style = PaintingStyle.stroke
..color = Colors.blue
..strokeWidth = 4;

var path = Path();
path.moveTo(50, 500);
path.cubicTo(50, 200, 300, 400, 350, 150);

canvas.drawPath(path, paint);

PathMetrics pathMetrics = path.computeMetrics();
PathMetric pathMetric3 = path.computeMetrics().elementAt(0);
//透過getTangentForOffset取得path路徑上的資料
Tangent? location=pathMetric3.getTangentForOffset(pathMetric3.length*tweenTime);
if (location != null) {
print(location.position);
canvas.drawCircle(location.position, 5, paint);
}


paint.color = Colors.red;
for (PathMetric pathMetric in pathMetrics) {

Path extractPath = pathMetric.extractPath(
0.0,
pathMetric.length * tweenTime,
);
canvas.drawPath(extractPath, paint);
}

@override
bool shouldRepaint(oldDelegate) {
return oldDelegate != oldDelegate;
}
}

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

沒有留言:

張貼留言