# 可见区域变化监听事件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在组件可见区域变化时触发该回调。

#### 解决方案

*   问题一：可以在事件回调中绑定唯一标识（如列表项索引）。
    
    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) => 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，监听绘制完成的可以使用布局回调on('layout')，onVisibleAreaChange的作用是监听组件是否在屏幕中，主要用于组件曝光统计与分析，资源按需加载与释放，感知复杂视图切换。示例参考官网感知组件可见性。

Q：页面跳转或半弹层遮挡时，如何使用onVisibleAreaChange判断页面可见性？

A：当前暂不支持。onVisibleAreaChange的可见性计算是基于组件区域在屏幕中的占比，与组件布局位置有关。对于页面跳转（如A页面跳转B页面）或半弹层遮挡（如A页面打开B半弹层页面）等场景，当前没有能力支持，后续也暂不会规划支持。