DEV Community

HarmonyOS
HarmonyOS

Posted on

The problem of multiple components in HarmonyOS using animateTo animations not being synchronized

Read the original article:The problem of multiple components in HarmonyOS using animateTo animations not being synchronized

Problem Description

In a HarmonyOS application, when using the animateTo method to implement property animations for two Column components, the animations are not executed synchronously.
The problematic code is as follows:

import { display } from '@kit.ArkUI';

const SCREEN_WIDTH = display.getDefaultDisplaySync().width;

@Entry
@Component
struct Index {
  @State translateX: number = SCREEN_WIDTH
  @State lastTranslateX: number = 0

  build() {
    Column() {
      Button('Triggering an animation')
        .onClick(() => this.startAnimation())
        .margin(100)
      Column() {
        Text('B')
          .fontSize(20)
      }
      .width('100%')
      .height(120)
      .backgroundColor('#f1f3f5')
      .opacity(1)
      .translate({ x: this.translateX })
      .alignItems(HorizontalAlign.Center)
      .justifyContent(FlexAlign.Center)

      Column() {
        Text('A')
          .fontSize(20)
      }
      .width('100%')
      .height(120)
      .backgroundColor('#f1f3f5')
      .opacity(1)
      .translate({ x: this.lastTranslateX })
      .alignItems(HorizontalAlign.Center)
      .justifyContent(FlexAlign.Center)
    }

  }

  private startAnimation() {
    this.getUIContext().animateTo({
      duration: 1800,
      curve: Curve.EaseOut
    }, () => {
      this.translateX = 0
      this.lastTranslateX = -SCREEN_WIDTH
    })
  }
}
Enter fullscreen mode Exit fullscreen mode

Background Knowledge

  • Translate is a general property provided by HarmonyOS for setting the translation of components.
  • getDefaultDisplaySync is a method provided by the display module to obtain the current default display object. Through this object, you can access screen-related properties such as width.
  • UIContext provides the animateTo interface to specify transition animations for state changes caused by closure code.
  • onAreaChange is a component area change event that triggers this callback when the component's area changes. This function only responds to callbacks caused by layout changes that affect the component's size or position.

Problem Analysis

In the animateTo method, all statements are executed in sequence, not synchronously.

Analysis Conclusion

Since the statements in the animateTo method can only be executed sequentially, is it possible to use other callback functions to achieve synchronized animation effects? One approach that comes to mind is using the onAreaChange callback method to synchronize the animations.

Solution

Define the newValue state variable and use the onAreaChange callback method to obtain the real-time width of component A on the screen. Then, set the translation value based on the component's width.

The complete code is shown below:

@Entry
@Component
struct AnimationOutOfSync {
  @State lastTranslateX: number = 0;
  @State newValue: number = 0;
  @State translateX: number = this.newValue;

  build() {
    Column() {
      Button('Triggering an animation')
        .onClick(() => this.startAnimation())
        .margin(100)
      Column() {
        Text('B')
          .fontSize(20)
      }
      .width('100%')
      .height(120)
      .backgroundColor('#f1f3f5')
      .translate({ x: this.translateX })
      .opacity(1)
      // Use onAreaChange to obtain the width of the current component
      .onAreaChange((oldValue: Area, newValue: Area) => {
        this.newValue = newValue.width as number;
        this.translateX = newValue.width as number;
      })
      .alignItems(HorizontalAlign.Center)
      .justifyContent(FlexAlign.Center)

      Column() {
        Text('A')
          .fontSize(20)
      }
      .width('100%')
      .height(120)
      .backgroundColor('#f1f3f5')
      .translate({ x: this.lastTranslateX })
      .alignItems(HorizontalAlign.Center)
      .justifyContent(FlexAlign.Center)
    }
  }

  private startAnimation() {
    this.getUIContext()?.animateTo({
      duration: 1800,
      curve: Curve.EaseOut
    }, () => {
      this.translateX = 0;
      this.lastTranslateX = -this.newValue;
    });
  }
}
Enter fullscreen mode Exit fullscreen mode

Written by Mehmet Emir Ucar

Top comments (0)