DEV Community

HarmonyOS
HarmonyOS

Posted on

How to Implement a Digital Display Component with Scroll Animation Effects

Read the original article:How to Implement a Digital Display Component with Scroll Animation Effects

How to Implement a Digital Display Component with Scroll Animation Effects

Problem Description

How to implement a numeric display component with a scrolling animation effect, where the number can be increased by clicking a button, and the number will transition to the new value with an animation effect.

Background Knowledge

  • The explicit animation interface animateTo can specify the insertion of transition effects for state changes caused by closure code.
  • The general property translate can set the component's translation, allowing the component to move within a coordinate system where the top-left corner of the component serves as the origin. The margin property can set the margin properties of the component, maintaining its relative position.

Solution

1.Define state variables. The state variable scrollNumber is bound to the num state of the parent component using the @Prop and @Watch decorators. When the num value changes, the changeNumber method is called. Other state variables are as follows:

@Prop @Watch('changeNumber') scrollNumber: number = 0;
@State bottomNum: number = 0;
@State showNumber: string = '0';
@State cacheNumber: string = '';
changeNumberFinal: string = '';
isScrolling: boolean = false;
isInit: boolean = false;

changeNumber() {
  if (this.isInit) {
    this.animateToScroll(this.scrollNumber);
  }
}
Enter fullscreen mode Exit fullscreen mode

2.Define the method initAnimateToScroll to initialize the animation and set a timer to start the animation after 50 milliseconds. In the animateToScroll method, execute the scrolling animation, update bottomNum and isScrolling based on the passed newNum, and update showNumber and cachNumber after the animation completes.

initAnimateToScroll() {
  setTimeout(() => {
    this.getUIContext().animateTo({
      duration: 600,
      onFinish: () => {
        this.isInit = true;
      }
    }, () => {
      this.bottomNum = -50;
    });
  }, 50);
}

animateToScroll(newNum: number) {
  this.changeNumberFinal = String(newNum);
  if (this.isScrolling) {
    return;
  }
  this.cacheNumber = this.changeNumberFinal;
  this.getUIContext().animateTo({
    duration: 600,
    onFinish: () => {
      this.bottomNum = -50;
      this.showNumber = this.cacheNumber;
      this.cacheNumber = '';
      this.isScrolling = false;
      if (this.changeNumberFinal !== this.showNumber) {
        this.animateToScroll(Number(this.changeNumberFinal));
      }
    }
  }, () => {
    this.bottomNum = 0;
    this.isScrolling = true;
  });
}
Enter fullscreen mode Exit fullscreen mode

3.Control the vertical position of the numbers using the translate and margin properties, and achieve a scrolling effect in conjunction with animations.

Row() {
  Text(this.showNumber)
    .fontSize(35)
    .fontColor('#0A59F7');
}
.margin({ bottom: this.bottomNum })
.height(50)
.translate({ y: this.bottomNum });
Enter fullscreen mode Exit fullscreen mode

Complete examples are as follows:

@Entry
@Component
struct ScrollNum {
  @State num: number = 0;
  @State isEnabled: boolean = true;

  build() {
    Column({ space: 50 }) {
      ScrollNumComponent({ scrollNumber: this.num });

      Button('Click to add')
        .enabled(this.isEnabled)
        .onClick(() => {
          this.isEnabled = !this.isEnabled;
          setTimeout(() => {
            this.num += 50;
            this.isEnabled = !this.isEnabled;
          }, 600);
        });
    }
    .alignItems(HorizontalAlign.Center)
    .justifyContent(FlexAlign.Center)
    .width('100%')
    .height('100%');
  }
}

@Component
export struct ScrollNumComponent {
  @Prop @Watch('changeNumber') scrollNumber: number = 0;
  @State bottomNum: number = 0;
  @State showNumber: string = '0';
  @State cacheNumber: string = '';
  changeNumberFinal: string = '';
  isScrolling: boolean = false;
  isInit: boolean = false;

  changeNumber() {
    if (this.isInit) {
      this.animateToScroll(this.scrollNumber);
    }
  }

  aboutToAppear(): void {
    this.initAnimateToScroll();
  }

  initAnimateToScroll() {
    setTimeout(() => {
      this.getUIContext().animateTo({
        duration: 600,
        onFinish: () => {
          this.isInit = true;
        }
      }, () => {
        this.bottomNum = -50;
      });
    }, 50);
  }

  animateToScroll(newNum: number) {
    this.changeNumberFinal = String(newNum);
    if (this.isScrolling) {
      return;
    }
    this.cacheNumber = this.changeNumberFinal;
    this.getUIContext().animateTo({
      duration: 600,
      onFinish: () => {
        this.bottomNum = -50;
        this.showNumber = this.cacheNumber;
        this.cacheNumber = '';
        this.isScrolling = false;
        if (this.changeNumberFinal !== this.showNumber) {
          this.animateToScroll(Number(this.changeNumberFinal));
        }
      }
    }, () => {
      this.bottomNum = 0;
      this.isScrolling = true;
    });
  }

  build() {
    Column() {
      Row() {
        Text(this.cacheNumber)
          .fontSize(35)
          .fontColor('#0A59F7');
      }
      .height(50)
      .translate({ y: this.bottomNum });

      Row() {
        Text(this.showNumber)
          .fontSize(35)
          .fontColor('#0A59F7');
      }
      .margin({ bottom: this.bottomNum })
      .height(50)
      .translate({ y: this.bottomNum });
    }
    .clip(true)
    .height(50);
  }
}
Enter fullscreen mode Exit fullscreen mode

Verification Result

_1.gif

Written by Recep Sadullah Yegin

Top comments (0)