DEV Community

HarmonyOS
HarmonyOS

Posted on

Implementing a Protractor Function with Canvas

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

Solution

Use the Canvas component to customize the drawing of a ring chart with graduations. The specific implementation steps are as follows

  1. 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.

  2. 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.

  3. 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.

  4. 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.

  5. 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)
  }
}
Enter fullscreen mode Exit fullscreen mode

The achieved effect is as follows:

image.png

Written by Fatih Turan Gundogdu

Top comments (0)