可见区域变化监听事件onVisibleAreaChange使用问题
=================================

#### 问题现象

onVisibleAreaChange在组件可见区域变化时触发该回调。如何正确理解和使用onVisibleAreaChange？

*   问题一：有多个子元素都绑定了onVisibleAreaChange事件，如何获取它们的触发顺序？
*   问题二：阈值数组中不存在1或者0，但直接控制组件显隐时仍然会触发onVisibleAreaChange。
    
    示例代码如下：
    
    @Entry
    @Component
    struct Index2 {
      @State isVisible: Visibility = Visibility.Visible;
    
      build() {
        Column() {
          Text('Hello World')
            .fontSize($r('app.float.page\_text\_font\_size'))
            .fontWeight(FontWeight.Bold)
            .visibility(this.isVisible)
            .onClick(() => {
              this.isVisible = Visibility.Hidden;
            })
            .onVisibleAreaChange(\[0.5\], (isVisible: boolean, currentRatio: number) => {
              console.info(\`Test Text isExpanding: ${isVisible}, currentRatio: ${currentRatio}\`);
            });
        }
        .height(100)
        .width('100%');
      }
    }
    
*   问题三：组件位置超出父组件后，未触发onVisibleAreaChange。
    
    示例代码如下：
    
    @Entry
    @Component
    struct Index3 {
      build() {
        Column() {
          Text('Hello World')
            .offset({ y: 100 })
            .fontSize($r('app.float.page\_text\_font\_size'))
            .fontWeight(FontWeight.Bold)
            .onVisibleAreaChange(\[0.0, 1.0\], (isVisible: boolean, currentRatio: number) => {
              console.info(\`Test Text isExpanding: ${isVisible}, currentRatio: ${currentRatio}\`);
            });
        }
        .height(100)
        .width('100%');
      }
    }
    

#### 背景知识

[onVisibleAreaChange](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/ts-universal-component-visible-area-change-event#onvisibleareachange)在组件可见区域变化时触发该回调。

#### 解决方案

*   问题一：可以在事件回调中绑定唯一标识（如列表项索引）。
    
    1.  记录事件触发的队列。
    2.  处理事件队列中的事件，按索引排序。
    
    示例代码如下：
    
    class DateBase {
      key: number;
      visible: boolean;
    
      constructor(key: number, visible: boolean) {
        this.key = key;
        this.visible = visible;
      }
    }
    
    @Entry
    @Component
    struct IndexDemo {
      @State listStr: string\[\] = \[\];
    
      aboutToAppear(): void {
        for (let index = 0; index < 50; index++) {
          this.listStr.push(\`测试数据 ${index}\`);
        }
      }
    
     _ // 记录事件触发的队列，按索引排序_
      private eventQueue: Array<DateBase> = \[\];
    
     _ // 处理事件的统一方法（按索引顺序执行）_
      private processEvents() {
      _  // 按索引排序_
        this.eventQueue.sort((a, b) => a.key - b.key);
       _ // 依次处理_
        this.eventQueue.forEach(item => {
        _  // 执行实际业务逻辑（如更新UI、加载数据等）_
          console.info(\`处理Item${item.key}，可见性: ${item.visible}\`);
        });
       _ // 清空队列_
        this.eventQueue = \[\];
      }
    
      build() {
        Column() {
          List({ space: 10 }) {
            ForEach(this.listStr, (item: string, index) => {
              ListItem() {
                Text(item)
                  .height(100)
                  .fontSize(20)
                  .fontColor(Color.Black)
                  .fontWeight(FontWeight.Bold)
                  .textAlign(TextAlign.Center);
              }
              .width('100%')
              .height('10%')
              .backgroundColor(Color.Red)
              .onVisibleAreaChange(\[0.0, 1.0\], (visible: boolean, currentRatio: number) => {
              _  // 不直接处理，先加入队列_
                if (visible && currentRatio === 1.0) {
                  let timeoutId: boolean = false;
                  this.eventQueue.push(new DateBase(index, visible));
                  if (!timeoutId) {
                    setTimeout(() => {
                      this.processEvents();
                      timeoutId = true;
                    }, 0);
                  }
                }
              });
            }, (item: string, index) => JSON.stringify(item));
          }
          .layoutWeight(1);
        }
        .height('100%')
        .width('100%');
      }
    }
    
*   问题二：ratios参数规定了onVisibleAreaChange触发回调的阈值数组，当组件的可见性经过这个阈值时即可触发。当阈值为0.5时，组件即便是突然显示或隐藏都会触发onVisibleAreaChange。
*   问题三：onVisibleAreaChange仅提供自身节点相对于所有祖先节点（直到window边界）的相对裁切面积与自身面积的比值及其变化趋势。
    
    当组件位置超出了父组件的可见区域（裁切区域）时，系统无法计算其在父组件内的有效可见面积（或判定为无有效变化），因此不会触发该回调。
    

#### 常见FAQ

Q：页面跳转时为什么不会触发onVisibleAreaChange。

A：当页面跳转时，由于页面出栈导致组件销毁，也不会触发onVisibleAreaChange。

Q：onVisibleAreaChange能否用于监听组件挂载或绘制完成？

A：监听挂载可以使用[onAppear](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/ts-universal-events-show-hide#onappear)，监听绘制完成的可以使用布局回调[on('layout')](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/js-apis-arkui-inspector#onlayout)，onVisibleAreaChange的作用是监听组件是否在屏幕中，主要用于组件曝光统计与分析，资源按需加载与释放，感知复杂视图切换。示例参考官网[感知组件可见性](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/arkts-manage-components-visibility)。