收藏
回答

如何理解 微信小程序 和 Web 标准在交叉观察器处理容器包含关系时候的标准差异?

框架类型 问题类型 API/组件名称 终端类型 微信版本 基础库版本
小程序 Bug IntersectionObserver 微信iOS客户端 6.5.3 2.0.0

mp 和 web 的标准差异:

  • mp 不会考虑 container 关系
  • web 必须是 container 关系才会计算交叉


mp 和 web 的代码示例如下:

web/w3c 标准:

<!DOCTYPE html>
<!--https://html.onlineviewer.net/-->
<html lang="zh">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>非空 Root IntersectionObserver 极简示例</title>
<style>
body { margin: 0; padding: 0;
border: 5px solid;
font-size: 20px;
font-weight: bold;
}


/* 1. Root 容器样式 */
#container {
width: 300px;
height: 300px;
margin: 50px auto; /* 居中 */
border: 5px solid blue;
overflow: scroll; /* 关键:使内容可滚动,模拟裁剪效果 */
background-color: #f0f8ff;
position: relative;
}


/* 2. 占位符样式,确保容器内有滚动内容 */
#spacer {
height: 500px;
background-color: yellow;
/* 将目标元素放在底部 */
margin-top: 200px;
}


/* 3. Target 元素样式 */
#target {
width: 100%;
height: 100px;
background-color: red;
color: white;
text-align: center;
line-height: 100px;
/* target 元素位于 container 内部,但比 container 底部低 */
position: absolute;
bottom: -100px;
}

#fixed {
left: 25px;
right: 25px;
top: 100px;
height: 100px;
border: 5px solid red;
position: fixed;
z-index: 2;
}
</style>
</head>
<body>
<div id="fixed"></div>
<div id="ratio">ratio</div>
<div id="ratio2">ratio</div>
<div id="container">
<div id="target">被观察的目标</div>
<div id="spacer">滚动占位区域</div>
</div>

<script>
// 1. 获取 Root 和 Target 元素
const rootElement = document.getElementById('container');
const targetElement = document.getElementById('target');
const fixedElement = document.getElementById('fixed');
const ratioElement = document.getElementById('ratio');
const ratioElement2 = document.getElementById('ratio2');

// 2. 配置选项:使用 #container 作为 Root,并定义阈值
const options = {
root: rootElement,
rootMargin: '0px',
threshold: Array.from({length: 101}, (_, i) => i / 100)
};
const options2 = {
root: targetElement,
rootMargin: '0px',
threshold: Array.from({length: 101}, (_, i) => i / 100)
};


// 3. 定义回调函数
const callback = (entries, observer) => {
entries.forEach(entry => {
const ratio = entry.intersectionRatio.toFixed(2);
console.log(`[IO] Target: ${entry.target.id}, Ratio: ${ratio}, isIntersecting: ${entry.isIntersecting}`);

// 实时更新 Target 元素的文本
ratioElement.textContent = `交集比例: ${ratio}`;
});
};
const callback2 = (entries, observer) => {
entries.forEach(entry => {
const ratio = entry.intersectionRatio.toFixed(2);
console.log(`[IO] Target: ${entry.target.id}, Ratio: ${ratio}, isIntersecting: ${entry.isIntersecting}`);

// 实时更新 Target 元素的文本
ratioElement2.textContent = `交集比例: ${ratio}`;
});
};


// 4. 创建并启动观察者
if (rootElement && targetElement) {
const observer = new IntersectionObserver(callback, options);

// 观察目标元素
observer.observe(targetElement);
}
if (fixedElement && targetElement) {
const observer2 = new IntersectionObserver(callback2, options2);

// 观察目标元素
observer2.observe(fixedElement);
}
</script>
</body>
</html>


小程序标准:

JS

Page({
  data: {
    ratio: 'ratio',
    ratio2: 'ratio2'
  },
  onReady() {
    // 1. 创建第一个 IntersectionObserver,以 #container 作为参照区域
    this.observer1 = this.createIntersectionObserver({
      thresholds: Array.from({ length: 101 }, (_, i) => i / 100)
    });
    this.observer1.relativeTo('#container').observe('#target', (res) => {
      // 在小程序中,res.intersectionRatio 是交集比例
      const ratio = res.intersectionRatio.toFixed(2);
      console.log(`[IO1] Target: #target, Root: #container, Ratio: ${ratio}`);
      this.setData({
        ratio: `被观察目标与蓝框的交叉比例: ${ratio}`
      });
    });
    // 2. 创建第二个 IntersectionObserver,以 #target 作为参照区域
    this.observer2 = this.createIntersectionObserver({
      thresholds: Array.from({ length: 101 }, (_, i) => i / 100)
    });
    this.observer2.relativeTo('#target').observe('#fixed', (res) => {
      const ratio = res.intersectionRatio.toFixed(2);
      console.log(`[IO2] Target: #fixed, Root: #target, Ratio: ${ratio}`);
      this.setData({
        ratio2: `被观察目标与红框的交叉比例: ${ratio}`
      });
    });
  },
  onUnload() {
    // 页面卸载时停止所有监听
    if (this.observer1) {
      this.observer1.disconnect();
    }
    if (this.observer2) {
      this.observer2.disconnect();
    }
  }
});

CSS

page {
    border: 5rpx solid;
    font-size: 40rpx;
    font-weight: bold;
}

#container {
    width: 600rpx;
    height: 600rpx;
    margin: 100rpx auto;
    border: 5rpx solid blue;
    background-color: #f0f8ff;
    position: relative;
}

#spacer {
    height: 1000rpx;
    width: 100%;
    background-color: yellow;
    margin-top: 400rpx;
}

#target {
    width: 100%;
    height: 200rpx;
    background-color: red;
    color: white;
    text-align: center;
    line-height: 200rpx;
    position: absolute;
    bottom: -200rpx;
}
 
#fixed {
    left: 50rpx;
    right: 50rpx;
    top: 200rpx;
    height: 200rpx;
    width: 650rpx;
    border: 5rpx solid red;
    position: fixed;
    z-index: 2;
}

#ratio, #ratio2 {
  font-size: 30rpx;
  position: fixed;
  z-index: 3;
  left: 10rpx;
  top: 10rpx;
}

#ratio2 {
  top: 50rpx;
}


XML

<view id="fixed"></view>
<view id="ratio">{{ratio}}</view>
<view id="ratio2">{{ratio2}}</view>
<scroll-view id="container" scroll-y="{{true}}">
    <view id="target">被观察的目标</view>
    <view id="spacer">滚动占位区域</view>
</scroll-view>
回答关注问题邀请回答
收藏
登录 后发表内容