DEV Community

HarmonyOS
HarmonyOS

Posted on

How to Draw Text with Different Alignments on Canvas

Read the original article:How to Draw Text with Different Alignments on Canvas

Requirement Description

When generating text using the CanvasRenderingContext2D method and drawing filled text with fillText, how can the overall text alignment be achieved? For example, center, right, or left alignment?

Background Knowledge

  • Canvas provides a canvas component, and the CanvasRenderingContext2D interface allows drawing on the Canvas component using a RenderingContext. The objects that can be drawn include rectangles, text, images, etc.
  • The fillText method can draw filled text, but it can only draw text within a specified region each time, making it unable to draw text with different alignments all at once.

Implementation Steps

To draw text with different alignments on Canvas, follow the steps below:

1.Create RenderingContextSettings and CanvasRenderingContext2D objects to configure and manage the Canvas rendering context. Define three variables — leftText, centerText, and rightText — to display left-aligned, center-aligned, and right-aligned text respectively. letterSpacing is used to set the character spacing, and lineX marks the horizontal position of the reference line.

   private settings: RenderingContextSettings = new RenderingContextSettings(true)
   private context: CanvasRenderingContext2D = new CanvasRenderingContext2D(this.settings)
   private leftText: string = 'Left-aligned text'
   private centerText: string = 'Center-aligned text'
   private rightText: string = 'Right-aligned text'
   private letterSpacing: number = 10
   private lineX: number = 200
Enter fullscreen mode Exit fullscreen mode

2.The getTotalWidth method calculates the total width of the specified text by iterating through each character, accumulating the width of each character and the corresponding spacing to obtain the total width of the text.

   getTotalWidth(text: string): number {
     let totalWidth = 0
     for (let i = 0; i < text.length; i++) {
       // Accumulate occupied width
       if (i === text.length - 1) {
         totalWidth += this.context.measureText(text[i]).width;
       } else {
         totalWidth += this.context.measureText(text[i]).width + this.letterSpacing;
       }
     }
     return totalWidth
   }
Enter fullscreen mode Exit fullscreen mode

3.In the onReady event of the Canvas, first set the pen color to blue and draw a reference line as the alignment baseline. Then, in this event handler, calculate the position for drawing the text and use a loop to call fillText to draw the text with different alignments sequentially:

  • Left Alignment: Determine the starting position for drawing the text by accumulating the width and spacing of each character.

     let leftTotalWidth = this.lineX
     this.context.font = '24vp sans-serif'
     for (let i = 0; i < this.leftText.length; i++) {
       this.context.fillText(this.leftText[i], leftTotalWidth, 50);
       // Accumulate occupied width
       if (i === this.leftText.length - 1) {
         leftTotalWidth += this.context.measureText(this.leftText[i]).width;
       } else {
         leftTotalWidth += this.context.measureText(this.leftText[i]).width + this.letterSpacing;
       }
     }
    
  • Center Alignment: Calculate the total text width first, then set the starting position to (lineX - totalWidth / 2) to achieve center alignment.

     let centerTotalWidth = 0
     centerTotalWidth = this.getTotalWidth(this.centerText)
     centerTotalWidth = this.lineX - centerTotalWidth / 2
     for (let i = 0; i < this.centerText.length; i++) {
       this.context.fillText(this.centerText[i], centerTotalWidth, 90);
       // Accumulate occupied width
       if (i === this.centerText.length - 1) {
         centerTotalWidth += this.context.measureText(this.centerText[i]).width;
       } else {
          centerTotalWidth += this.context.measureText(this.centerText[i]).width + this.letterSpacing;
       }
     }
    
  • Right Alignment: Calculate the total text width first, then set the starting position to (lineX - totalWidth) to achieve right alignment.

     let rightTotalWidth = 0
     rightTotalWidth = this.getTotalWidth(this.rightText)
     rightTotalWidth = this.lineX - rightTotalWidth
     for (let i = 0; i < this.rightText.length; i++) {
       this.context.fillText(this.rightText[i], rightTotalWidth, 130);
       // Accumulate occupied width
       if (i === this.rightText.length - 1) {
         rightTotalWidth += this.context.measureText(this.rightText[i]).width;
       } else {
         rightTotalWidth += this.context.measureText(this.rightText[i]).width + this.letterSpacing;
       }
     }
    

Code Snippet

Complete example reference:

@Entry
@Component
struct Index {
  private settings: RenderingContextSettings = new RenderingContextSettings(true)
  private context: CanvasRenderingContext2D = new CanvasRenderingContext2D(this.settings)
  private leftText: string = 'Left-aligned text'
  private centerText: string = 'Center-aligned text'
  private rightText: string = 'Right-aligned text'
  private letterSpacing: number = 0
  private lineX: number = 116

  getTotalWidth(text: string): number {
    let totalWidth = 0
    for (let i = 0; i < text.length; i++) {
      // Accumulate occupied width
      if (i === text.length - 1) {
        totalWidth += this.context.measureText(text[i]).width;
      } else {
        totalWidth += this.context.measureText(text[i]).width + this.letterSpacing;
      }
    }
    return totalWidth
  }

  build() {
    Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) {
      Canvas(this.context)
        .width('100%')
        .height('100%')
        .backgroundColor('#ffff00')
        .onReady(() => {
          this.context.strokeStyle = '#0000ff'
          this.context.moveTo(this.lineX, 10)
          this.context.lineTo(this.lineX, 160)
          this.context.stroke()

          // Left Alignment
          let leftTotalWidth = this.lineX
          this.context.font = '12vp sans-serif'
          for (let i = 0; i < this.leftText.length; i++) {
            this.context.fillText(this.leftText[i], leftTotalWidth, 50);
            // Accumulate occupied width
            if (i === this.leftText.length - 1) {
              leftTotalWidth += this.context.measureText(this.leftText[i]).width;
            } else {
              leftTotalWidth += this.context.measureText(this.leftText[i]).width + this.letterSpacing;
            }
          }

          // Center Alignment
          let centerTotalWidth = 0
          centerTotalWidth = this.getTotalWidth(this.centerText)
          centerTotalWidth = this.lineX - centerTotalWidth / 2
          for (let i = 0; i < this.centerText.length; i++) {
            this.context.fillText(this.centerText[i], centerTotalWidth, 90);
            // Accumulate occupied width
            if (i === this.centerText.length - 1) {
              centerTotalWidth += this.context.measureText(this.centerText[i]).width;
            } else {
              centerTotalWidth += this.context.measureText(this.centerText[i]).width + this.letterSpacing;
            }
          }

          // Right Alignment
          let rightTotalWidth = 0
          rightTotalWidth = this.getTotalWidth(this.rightText)
          rightTotalWidth = this.lineX - rightTotalWidth
          for (let i = 0; i < this.rightText.length; i++) {
            this.context.fillText(this.rightText[i], rightTotalWidth, 130);
            // Accumulate occupied width
            if (i === this.rightText.length - 1) {
              rightTotalWidth += this.context.measureText(this.rightText[i]).width;
            } else {
              rightTotalWidth += this.context.measureText(this.rightText[i]).width + this.letterSpacing;
            }
          }
        })
    }
    .width('100%')
    .height('100%')
  }
}
Enter fullscreen mode Exit fullscreen mode

Test Results

cke_1565.png

Limitations or Considerations

Canvas :

supported since API version 8

Supported devices : Phone PC/2in1 Tablet TV Wearable

CanvasRenderingContext2D :

supported since API version 8,

Supported devices : Phone PC/2in1 Tablet TV Wearable

fillText :

supported since API version 8,

Supported devices : Phone PC/2in1 Tablet TV Wearable

Written by Bunyamin Akcay

Top comments (0)