IntersectionObserver是什麼?

IntersectionObserver.png

  • IntersectionObserver概覽
  • IntersectionObserver構造器
  • IntersectionObserver方法
  • IntersectionObserver懶加載(vue單文件組件版)
  • IntersectionObserver吸頂(vue單文件組件版)
  • IntersectionObserver觸底(vue單文件組件版)
  • IntersectionObserver懶加載、吸頂、觸底綜合(vue單文件組件版)
  • 基於Intersection的開源工具 Scrollama
  • 總結
  • 參考資料

IntersectionObserver概覽

  • IntersectionObserver提供了一個方式去異步觀察 有一個祖先element或者top-level document viewport的目標element 的交叉變化。
  • 祖先元素或者viewport被當作root節點。
  • 當IntersectionObserver建立出的時候,它會被配置到監聽root內部的給定visibility的變化。
  • 一旦IntersectionObserver建立出來,它的配置是不能變的。 因此一個observer object只能用來監測一個指定visibility值的變化。
  • 雖然只能一對一去watch ratio,可是能夠在同一個observer中watch多個target elements。也就是說一個visibility ratio能夠檢測多個不一樣的elements。

IntersectionObserver構造器

var observer = new IntersectionObserver(callback[, options]);css

  • 和其餘構造器同樣,建立並返回一個新的Intersection對象。
  • rootMargin若是指定一個特殊值,是爲了確保語法是否正確
  • 閥值是爲了確保值在0.0到1.0之間,threshold會按照升序排列。若threshold是空,值爲[0.0]。

參數

  • callback 當目標元素的透明度穿過設定的threshold值時,函數會被調用。callback接受兩個 參數。vue

    • entries 傳入各個threshold值的數組,比該閥值指定的百分比更明顯或者更不明顯。
    • observer 調用callback的observer實例。
  • options 若options沒設置。observer使用document的viewport做爲root,沒有margin,0%的threshold(意味着即便有1 px的變化也會觸發回調)git

    • root 被當作viewport的元素。
    • rootMargin 語法是"0px 0px 0px 0px"
    • threshold 指明被監測目標總綁定盒模型的交叉區域ratio,值在0.0到1.0之間;0.0意味着即便是1px也會被當作可見的。1.0意味着整個元素是可見的。默認threshold值是0.0。

IntersectionObserver方法

  • IntersectionObserver.disconnect() 中止observe一個目標。
  • IntersectionObserver.observe() 告訴IntersectionObserver一目標元素去observe。
  • IntersectionObserver.takeRecords() 返回包含全部observe的對象一個數組。
  • IntersectionObserver.unobserve() 取消observe一個目標對象。

示例

下面的例子在threshold值變化在10%以上時觸發myObserverCallback。github

let observer = new IntersectionObserver(myObserverCallback, { "threshold": 0.1 });

IntersectionObserver懶加載(vue單文件組件版)

<template>
  <div>
    <img v-for="(image, i) in images" :key="i" src :data-img-url="image" />
  </div>
</template>

<script>
export default {
  data() {
    return {
      images: [
        'https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1574247890587&di=88d4066be3d57ac962a6bec37e265d37&imgtype=0&src=http%3A%2F%2F01.imgmini.eastday.com%2Fmobile%2F20170810%2F20170810151144_d41d8cd98f00b204e9800998ecf8427e_3.jpeg',
        'https://ss3.bdstatic.com/70cFv8Sh_Q1YnxGkpoWK1HF6hhy/it/u=4054762707,1853885380&fm=26&gp=0.jpg',
        'https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1574247912077&di=508a949e5e291875debf6ca844292cd4&imgtype=0&src=http%3A%2F%2F03imgmini.eastday.com%2Fmobile%2F20180827%2F20180827095359_6759372e9bd28026ee6f53b500fb4291_2.jpeg',
      ],
    };
  },
  mounted() {
    const images = document.querySelectorAll('img');

    const observerLazyLoad = new IntersectionObserver((entries) => {
      entries.forEach((item) => {
        if (item.isIntersecting) {
          item.target.src = item.target.dataset.imgUrl;
        }
      });
    });

    images.forEach((image) => {
      observerLazyLoad.observe(image);
    });
  },
};
</script>

<style lang="scss" scoped>
img {
  display: block;
  height: 500px;
  margin: 30px;
}
</style>

IntersectionLazyLoad.gif

IntersectionObserver吸頂(vue單文件組件版)

<template>
  <div>
    <p class="fixed-top-helper"></p>
    <p class="fixed-top-reference"></p>
    <header>頭部</header>
    <main>
      <img v-for="(image, i) in images" :key="i" :src="image" />
    </main>
  </div>
</template>

<script>
export default {
  data() {
    return {
      images: [
        'https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1574247890587&di=88d4066be3d57ac962a6bec37e265d37&imgtype=0&src=http%3A%2F%2F01.imgmini.eastday.com%2Fmobile%2F20170810%2F20170810151144_d41d8cd98f00b204e9800998ecf8427e_3.jpeg',
        'https://ss3.bdstatic.com/70cFv8Sh_Q1YnxGkpoWK1HF6hhy/it/u=4054762707,1853885380&fm=26&gp=0.jpg',
        'https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1574247912077&di=508a949e5e291875debf6ca844292cd4&imgtype=0&src=http%3A%2F%2F03imgmini.eastday.com%2Fmobile%2F20180827%2F20180827095359_6759372e9bd28026ee6f53b500fb4291_2.jpeg',
      ],
    };
  },
  mounted() {
    const header = document.querySelector('header');
    const fixedTopReference = document.querySelector('.fixed-top-reference');
    fixedTopReference.style.top = `${header.offsetTop}px`;

    const observerFixedTop = new IntersectionObserver((entries) => {
      entries.forEach((item) => {
        if (item.boundingClientRect.top < 0) {
          header.classList.add('fixed');
        } else {
          header.classList.remove('fixed');
        }
      });
    });
    observerFixedTop.observe(fixedTopReference);
  },
};
</script>

<style lang="scss" scoped>
.fixed-top-helper {
  height: 1px;
  background: #ccc;
}
header {
  background: #ccc;
  &.fixed {
    position: fixed;
    top: 0;
    left: 0;
    width: 100%;
  }
}
main {
  img {
    display: block;
    height: 500px;
    margin: 30px;
  }
}
footer {
  background: #ccc;
}
</style>

IntersectionObserverFixedTop.gif

注意事項:數組

  • fixedTopReference是爲了不緩慢移動時add remove .fixed死循環,死循環的結果是抖動
  • fixedTopHelper是爲了不被吸頂元素沒有上一個sibling元素(也就是說被吸頂元素是最上層元素)時,避免緩緩移動時add remove .fixed死循環抖動,特殊引入的標籤,須要設置1個px的height
  • fixedTopHelper須要與被吸頂元素保持樣式一致,以確保好的用戶體驗。例如在本例中將其background設置爲#ccc,很好的作到了隱藏

吸頂抖動

dance.gif

IntersectionObserver觸底(vue單文件組件版)

<template>
  <div>
    <main>
      <img v-for="(image, i) in images" :key="i" src="image" />
    </main>
    <footer>底部</footer>
  </div>
</template>

<script>
export default {
  data() {
    return {
      images: [
        'https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1574247890587&di=88d4066be3d57ac962a6bec37e265d37&imgtype=0&src=http%3A%2F%2F01.imgmini.eastday.com%2Fmobile%2F20170810%2F20170810151144_d41d8cd98f00b204e9800998ecf8427e_3.jpeg',
        'https://ss3.bdstatic.com/70cFv8Sh_Q1YnxGkpoWK1HF6hhy/it/u=4054762707,1853885380&fm=26&gp=0.jpg',
        'https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1574247912077&di=508a949e5e291875debf6ca844292cd4&imgtype=0&src=http%3A%2F%2F03imgmini.eastday.com%2Fmobile%2F20180827%2F20180827095359_6759372e9bd28026ee6f53b500fb4291_2.jpeg',
      ],
    };
  },
  mounted() {
    const footer = document.querySelector('footer');

    const observerTouchBottom = new IntersectionObserver((entries) => {
      entries.forEach((item) => {
        if (item.isIntersecting) {
          setTimeout(() => {
            console.log('滾動到了底部,能夠發request請求數據了');
          }, 2000);
        }
      });
    });

    observerTouchBottom.observe(footer);
  },
};
</script>

<style lang="scss" scoped>
main {
  img {
    display: block;
    height: 500px;
    margin: 30px;
  }
}
footer {
  background: #ccc;
}
</style>

IntersectionObserverTouchBottom.gif

IntersectionObserver懶加載、吸頂、觸底綜合(vue單文件組件版)

<template>
  <div>
    <p class="fixed-top-helper"></p>
    <p class="fixed-top-reference"></p>

    <header>頭部</header>
    <main>
      <img v-for="(image, i) in images" :key="i" src :data-img-url="image" />
    </main>
    <footer>底部</footer>
  </div>
</template>

<script>
export default {
  data() {
    return {
      images: [
        'https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1574247890587&di=88d4066be3d57ac962a6bec37e265d37&imgtype=0&src=http%3A%2F%2F01.imgmini.eastday.com%2Fmobile%2F20170810%2F20170810151144_d41d8cd98f00b204e9800998ecf8427e_3.jpeg',
        'https://ss3.bdstatic.com/70cFv8Sh_Q1YnxGkpoWK1HF6hhy/it/u=4054762707,1853885380&fm=26&gp=0.jpg',
        'https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1574247912077&di=508a949e5e291875debf6ca844292cd4&imgtype=0&src=http%3A%2F%2F03imgmini.eastday.com%2Fmobile%2F20180827%2F20180827095359_6759372e9bd28026ee6f53b500fb4291_2.jpeg',
      ],
    };
  },
  mounted() {
    const images = document.querySelectorAll('img');

    const observerLazyLoad = new IntersectionObserver((entries) => {
      entries.forEach((item) => {
        if (item.isIntersecting) {
          item.target.src = item.target.dataset.imgUrl;
        }
      });
    });

    images.forEach((image) => {
      observerLazyLoad.observe(image);
    });

    const footer = document.querySelector('footer');

    const observerTouchBottom = new IntersectionObserver((entries) => {
      entries.forEach((item) => {
        if (item.isIntersecting) {
          setTimeout(() => {
            console.log('滾動到了底部,能夠發request請求數據了');
          }, 2000);
        }
      });
    });

    observerTouchBottom.observe(footer);

    const header = document.querySelector('header');
    const fixedTopReference = document.querySelector('.fixed-top-reference');
    fixedTopReference.style.top = `${header.offsetTop}px`;

    const observerFixedTop = new IntersectionObserver((entries) => {
      entries.forEach((item) => {
        if (item.boundingClientRect.top < 0) {
          header.classList.add('fixed');
        } else {
          header.classList.remove('fixed');
        }
      });
    });

    observerFixedTop.observe(fixedTopReference);
  },
};
</script>

<style lang="scss" scoped>
.fixed-top-helper {
  height: 1px;
  background: #ccc;
}
header {
  background: #ccc;
  &.fixed {
    position: fixed;
    top: 0;
    left: 0;
    width: 100%;
  }
}
main {
  img {
    display: block;
    height: 500px;
    margin: 30px;
  }
}
footer {
  background: #ccc;
}
</style>

基於Intersection的開源工具 Scrollama

官方提供了Basic Process,Progress,Sticky Side,Sticky Overlay幾種示例。異步

Basic Process

basic.gif

Progress

Progress.gif

Sticky Side

StickySide.gif

Sticky Overlay

StickyOverlay.gif

在項目中能夠當作適當引用。ide

項目地址:https://github.com/russellgol...
demo地址:https://russellgoldenberg.git...函數

總結

  • 主要使用IntersectionObserver實現懶加載圖片,觸底,吸頂
  • 雖然vue單文件組件版本,可是我相信聰明的你知道核心部分在哪裏
  • IntersectionObserver可能還會有其餘的用處,來日方長,慢慢探索

參考資料

https://developer.mozilla.org...
https://developer.mozilla.org...
https://developer.mozilla.org...
https://juejin.im/post/5ca15c...
https://medium.com/walmartlab...
https://juejin.im/post/5d6651...
https://github.com/russellgol...工具

原文地址:IntersectionObserver是什麼?post

相關文章
相關標籤/搜索