DEV Community

HarmonyOS
HarmonyOS

Posted on

Implementing Product Switching Animation

Read the original article:Implementing Product Switching Animation

Requirement Description

The goal is to implement an animated product switching effect commonly used on shopping app homepages to display multiple product images that transition automatically with a smooth visual effect.

Background Knowledge

  • Swiper: A container that allows child components to slide or loop automatically, suitable for banner-style carousels.
  • Stack: A layout container that stacks components in order, where later components overlay earlier ones.
  • opacity: Defines the transparency of a component.
  • scale: Defines the scaling size of a component.
  • setInterval: Calls a function repeatedly at specified time intervals.

Implementation Steps

Solution 1 – Using Swiper Component

Group product images in pairs and use the Swiper component to flip through them by page.
This creates a two-by-two switching effect similar to a carousel.
Refer to Example 4 (custom page transition animation) in the HarmonyOS documentation for implementation.

Solution 2 – Using Stack Layout with opacity and scale Animations

Combine the Stack layout with the opacity and scale attributes.
Use a timer (setInterval) to trigger periodic animations that change image opacity and scale to achieve a smooth fade-and-zoom switching effect.

Code Snippet / Configuration

@Entry
@Component
struct ProductSwitch {
  @State imgArr: AnimateModel[] = [];
  @State topImg: AnimateModel | undefined = undefined;
  @State bottomImg: AnimateModel | undefined = undefined;
  @State index: number = 0;
  @State imageOpacity1: number = 1;
  @State imageOpacity2: number = 0;
  @State imageScale1: number = 1;
  @State imageScale2: number = 0.6;
  private timer: number | null = null;

  aboutToDisappear(): void {
    if (this.timer !== null) {
      clearInterval(this.timer); // Clear timer on exit
      this.timer = null;
    }
  }

  aboutToAppear(): void {
    // 'app.media.startIcon' is for demonstration only; replace with your own resources.
    this.imgArr.push(new AnimateModel($r('app.media.startIcon'), $r('app.media.startIcon')));
    this.imgArr.push(new AnimateModel($r('app.media.startIcon'), $r('app.media.startIcon')));
    this.imgArr.push(new AnimateModel($r('app.media.startIcon'), $r('app.media.startIcon')));
    this.imgArr.push(new AnimateModel($r('app.media.startIcon'), $r('app.media.startIcon')));

    this.topImg = this.imgArr[this.index];
    this.bottomImg = this.imgArr[this.index + 1];

    // Start animation timer
    this.timer = setInterval(() => {
      this.getUIContext().animateTo({
        duration: 500,
        onFinish: () => {
          if (++this.index === this.imgArr.length) {
            this.index = 0;
          }
          let tempIndex = this.index + 1 === this.imgArr.length ? 0 : this.index + 1;
          if (this.imageOpacity1 === 1) {
            this.bottomImg = this.imgArr[tempIndex];
          } else {
            this.topImg = this.imgArr[tempIndex];
          }
        }
      }, () => {
        this.imageOpacity1 = this.imageOpacity1 ? 0 : 1;
        this.imageOpacity2 = this.imageOpacity2 ? 0 : 1;
        this.imageScale1 = this.imageScale1 === 1 ? 0.6 : 1;
        this.imageScale2 = this.imageScale2 === 1 ? 0.6 : 1;
      });
    }, 2000);
  }

  build() {
    Row({ space: 20 }) {
      // Left product animation
      Stack() {
        Image(this.topImg?.leftImg)
          .width(100)
          .opacity(this.imageOpacity1)
          .scale({ x: this.imageScale1, y: this.imageScale1 })
        Image(this.bottomImg?.leftImg)
          .width(100)
          .opacity(this.imageOpacity2)
          .scale({ x: this.imageScale2, y: this.imageScale2 })
      }

      // Right product animation
      Stack() {
        Image(this.topImg?.rightImg)
          .width(100)
          .opacity(this.imageOpacity1)
          .scale({ x: this.imageScale1, y: this.imageScale1 })
        Image(this.bottomImg?.rightImg)
          .width(100)
          .opacity(this.imageOpacity2)
          .scale({ x: this.imageScale2, y: this.imageScale2 })
      }
    }
    .height('100%')
    .width('100%')
    .justifyContent(FlexAlign.Center)
  }
}

class AnimateModel {
  leftImg: Resource;
  rightImg: Resource;
  constructor(leftImg: Resource, rightImg: Resource) {
    this.leftImg = leftImg;
    this.rightImg = rightImg;
  }
}
Enter fullscreen mode Exit fullscreen mode

Test Results

  • Product images smoothly switch every 2 seconds.
  • Opacity and scale changes create a fade-and-zoom effect similar to a professional e-commerce homepage.
  • The transition runs stably without flickering or lag.

Limitations or Considerations

  • Developers must replace $r('app.media.startIcon') with valid image resources
  • Be sure to clear timers (clearInterval) in aboutToDisappear() to prevent memory leaks.
  • The animation interval and duration can be adjusted for desired effect smoothness.
  • Supports API Version 20 Release and above.
  • Requires HarmonyOS 6.0.0 Release SDK or later.
  • Must be compiled and executed using DevEco Studio 6.0.0 Release or later.

Related Documents or Links

cke_3287.png

Written by Arif Emre Ankara

Top comments (0)