Numeric Precision Issues, Slider Problems, Routing Animation Issues, Fixed-Count Loop Rendering



This content originally appeared on DEV Community and was authored by kouwei qing

[Daily HarmonyOS Next Knowledge] Numeric Precision Issues, Slider Problems, Routing Animation Issues, Fixed-Count Loop Rendering, Canvas Animation Implementation

1. Does HarmonyOS directly convert to number type for addition, subtraction, multiplication, and division? How to avoid precision anomalies after calculation?

Are calculations directly converted to the number type for addition, subtraction, multiplication, and division? How to avoid precision anomalies in calculated data?

Since all numbers are double-precision floating-point numbers stored in binary in computers, decimals and non-safe integers (data exceeding the integer safety range [-Math.pow(2, 53), Math.pow(2, 53)]) may experience precision loss during calculations.

  1. Decimal operations: For example, “0.2 + 2.22 = 2.4200000000000004”. Convert decimals to integers for calculation, then scale the result down:

    (0.2 * 100 + 2.22 * 100) / 100 = 2.42.

  2. Non-safe integer operations: For example, “9007199254740992 + 1 = 9.007199254740992”. Convert numbers longer than 15 digits to scientific notation:

    9007199254740992 + 1 = 9.007199254740993e15.

    Refer to the official demo: https://developer.huawei.com/consumer/cn/codelabsPortal/carddetails/tutorials_NEXT-SimpleCalculator

2. When the HarmonyOS Slider is set to vertical direction with blockStyle as IMAGE, the slider cannot be moved?

The Slider can move normally when set to vertical direction with blockStyle as IMAGE.

@Entry
@Component
struct SliderExample {
  @State vInSetValueOne: number = 40
  @State vInSetValueTwo: number = 40

  build() {
    Column({ space: 8 }) {
      Column() {
        Text('vertical inset slider').fontSize(9).fontColor(0xCCCCCC).width('50%').margin(15)
        Row() {
          Slider({
            value: this.vInSetValueOne,
            style: SliderStyle.InSet,
            direction: Axis.Vertical,
            reverse: true // The vertical Slider defaults to min at the top and max at the bottom; set reverse to true for bottom-to-top sliding
          })
            .blockStyle({ type: SliderBlockType.IMAGE, image: $r('sys.media.ohos_ic_public_ok') })
            .showTips(true)
            .onChange((value: number, mode: SliderChangeMode) => {
              this.vInSetValueOne = value
              console.info('value:' + value + 'mode:' + mode.toString())
            })
          Slider({
            value: this.vInSetValueTwo,
            step: 10,
            style: SliderStyle.InSet,
            direction: Axis.Vertical,
            reverse: true
          })
            .blockStyle({ type: SliderBlockType.IMAGE, image: $r('sys.media.ohos_app_icon') })
            .showSteps(true)
            .onChange((value: number, mode: SliderChangeMode) => {
              this.vInSetValueTwo = value
              console.info('value:' + value + 'mode:' + mode.toString())
            })
        }
      }.width('50%').height(300)
    }.width('100%')
  }
}

3. [router] When page 1 disables the exit transition animation, pushing to page 2 makes page 2 fully transparent during transition, revealing page 1 until the animation ends?

Steps:

  1. Page 1 overrides pageTransition to disable the exit transition animation.
  2. Page 1 pushes to page 2 via router.
  3. Page 2 becomes fully transparent during transition, showing page 1 until the animation completes.

Expectation: Page 2 should not become fully transparent during transition.

To disable page transition animations, set both entry and exit transitions. Refer to the code:

// main.ets
import router from '@ohos.router';

@Entry
@Component
struct Index {
  @State message: string = 'Hello World';

  build() {
    Row() {
      Column() {
        Text(this.message).fontSize(50).fontWeight(FontWeight.Bold).onClick(() => {
          router.pushUrl({ url: 'pages/240115173611047_second' })
        })
      }.width('100%')
    }.height('100%').backgroundColor('#A0FF0000')
  }

  pageTransition() {
    PageTransitionEnter({ type: RouteType.None, duration: 0 })
    PageTransitionExit({ type: RouteType.None, duration: 0 })
  }
}

// second.ets
import router from '@ohos.router'

@Entry
@Component
export struct Second {
  build() {
    Column() {
      Text('Back').onClick(() => {
        router.back()
      })
    }.width('100%').height('100%').backgroundColor('#A00000FF')
  }

  pageTransition() {
    PageTransitionEnter({ type: RouteType.None, duration: 0 })
    PageTransitionExit({ type: RouteType.None, duration: 0 })
  }
}

The Navigation component is suitable for in-module page switching in cross-device scenarios, offering natural transition effects and various title bar styles. It automatically adapts to window sizes, switching to split-column display for larger windows. Use Navigation for page routing (router will not be further evolved). Refer to: https://developer.huawei.com/consumer/cn/doc/harmonyos-references-V5/ts-basic-components-navigation-V5

4. How to render a fixed number of items in a loop in HarmonyOS ArkUI?

How to render a fixed number of items in a loop in ArkUI?

Currently, component rendering only supports ForEach, which requires an array as the first parameter. For simple rendering without complex elements, create a dummy array:

ForEach([1,2,3,4,5],(item:number) =>{
  Text('这是Item='+item)
})

5. How to implement animations with HarmonyOS Canvas?

Refer to: https://developer.huawei.com/consumer/cn/doc/harmonyos-references-V5/ts-canvasrenderingcontext2d-V5

ArkTS Canvas usage is similar to web Canvas. Animations can be ported from web Canvas demos:

@Entry
@Component
struct Index {
  private settings: RenderingContextSettings = new RenderingContextSettings(true)
  private context: CanvasRenderingContext2D = new CanvasRenderingContext2D(this.settings)
  @State timerId: number = 0
  @State startAngle: number = Math.PI * 0.2 + (Math.PI / 180) * 90; // Start angle (radians)
  @State endAngle: number = Math.PI * 1.8 + (Math.PI / 180) * 90; // End angle (radians)
  lengthToAdd: number = Math.PI / 360; // Length added per draw (radians)
  @State currentAngle: number = 0
  @State step: number = 1;
  // Center coordinates and radius
  centerX = 200;
  centerY = 200;
  radius = 100;

  aboutToAppear(): void {
    this.currentAngle = this.startAngle
    this.timerId = setInterval(() => {
      if (this.step %2 == 1) {
        this.currentAngle += this.lengthToAdd;
        this.drawArc();
        if (this.currentAngle > this.endAngle) {
          this.step++;
          this.context.globalCompositeOperation = "destination-out"; // Change composite mode for erasing
          this.context.lineWidth = 21; // Wider line for erasing to prevent residues
        }
      } else {
        this.eraseArc();
        this.currentAngle -= this.lengthToAdd;
        if (this.currentAngle<= this.startAngle) {
          this.step++;
          this.context.globalCompositeOperation = "source-over"; // Restore composite mode for normal drawing
          this.context.lineWidth = 20; // Normal line width
        }
      }
    }, 10)
  }

  drawArc() {
    // Draw arc
    this.context.beginPath();
    this.context.arc(
      this.centerX, // Arc center x-coordinate
      this.centerY + 30, // Arc center y-coordinate
      this.radius, // Arc radius
      this.currentAngle - this.lengthToAdd, // Start angle (radians)
      this.currentAngle + this.lengthToAdd // End angle (radians)
    );
    this.context.stroke();
  }

  eraseArc() {
    this.context.beginPath();
    this.context.arc(
      this.centerX,
      this.centerY + 30,
      this.radius,
      this.currentAngle - this.lengthToAdd,
      this.currentAngle + 2 * this.lengthToAdd // Wider erasing to prevent residues
    );
    this.context.stroke();
  }

  build() {
    Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) {
      Canvas(this.context)
        .width('100%')
        .height('100%')
        .backgroundColor('#ffff00')
        .onReady(() => {
          // Set arc color and width
          this.context.strokeStyle = Color.Green;
          this.context.lineWidth = 20;
        })
    }
    .width('100%')
    .height('100%')
  }
}


This content originally appeared on DEV Community and was authored by kouwei qing