# 如何实现连续点击小圆点的动画

#### 问题现象

在页面上根据手指点击动态增加小圆点，1秒后自动淡出，要求可以连续点击，后面的点击不会影响前面点击的小圆点动画，这种功能需要如何实现？效果图如下：

#### 背景知识

*   通过动画属性改变引起UI上产生的连续视觉效果，即为属性动画。属性动画是最基础易懂的动画，ArkUI提供三种属性动画接口animateTo、animation和keyframeAnimateTo驱动组件属性按照动画曲线等动画参数进行连续的变化，产生属性动画。
*   opacity是HarmonyOS提供的一种通用属性，用于设置组件的不透明度。

#### 解决方案

1.  点击事件获取坐标：在屏幕上点击时，通过事件对象获取点击位置相对于屏幕的坐标。
    
    ```
    .onClick((e) => {
      this.indexArray[this.index].top = e.y;
      this.indexArray[this.index].left = e.x;
      this.display(this.indexArray[this.index]);
      this.index = this.index + 1 >= 20 ? 0 : this.index + 1;
    });
    ```
    
2.  循环渲染小圆点：使用ForEach循环渲染数组或Map中记录的小圆点，每个小圆点根据其位置信息进行定位。
    
    ```
    ForEach(this.indexArray, (test: Test) => {
      Dot({ test: test });
    }, (test: Test, index: number) => {
      return test.top + ':' + test.left + ':' + index;
    });
    ```
    
3.  定时删除小圆点：在创建小圆点时，设置一个定时器，在指定时间后移除该小圆点的记录。
    
    ```
    hide(test: Test) {
      this.getUIContext().animateTo({ duration: 1000 }, () => {
        test.is = false;
      });
    }
    ```
    
    完整示例参考如下：
    
    ```
    @Entry
    @Component
    struct ClickDotPage {
      @State indexArray: Test[] = [];
      @State index: number = 0;
    
      display(test: Test) {
        this.getUIContext().animateTo({ duration: 1000 }, () => {
          test.is = true;
        });
        setTimeout(() => {
          this.hide(test);
        }, 1000);
      }
    
      hide(test: Test) {
        this.getUIContext().animateTo({ duration: 1000 }, () => {
          test.is = false;
        });
      }
    
      aboutToAppear(): void {
        // 初始化数据
        for (let i = 0; i < 20; i++) {
          this.indexArray.push(new Test(0, 0));
        }
      }
    
      build() {
        Stack() {
          // 在Stack组件中循环渲染数组或Map中记录的小圆点，每个小圆点根据其位置信息进行定位
          ForEach(this.indexArray, (test: Test) => {
            Dot({ test: test });
          }, (test: Test, index: number) => {
            return test.top + ':' + test.left + ':' + index;
          });
        }.width('100%')
        .height('100%')
        .alignContent(Alignment.TopStart)
        // 点击事件获取坐标
        .onClick((e) => {
          this.indexArray[this.index].top = e.y;
          this.indexArray[this.index].left = e.x;
          this.display(this.indexArray[this.index]);
          this.index = this.index + 1 >= 20 ? 0 : this.index + 1;
        });
    
      }
    }
    
    @Component
    struct Dot {
      @ObjectLink test: Test;
      private w: number = 30;
    
      build() {
        if (this.test.is) {
          Row()
            .width(this.w)
            .height(this.w)
            .borderRadius(this.w)
            .backgroundColor(Color.Blue)
            .margin({ left: this.test.left, top: this.test.top })
            .transition({
              opacity: 0
            });
        }
      }
    }
    
    @Observed
    class Test {
      top: number;
      left: number;
      is: boolean = false;
    
      constructor(top: number, left: number) {
        this.top = top;
        this.left = left;
      }
    }
    ```