1.贝塞尔曲线
class Path extends NativeFieldWrapperClass2 {
/// Create a new empty [Path] object.
@pragma('vm:entry-point')
Path() { _constructor(); }
void _constructor() native 'Path_constructor';
/// Creates a copy of another [Path].
///
/// This copy is fast and does not require additional memory unless either
/// the `source` path or the path returned by this constructor are modified.
factory Path.from(Path source) {
return source._clone();
}
Path _clone() native 'Path_clone';
/// Determines how the interior of this path is calculated.
///
/// Defaults to the non-zero winding rule, [PathFillType.nonZero].
PathFillType get fillType => PathFillType.values[_getFillType()];
set fillType(PathFillType value) => _setFillType(value.index);
int _getFillType() native 'Path_getFillType';
void _setFillType(int fillType) native 'Path_setFillType';
/// Starts a new sub-path at the given coordinate.
void moveTo(double x, double y) native 'Path_moveTo';
/// Starts a new sub-path at the given offset from the current point.
void relativeMoveTo(double dx, double dy) native 'Path_relativeMoveTo';
/// Adds a straight line segment from the current point to the given
/// point.
void lineTo(double x, double y) native 'Path_lineTo';
/// Adds a straight line segment from the current point to the point
/// at the given offset from the current point.
void relativeLineTo(double dx, double dy) native 'Path_relativeLineTo';
/// Adds a quadratic bezier segment that curves from the current
/// point to the given point (x2,y2), using the control point
/// (x1,y1).
void quadraticBezierTo(double x1, double y1, double x2, double y2) native 'Path_quadraticBezierTo';
/// Adds a quadratic bezier segment that curves from the current
/// point to the point at the offset (x2,y2) from the current point,
/// using the control point at the offset (x1,y1) from the current
/// point.
void relativeQuadraticBezierTo(double x1, double y1, double x2, double y2) native 'Path_relativeQuadraticBezierTo';
/// Adds a cubic bezier segment that curves from the current point
/// to the given point (x3,y3), using the control points (x1,y1) and
/// (x2,y2).
void cubicTo(double x1, double y1, double x2, double y2, double x3, double y3) native 'Path_cubicTo';
/// Adds a cubic bezier segment that curves from the current point
/// to the point at the offset (x3,y3) from the current point, using
/// the control points at the offsets (x1,y1) and (x2,y2) from the
/// current point.
void relativeCubicTo(double x1, double y1, double x2, double y2, double x3, double y3) native 'Path_relativeCubicTo';
/// Adds a bezier segment that curves from the current point to the
/// given point (x2,y2), using the control points (x1,y1) and the
/// weight w. If the weight is greater than 1, then the curve is a
/// hyperbola; if the weight equals 1, it's a parabola; and if it is
/// less than 1, it is an ellipse.
void conicTo(double x1, double y1, double x2, double y2, double w) native 'Path_conicTo';
/// Adds a bezier segment that curves from the current point to the
/// point at the offset (x2,y2) from the current point, using the
/// control point at the offset (x1,y1) from the current point and
/// the weight w. If the weight is greater than 1, then the curve is
/// a hyperbola; if the weight equals 1, it's a parabola; and if it
/// is less than 1, it is an ellipse.
void relativeConicTo(double x1, double y1, double x2, double y2, double w) native 'Path_relativeConicTo';
/// If the `forceMoveTo` argument is false, adds a straight line
/// segment and an arc segment.
///
/// If the `forceMoveTo` argument is true, starts a new sub-path
/// consisting of an arc segment.
///
/// In either case, the arc segment consists of the arc that follows
/// the edge of the oval bounded by the given rectangle, from
/// startAngle radians around the oval up to startAngle + sweepAngle
/// radians around the oval, with zero radians being the point on
/// the right hand side of the oval that crosses the horizontal line
/// that intersects the center of the rectangle and with positive
/// angles going clockwise around the oval.
///
/// The line segment added if `forceMoveTo` is false starts at the
/// current point and ends at the start of the arc.
void arcTo(Rect rect, double startAngle, double sweepAngle, bool forceMoveTo) {
assert(_rectIsValid(rect));
_arcTo(rect.left, rect.top, rect.right, rect.bottom, startAngle, sweepAngle, forceMoveTo);
}
void _arcTo(double left, double top, double right, double bottom,
double startAngle, double sweepAngle, bool forceMoveTo) native 'Path_arcTo';
/// Appends up to four conic curves weighted to describe an oval of `radius`
/// and rotated by `rotation`.
///
/// The first curve begins from the last point in the path and the last ends
/// at `arcEnd`. The curves follow a path in a direction determined by
/// `clockwise` and `largeArc` in such a way that the sweep angle
/// is always less than 360 degrees.
///
/// A simple line is appended if either either radii are zero or the last
/// point in the path is `arcEnd`. The radii are scaled to fit the last path
/// point if both are greater than zero but too small to describe an arc.
///
void arcToPoint(Offset arcEnd, {
Radius radius = Radius.zero,
double rotation = 0.0,
bool largeArc = false,
bool clockwise = true,
}) {
assert(_offsetIsValid(arcEnd));
assert(_radiusIsValid(radius));
_arcToPoint(arcEnd.dx, arcEnd.dy, radius.x, radius.y, rotation,
largeArc, clockwise);
}
void _arcToPoint(double arcEndX, double arcEndY, double radiusX,
double radiusY, double rotation, bool largeArc,
bool clockwise) native 'Path_arcToPoint';
/// Appends up to four conic curves weighted to describe an oval of `radius`
/// and rotated by `rotation`.
///
/// The last path point is described by (px, py).
///
/// The first curve begins from the last point in the path and the last ends
/// at `arcEndDelta.dx + px` and `arcEndDelta.dy + py`. The curves follow a
/// path in a direction determined by `clockwise` and `largeArc`
/// in such a way that the sweep angle is always less than 360 degrees.
///
/// A simple line is appended if either either radii are zero, or, both
/// `arcEndDelta.dx` and `arcEndDelta.dy` are zero. The radii are scaled to
/// fit the last path point if both are greater than zero but too small to
/// describe an arc.
void relativeArcToPoint(Offset arcEndDelta, {
Radius radius = Radius.zero,
double rotation = 0.0,
bool largeArc = false,
bool clockwise = true,
}) {
assert(_offsetIsValid(arcEndDelta));
assert(_radiusIsValid(radius));
_relativeArcToPoint(arcEndDelta.dx, arcEndDelta.dy, radius.x, radius.y,
rotation, largeArc, clockwise);
}
void _relativeArcToPoint(double arcEndX, double arcEndY, double radiusX,
double radiusY, double rotation,
bool largeArc, bool clockwise)
native 'Path_relativeArcToPoint';
/// Adds a new sub-path that consists of four lines that outline the
/// given rectangle.
void addRect(Rect rect) {
assert(_rectIsValid(rect));
_addRect(rect.left, rect.top, rect.right, rect.bottom);
}
void _addRect(double left, double top, double right, double bottom) native 'Path_addRect';
/// Adds a new sub-path that consists of a curve that forms the
/// ellipse that fills the given rectangle.
///
/// To add a circle, pass an appropriate rectangle as `oval`. [Rect.fromCircle]
/// can be used to easily describe the circle's center [Offset] and radius.
void addOval(Rect oval) {
assert(_rectIsValid(oval));
_addOval(oval.left, oval.top, oval.right, oval.bottom);
}
void _addOval(double left, double top, double right, double bottom) native 'Path_addOval';
/// Adds a new sub-path with one arc segment that consists of the arc
/// that follows the edge of the oval bounded by the given
/// rectangle, from startAngle radians around the oval up to
/// startAngle + sweepAngle radians around the oval, with zero
/// radians being the point on the right hand side of the oval that
/// crosses the horizontal line that intersects the center of the
/// rectangle and with positive angles going clockwise around the
/// oval.
void addArc(Rect oval, double startAngle, double sweepAngle) {
assert(_rectIsValid(oval));
_addArc(oval.left, oval.top, oval.right, oval.bottom, startAngle, sweepAngle);
}
void _addArc(double left, double top, double right, double bottom,
double startAngle, double sweepAngle) native 'Path_addArc';
/// Adds a new sub-path with a sequence of line segments that connect the given
/// points.
///
/// If `close` is true, a final line segment will be added that connects the
/// last point to the first point.
///
/// The `points` argument is interpreted as offsets from the origin.
void addPolygon(List points, bool close) {
assert(points != null);
_addPolygon(_encodePointList(points), close);
}
void _addPolygon(Float32List points, bool close) native 'Path_addPolygon';
/// Adds a new sub-path that consists of the straight lines and
/// curves needed to form the rounded rectangle described by the
/// argument.
void addRRect(RRect rrect) {
assert(_rrectIsValid(rrect));
_addRRect(rrect._value32);
}
void _addRRect(Float32List rrect) native 'Path_addRRect';
/// Adds a new sub-path that consists of the given `path` offset by the given
/// `offset`.
///
/// If `matrix4` is specified, the path will be transformed by this matrix
/// after the matrix is translated by the given offset. The matrix is a 4x4
/// matrix stored in column major order.
void addPath(Path path, Offset offset, {Float64List matrix4}) {
assert(path != null); // path is checked on the engine side
assert(_offsetIsValid(offset));
if (matrix4 != null) {
assert(_matrix4IsValid(matrix4));
_addPathWithMatrix(path, offset.dx, offset.dy, matrix4);
} else {
_addPath(path, offset.dx, offset.dy);
}
}
void _addPath(Path path, double dx, double dy) native 'Path_addPath';
void _addPathWithMatrix(Path path, double dx, double dy, Float64List matrix) native 'Path_addPathWithMatrix';
/// Adds the given path to this path by extending the current segment of this
/// path with the the first segment of the given path.
///
/// If `matrix4` is specified, the path will be transformed by this matrix
/// after the matrix is translated by the given `offset`. The matrix is a 4x4
/// matrix stored in column major order.
void extendWithPath(Path path, Offset offset, {Float64List matrix4}) {
assert(path != null); // path is checked on the engine side
assert(_offsetIsValid(offset));
if (matrix4 != null) {
assert(_matrix4IsValid(matrix4));
_extendWithPathAndMatrix(path, offset.dx, offset.dy, matrix4);
} else {
_extendWithPath(path, offset.dx, offset.dy);
}
}
void _extendWithPath(Path path, double dx, double dy) native 'Path_extendWithPath';
void _extendWithPathAndMatrix(Path path, double dx, double dy, Float64List matrix) native 'Path_extendWithPathAndMatrix';
/// Closes the last sub-path, as if a straight line had been drawn
/// from the current point to the first point of the sub-path.
void close() native 'Path_close';
/// Clears the [Path] object of all sub-paths, returning it to the
/// same state it had when it was created. The _current point_ is
/// reset to the origin.
void reset() native 'Path_reset';
/// Tests to see if the given point is within the path. (That is, whether the
/// point would be in the visible portion of the path if the path was used
/// with [Canvas.clipPath].)
///
/// The `point` argument is interpreted as an offset from the origin.
///
/// Returns true if the point is in the path, and false otherwise.
bool contains(Offset point) {
assert(_offsetIsValid(point));
return _contains(point.dx, point.dy);
}
bool _contains(double x, double y) native 'Path_contains';
/// Returns a copy of the path with all the segments of every
/// sub-path translated by the given offset.
Path shift(Offset offset) {
assert(_offsetIsValid(offset));
return _shift(offset.dx, offset.dy);
}
Path _shift(double dx, double dy) native 'Path_shift';
/// Returns a copy of the path with all the segments of every
/// sub-path transformed by the given matrix.
Path transform(Float64List matrix4) {
assert(_matrix4IsValid(matrix4));
return _transform(matrix4);
}
Path _transform(Float64List matrix4) native 'Path_transform';
/// Computes the bounding rectangle for this path.
///
/// A path containing only axis-aligned points on the same straight line will
/// have no area, and therefore `Rect.isEmpty` will return true for such a
/// path. Consider checking `rect.width + rect.height > 0.0` instead, or
/// using the [computeMetrics] API to check the path length.
///
/// For many more elaborate paths, the bounds may be inaccurate. For example,
/// when a path contains a circle, the points used to compute the bounds are
/// the circle's implied control points, which form a square around the circle;
/// if the circle has a transformation applied using [transform] then that
/// square is rotated, and the (axis-aligned, non-rotated) bounding box
/// therefore ends up grossly overestimating the actual area covered by the
/// circle.
// see https://skia.org/user/api/SkPath_Reference#SkPath_getBounds
Rect getBounds() {
final Float32List rect = _getBounds();
return Rect.fromLTRB(rect[0], rect[1], rect[2], rect[3]);
}
Float32List _getBounds() native 'Path_getBounds';
/// Combines the two paths according to the manner specified by the given
/// `operation`.
///
/// The resulting path will be constructed from non-overlapping contours. The
/// curve order is reduced where possible so that cubics may be turned into
/// quadratics, and quadratics maybe turned into lines.
static Path combine(PathOperation operation, Path path1, Path path2) {
assert(path1 != null);
assert(path2 != null);
final Path path = Path();
if (path._op(path1, path2, operation.index)) {
return path;
}
throw StateError('Path.combine() failed. This may be due an invalid path; in particular, check for NaN values.');
}
bool _op(Path path1, Path path2, int operation) native 'Path_op';
/// Creates a [PathMetrics] object for this path.
///
/// If `forceClosed` is set to true, the contours of the path will be measured
/// as if they had been closed, even if they were not explicitly closed.
PathMetrics computeMetrics({bool forceClosed = false}) {
return PathMetrics._(this, forceClosed);
}
}
2.绘制贝塞尔曲线
1.要绘制贝塞尔线,我们需要四个点:起点,终点和两个控制点,如下图所示。移动控制点会改变曲线的斜率。您可以在此在线工具中使用控制点。
我们可以使用类Path的cubicTo方法绘制贝塞尔曲线:
void cubicTo(double x1, double y1, double x2, double y2, double x3, double y3)
使用控制点(x1,y1)和(x2,y2)添加从当前点到给定点(x3,y3)的曲线的三次贝塞尔曲线段。
如您所见,该cubicTo方法接受三个参数。其中两个是控制点,最后一个参数是终点。起点是您的笔已经位于画布上的位置。
不要忘记在画布坐标中,左上角是(0,0)点,右下角是(size.width,size.height)。因此,请尝试相应地调整四点:
//自绘接口
void paint(Canvas canvas, Size size) {
var paint = Paint();
paint.color = Colors.lightBlue;
paint.style = PaintingStyle.stroke;
paint.strokeWidth = 3;
var startPoint = Offset(0, size.height / 2);
var controlPoint1 = Offset(size.width / 4, size.height / 3);
var controlPoint2 = Offset(3 * size.width / 4, size.height / 3);
var endPoint = Offset(size.width, size.height / 2);
var path = Path();
path.moveTo(startPoint.dx, startPoint.dy);
path.cubicTo(controlPoint1.dx, controlPoint1.dy,
controlPoint2.dx, controlPoint2.dy,
endPoint.dx, endPoint.dy);
canvas.drawPath(path, paint);
}
请记住,paint对象就像我们的笔,我们将其颜色设置为蓝色,宽度设置为3。
我们用path对象描述了bezier路径。该moveTo方法已用于将笔移动到路径的起点。然后我们调用cubicTo方法来定义控制点和终点。之后,我们使用该drawPath方法绘制了路径。
贝塞尔曲线参考:
https://www.jianshu.com/p/90be43fc7bee
///推荐一些曲线图/折线图/柱状图参考:
https://blog.csdn.net/sxt_zls/article/details/89419092
https://www.codercto.com/a/45868.html
https://zhuanlan.zhihu.com/p/48207046
https://blog.csdn.net/qq_17766199/article/details/95632571?utm_medium=distribute.pc_aggpage_search_result.none-task-blog-2allfirst_rank_v2~rank_v25-2-95632571.nonecase&utm_term=flutter%20%E5%8A%A8%E6%80%81%E7%BB%98%E5%88%B6%E6%9B%B2%E7%BA%BF