I. Understand the API of each end

In this lesson, we’ll give you a preliminary preview of the apis on each side. The whole to preview is still necessary, just dabble in this part of the preparation and awareness. Later we will gradually study and discuss and practice cases to learn, so there is no need to worry too much and too miscellaneous.

  • The canvasCanvasMethods provided
  • The brushPaintProvided properties
  • The pathPathProvided operations

1, the Android side

  • CanvasNot only as a carrier in drawing, it’s very important that we canCooperate withSome canvas rotations, panning, zooming, and so on make extremely complex drawings easy. Of course, the canvas state and so onsaveandswitchIt also gives canvas a professional imagePhotoshoptoolThe layerWe’ll talk about it in the drawing section, and we’ll see it in multiple cases.
/** Dot-line related API**/Draw a point at the beginning of the canvas coordinate (x,y). Fill it with paintpublic void drawPoint(float x, float y, @NonNullPaint Paint) draws a series of points on the canvas based on the PTS coordinate arraypublic void drawPoints(@Size(multiple = 2) float[] pts, int offset, int count,@NonNullPaint Paint) draws lines based on starting and ending pointspublic void drawLine(float startX, float startY, float stopX, float stopY,@NonNullPaint Paint) Draws a fixed-point array.. That's long enough. I'll do it laterpublic void drawVertices(@NonNull VertexMode mode, int vertexCount, @NonNull float[] verts,int vertOffset, @Nullable float[] texs, int texOffset, @Nullable int[] colors,int colorOffset, @Nullable short[] indices, int indexOffset, int indexCount,@NonNull Paint paint)


/** Radians class API**/(cx,cy) is the coordinate and radius is the radius to draw a circle on the canvas. The brush serves as a fill or strokepublic void drawCircle(float cx, float cy, float radius, @NonNullPaint Paint) draws the specified oval using the specified brush color. The ellipse will be filled or framed according to the pattern in the paintingpublic void drawOval(float left, float top, float right, float bottom, @NonNullPaint Paint) draws the specified ellipse in the RectF rectangle region using the specified brush color. The ellipse will be filled or framed according to the pattern in the paintingpublic void drawOval(@NonNull RectF oval, @NonNullPaint Paint) passes the sector between the starting Angle and (starting Angle + scanned Angle) in the formed rectangle areapublic void drawArc(float left, float top, float right, float bottom, float startAngle,float sweepAngle, boolean useCenter, @NonNull Paint paint)
public void drawArc(@NonNull RectF oval, float startAngle, float sweepAngle, boolean useCenter,@NonNull Paint paint)


/** Draw rectangle **/Draw the rectangle, and the brush sets the fill or strokepublic void drawRect(@NonNull RectF rect, @NonNullPaint Paint) draws a rectangle, and the brush sets the fill or strokepublic void drawRect(@NonNull Rect rect, @NonNullDraw the rounded rectangle with rx and ry as the horizontal and vertical radius of the rounded corner respectively. Set the brush to fill or strokepublic void drawRoundRect(@NonNull RectF rect, float rx, float ry, @NonNull Paint paint)
public void drawRoundRect(float left, float top, float right, float bottom, float rx, float ry,@NonNullDraw a double rounded rectangle using the specified drawing, rounded for horizontal and vertical fixed radiipublic void drawDoubleRoundRect(@NonNull RectF outer, float outerRx, float outerRy, @NonNull RectF inner, float innerRx, float innerRy, @NonNullThe radius radians that vary in size for each Angle will be explained laterpublic void drawDoubleRoundRect(@NonNull RectF outer, @NonNull float[] outerRadii,@NonNull RectF inner.@NonNull float[] innerRadii, @NonNull Paint paint)


/** Draw a bitmap **/Draws the specified bitmap with the specified drawing, its upper left corner at (x, y), and transforms through the current matrixpublic void drawBitmap(@NonNull Bitmap bitmap, float left, float top, @Nullable Paint paint) 
public void drawBitmap(@NonNull Bitmap bitmap, float left, float top, @Nullable Paint paint)
public void drawBitmap(@NonNull Bitmap bitmap, @Nullable Rect src, @NonNull RectF dst,@Nullable Paint paint)
public void drawBitmap(@NonNull Bitmap bitmap, @Nullable Rect src, @NonNull Rect dst,@Nullable Paint paint) 
public void drawBitmap(@NonNull int[] colors, int offset, int stride, int x, int y,int width, int height, boolean hasAlpha, @Nullable Paint paint)
public void drawBitmap(@NonNull Bitmap bitmap, @NonNull Matrix matrix, @Nullable Paint paint)
public void drawBitmapMesh(@NonNull Bitmap bitmap, int meshWidth, int meshHeight,@NonNull float[] verts, int vertOffset, @Nullable int[] colors, int colorOffset,@Nullable Paint paint)

/** Clipping class related API**/Clipping the area inside the path.public boolean clipPath(@NonNullPath Path) sets clipping to the difference currently clipped to the incoming Pathpublic boolean clipOutPath(@NonNullPath Path) Clipping rectanglepublic boolean canvas.clipRect(@NonNull Rect rect)
public boolean clipRect(float left, float top, float right, float bottom,@NonNull Region.Op op)
publicBoolean clipRect(float left, float top, float right, float bottom) clipped to the intersection of the current clipped rectanglepublic boolean clipOutRect(@NonNullRect Rect) clipping the specified areapublic boolean clipRegion(@NonNull Region region, @NonNullRegion.Op Op) the current clipping intersects the specified Regionpublic boolean clipRegion(@NonNull Region region)

/** State related API**/Save the current matrix canvas to the stack for later use. Save () saves the current state into a copy to form a grouppublic int saveLayer(@Nullable RectF bounds, @Nullable Paint paint, @SaveflagsInt saveFlags) this call balances the previous call to save () and is used to delete all changes to the matrix/clip state since the last save call.public void restore()

/** Sets canvas transform **/Set canvas translationpublicVoid translate(float dx, float dy) The canvas scalespublicVoid Scale (float sx, float SY) Canvas rotationpublicRotate (float degrees) Rotate (float degrees) rotate(float degrees) rotate(float degrees) rotate(float degrees) rotate(float degrees) rotate(float degrees) rotate(float degrees) rotate(float degrees) rotate(float degrees) CGAffineTransform) -> some View3dTransforms and the API's render output to rotate the view in three dimensions around a given rotation axis. func rotation3DEffect(_ angle: Angle, axis: (x: CGFloat, y: CGFloat, z: CGFloat), anchor: UnitPoint = .center, anchorZ: CGFloat =0, perspective: CGFloat = 1) -> some View
Copy the code
  • PathClass encapsulates the baseStraight line segments.radian,The second order curve,Third order curve isometric path. Not only can you use canvas. DrawPath (path,paint)Paint on canvasAnd can be done according to the paint StylefillorstrokeEven more powerful is the ability to match the canvas with pathstailoringCrop out extremely rich View effects.
Void moveTo(float x, float y) Public void moveTo(float x, float y) sets the last point relative to the previous contour as the starting point of the next contour in the relative position. Same as moveTo () if there is no previous outline. Public void rMoveTo(float dx, float dy) same as lineTo, but treats coordinates as the last point relative to this contour. If there's no previous point, MoveTo (0,0) public void rLineTo(float dx, float dy) public void offset(float dx, float dy) Public void setLastPoint(float dx, Public void transform(@nonnull Matrix Matrix) /** Add a shape to the current path **/ public void addCircle(float x, Public void addArc(@nonnull RectF oval, float startAngle, float y, float radius, @nonnull directiondir) Public void addRoundRect(@nonnull RectF rect, float rx, float ry, Public void addOval(@nonnull RectF oval, @nonnull Direction dir) /** Add a curve correlation **/ Add a quadratic Bezier curve from the last point, approaching the control point (x1, y1) and ending at (x2, y2). If moveTo () is not called for this contour, the first point is automatically set to (0,0) public void quadTo(float x1, float y1, float x2, float y2) add a cubic bezier curve from the last point, Approximates control points * (x1, y1) and (x2, y2) and ends at (x3, y3). If the contour has not been moveTo () called, the first point is automatically set to (0,0). public void cubicTo(float x1, float y1, float x2, float y2,float x3, float y3) public void rCubicTo(float x1, float y1, Float x2, float y2,float x3, float y3) appends the specified arc to the path as a new profile. If the starting point of the path is different from the current last point of the path, an automatic lineTo () is added to connect the current profile to the starting point of the arc. However, if the path is empty, Public void arcTo(@nonnull RectF oval, float startAngle, Float sweepAngle, Boolean forceMoveTo) Automatically closes the path contour if the last point does not match the first point. Public void close()Copy the code

1, the IOS side

  • CanvasThe Android and Flutter side allows developers to freely play with Flutter and makes complex drawing easier. As Android and Flutter developers, we have benefited greatly. SWiftUI currently doesn’t offer a powerful canvas class like Canvas. Of course, we have also listed most of the alternatives, which I think can be used in SwiftUIZStackInstead of comparingCGRectSuitable for.
SwiftUI:func path(in rect: CGRect) -> PathRect is provided as the artboard carrier/** whether to include in the canvas **/Whether the point is detected inside the canvas areafunc contains(_ point: CGPoint) -> BoolReturns whether the canvas contains a second rectangle.
func contains(_ rect2: CGRect) -> BoolReturns whether two rectangles are the same size and positionfunc equalTo(_ rect2: CGRect) -> Bool


/** Split rectangle canvas **/Create two rectangles by splitting the original one.
func divided(atDistance: CGFloat.from fromEdge: CGRectEdge) -> (slice: CGRect, remainder: CGRectReturns a rectangle smaller or larger than the source rectangle with the same center pointfunc insetBy(dx: CGFloat.dy: CGFloat) -> CGRectGets the intersection of two rectanglesfunc intersection(_ r2: CGRect) -> CGRectReturns the union of two rectanglesfunc union(_ r2: CGRect) -> CGRectRect alone may lead to apparent passivity in our operations.SwiftUI we can use ZStack as Canvas for operation, so many can be quickly located and bring convenient operation.

/ * * * * / ZStackDraw circles to limit size, etcCircle(a)Circle works with path to limit size,offset locates the canvas [ZStack], and stroke sets the brush size,pen,ForegroundColor add color and so on are also relatively providedMost of the features that Paint has.
Circle().path(in:CGRect(x:1,y:1,width:55,height:55)).offset(x: 50.0, y: 100).stroke(style: StrokeStyle(lineWidth: 2, lineCap: CGLineCap.butt, lineJoin: CGLineJoin.round, miterLimit:11, dash: [1.1.3.4.5.6], dashPhase: 12).foregroundcolor (.blue) Draw rectangle of courseThe Path can be.
Rectangle(a)Views have cropping methods, so all views have cropping
func clipped(antialiased: Bool = false) -> some ViewClips this view to its bounding rectangle frame.
@inlinable public func clipShape<S> (_ shape: S.style: FillStyle = FillStyle()) -> some View where S : Shape

/** transform related API**/Rotate the render output of the view around the specified point.
func rotationEffect(_ angle: Angle.anchor: UnitPoint = .center) -> some ViewThe render output of this view is scaled with respect to anchor points for the given vertical and horizontal sizesfunc scaleEffect(_ scale: CGSize.anchor: UnitPoint = .center) -> some ViewTransformations are applied to the rendering of this view, manipulating the view through various transformationsfunc transformEffect(_ transform: CGAffineTransform) -> some ViewRotate the render output of the view in three dimensions around the given rotation axis.
func rotation3DEffect(_ angle: Angle.axis: (x: CGFloat, y: CGFloat, z: CGFloat), anchor: UnitPoint = .center, anchorZ: CGFloat = 0.perspective: CGFloat = 1) -> some View
Copy the code
  • PathAndroid and the Flutter side provide extremely rich apis that are easy for you to master with beautiful UI drawing, of courseIOSIt also provides basic path-related functions. Next we move to SwiftUI, where Path is also used for many graphics and images on IOSdraw,tailoringFoundation, we can do a lot of things.
Public mutating func move(to p: CGPoint) public mutating func addLine(to p: CGPoint) CGPoint) adds a series of connected straight segments to the path. Public mutating func addLines(_ lines: [CGPoint]) appends a copy of the given path to this path. public mutating func addPath(_ path: Path, transform: CGAffineTransform =. Identity)/Bezier * * * * / to add to the path with a specified end point of the quadratic Bezier curve public mutating func addQuadCurve (to p: CGPoint, controlCP: CGPoint) Adds a cubic Bezier curve to a path with specified endpoints and a public mutating func addCurve(to P: CGPoint, control1 cp1: CGPoint, control2 cp2: CGPoint) /** Adds a path to an existing path **/ Adds a rectangular subpath to a path. Public mutating func addRect(_ rect: CGRect, transform: CGAffineTransform =.identity) Adds a rounded rectangle to a path. public mutating func addRoundedRect(in rect: CGRect, cornerSize: CGSize, style: RoundedCornerStyle =. Circular, transform: CGAffineTransform =. Identity) Adds an ellipse to the path. Public mutating func addEllipse(in Rect: CGRect, transform: CGAffineTransform =.identity) adds a series of rectangular subpaths to a path. Public mutating func addRects(_ rects: [CGRect], transform: CGAffineTransform =.identity) adds the arc of the circle to the path, specifying the radius and Angle difference. public mutating func addRelativeArc(center: CGPoint, radius: CGFloat, startAngle: Angle, delta: Angle, transform: CGAffineTransform =.identity) adds the arc of the circle to the path, specified by radius and Angle. public mutating func addArc(center: CGPoint, radius: CGFloat, startAngle: Angle, endAngle: Angle, clockwise: Bool, transform: CGAffineTransform =.identity) adds the arc of the circle to the path, specifying the radius and the radius tangent. public mutating func addArc(tangent1End p1: CGPoint, tangent2End p2: CGPoint, radius: CGFloat, transform: CGAffineTransform =.identity) /** Other related operations **/ returns the last point in the path. public var currentPoint: CGPoint? Public func applying(_ transform: CGAffineTransform) -> Path Returns the Path constructed by the transform public func offsetBy(dx: CGFloat, dy: CGFloat) -> Path closes and completes the current subpath. public mutating func closeSubpath()Copy the code

3, Flutter

  • CanvasinFlutterI believe a lot of people are verylikeAnd, of course,The android nativeWrite about the developers of FlutterThe most comfortableNow,almostThe same API, a lot of complex path operations, we can save the canvas state to simply solve the implementation. The API of Flutter is as follows:
DrawPoints (PointMode PointMode, List<Offset> points, Void drawRawPoints(PointMode PointMode, Float32List points, Void drawLine(Offset p1, Offset p2, Paint Paint) > use the given Paint to draw a line between the given points. Void drawVertices(Vertices Vertices, BlendMode, BlendMode, Vertices) Void drawCircle(Offset C, double radius, Paint Paint) > Draw a circle centered on the point given by the first parameter. The second parameter is the radius and the third parameter [Paint] is the parameter. Whether a circle is solid or hollow is controlled by paint.style. Void drawOval(Rect Rect, Paint Paint) '> Draws an axially aligned ellipse and fills the given axially aligned rectangle with the given [Paint]. Is the ellipse filled or stroke set in paint to draw a scalloped arc 'drawArc(Rect Rect, Double startAngle, Double sweepAngle, bool useCenter, Scale to the arc that fits inside the given rectangle, set the start Angle to (Start Angle + scan Angle), and set the brush to draw. There's no hurry. Void drawRect(Rect Rect, Paint) '> draw a rectangle using the given [Paint]. The rectangle is filled or stroke controlled by [Paint. Style]. The rounded rectangle 'canvas.drawrrect (rrect, Paint)' > draws a rounded rectangle using the given [Paint]. Void drawDRRect(RRect outer, RRect inner,) void drawDRRect(RRect outer, RRect inner,) Paint) '> Draws a shape consisting of the difference between two rounded rectangles using the given [Paint]. Whether the shape is filled or stroke (or both) is controlled by [paint.style]. DrawImage (Image Image, Offset Offset, Paint Paint) > drawImage(Image Image, Offset, Paint Paint) 'void drawImageRect(Image Image, Rect SRC, Rect DST, Paint Paint)' > Draw the Image of the SRC rectangle of the future Image area onto the DST area of the canvas.  Void drawImageNine(Image Image, Rect Center, Rect DST, Paint Paint) '> draw the Image to the specified rectangle area. Transforms (Image atlas, List<RSTransform> Transforms, List<Rect> rects, List<Color>? colors, BlendMode? blendMode, Rect? CullRect, Paint Paint) '> Atlas drawn to canvas, transformable for each area, etc. More on that later in the tutorial. Transforms (Image Atlas, Float32List rstTransforms, Float32List Rects, Int32List? colors, BlendMode? blendMode, Rect? CullRect, Paint Paint) '> Atlas drawn to canvas, transformable for each area, etc. More on that later in the tutorial. Void clipRect(Rect Rect, {ClipOp = clipop. intersect, Bool doAntiAlias = true}) '> Shrinks the fragment region to the intersection of the current fragment and the given rectangle. If [doAntiAlias] is true, the clip will be de-aliased. If more than one draw command intersects the clip boundary, it may result in incorrect mixing at the clip boundary. Void clipRRect(RRect RRect, {bool doAntiAlias = true}) '> shrinks the segment area to the intersection of the current segment and the given rounded rectangle. If [doAntiAlias] is true, the clip will be de-aliased. If more than one drawing command intersects the clip boundary, it may result in incorrect mixing at the clip boundary. Clipping 'void clipPath(Path Path, {bool doAntiAlias = true})' reduces the clip region to the intersection of the current clip and the given [Path]. If [doAntiAlias] is true, the clip will be de-aliased. If multiple draw commands intersect clipping boundaries, It may cause multiple draw commands to intersect the clipping boundary with the canvas state and transform related apis to save the state 'save()' > save the current state of the canvas on the stack 🀄️ reset 'restore()' > state to the state of the canvas last saved on the stack save the current state into a copy to form a group 'saveLayer(Rect Bounds, Paint Paint)' > stores a copy of the current transform and clip on the save stack, then creates a new group, Subsequent calls will become part of this group to get the number of times the saved state 'getSaveCount()' /** Canvas fills with background Color etc. **/ fills with background Color 'drawColor(Color Color, BlendMode BlendMode) '> Fill the background color with Paint' canvas. DrawPath (Path Path, Paint Paint) '> Fill the background color with Paint instead of drawColor. 'canvas. DrawShadow (Path Path, Color Color, double elevation, bool transparentOccluder)' > Rotate 'void rotate(double radians)' > Rotate 'void rotate(double radians)' > rotate 'void rotate(double radians)' > rotate 'void rotate(double radians)' > rotate 'void rotate(double radians)' > rotate 'void rotate(double radians)' > Scale (double sx, [double sy]) '> void translate(double dx, double dy)' > Sava and restore can bring a lot of convenience in many cases. Matrix transform 'void Transform (double radians)' > Matrix transform canvas. Detailed cases will followCopy the code