Read the original article:Implementing a Protractor Function with Canvas
Problem Description
How can the Canvas component be used to implement a protractor function?
Background Knowledge
- Canvas provides a canvas component for custom drawing of graphics. Developers use the CanvasRenderingContext2D object and OffscreenCanvasRenderingContext2D object to draw on the Canvas component, supporting the rendering of shapes, text, images, and complex animations. The CanvasRenderingContext2D.arc method can draw arcs.
- onTouch is a touch event; a finger touch action triggers this callback function.
Solution
Use the Canvas component to customize the drawing of a ring chart with graduations. The specific implementation steps are as follows
Drawing the protractor face:In the draw() method, clear the canvas. Use the arc() method to draw a semicircle with a radius of 120, forming the protractor face.
Drawing the scale lines: Loop from 90 degrees to 270 degrees, incrementing by 5 degrees each time. Use the moveTo() and lineTo() methods to draw each scale line, indenting 3 units inward from the radius 120 point.
Draw the pointer: Use the arc() method to draw a sector starting from 180 degrees (true left), extending to the angle specified by the angle variable. Complete the pointer using the stroke() and fill() methods.
Handle touch events: Within the onTouch event, calculate the coordinates of the touch point relative to the center point. Use Math.atan() to compute the angle, adjusting the range as needed to fit between 0 and 180 degrees. Update the angle variable and call the draw() method to redraw the protractor, displaying the current angle.
Display the current angle: Use the Text component to show the current angle value, formatted to two decimal places.
Example code is as follows:
@Entry
@ComponentV2
struct GaugeScale {
@Local angle: number = 0
private settings: RenderingContextSettings = new RenderingContextSettings(true)
private context: CanvasRenderingContext2D = new CanvasRenderingContext2D(this.settings)
private radius: number = 120;
private centerX: number = 0;
private centerY: number = 0;
draw() {
this.context.clearRect(0, 0, this.context.width, this.context.height)
// Drawing Panel
this.context.beginPath();
this.context.arc(this.centerX, this.centerY, this.radius, Math.PI, Math.PI * 2);
this.context.lineWidth = 1;
this.context.strokeStyle = 'black';
this.context.stroke(); // Drawing scales
for (let i = 90; i <= 270; i += 5) {
this.context.beginPath()
this.context.lineWidth = 1
this.context.strokeStyle = 'red'
this.context.moveTo(this.centerX + this.radius * Math.sin(i / 180 * Math.PI),
this.centerY + this.radius * Math.cos(i / 180 * Math.PI))
this.context.lineTo(this.centerX + (this.radius - 3) * Math.sin(i / 180 * Math.PI),
this.centerY + (this.radius - 3) * Math.cos(i / 180 * Math.PI))
this.context.stroke()
} // Drawing the Pointer
this.context.beginPath()
this.context.strokeStyle = 'green'
this.context.fillStyle = '#8000ff00'
this.context.moveTo(this.centerX, this.centerY)
this.context.lineTo(0, this.centerY)
this.context.arc(this.centerX, this.centerY, this.radius, Math.PI, Math.PI * (1 + this.angle / 180))
this.context.lineTo(this.centerX, this.centerY)
this.context.stroke()
this.context.fill()
}
build() {
Column() {
Canvas(this.context)
.width(300)
.height(150)
.backgroundColor(Color.Pink)
.onReady(() => {
this.centerX = this.context.width / 2
this.centerY = this.context.height
this.draw()
})
.onTouch((event) => {
let x = this.centerX - event.touches[0].x
let y = this.centerY - event.touches[0].y
this.angle = Math.atan(y / x) * (180 / Math.PI)
if (this.angle < 0) { // Indicates an obtuse angle
this.angle += 180
}
this.draw()
})
Text('Current Angle:' + this.angle.toFixed(2))
}.width('100%').height('100%').justifyContent(FlexAlign.Center).alignItems(HorizontalAlign.Center)
}
}
The achieved effect is as follows:

Top comments (0)