一, 图片懒加载原理
浏览器是否发起请求图片是根据 < img > 中 src 的属性, 所以实现原理就是在图片没有进入可视区域的时候将图片链接放在 datasrc 中, 等到图片载入可视区域再给 src 赋值
二, 懒加载思路及实现
懒加载的实现这里提供的两种方法
- CSS
- <style>
- #App img{
- height: 500px; width:800px;margin: 10px auto;display: block;border: 1px solid #ccc;
- }
- @keyframes fadeIn{
- 0%{opacity: 0.1;}
- 100%{opacity: 1;}
- }
- </style>
- HTML
- <div id="app">
- <img class="01" datasrc="img/WechatIMG2.jpeg" alt="加载中" src="" />
- <img class="02" datasrc="img/WechatIMG3.jpeg" alt="加载中" src="" />
- <img class="03" datasrc="img/WechatIMG4.jpeg" alt="加载中" src="" />
- <img class="04" datasrc="img/WechatIMG5.jpeg" alt="加载中" src="" />
- <img class="05" datasrc="img/WechatIMG6.jpeg" alt="加载中" src="" />
- <img class="06" datasrc="img/WechatIMG7.jpeg" alt="加载中" src="" />
- </div>
2. JS 部分, 这里介绍两种方法
2.1. 传统的方法, 利用 Windows 和 document 的 API
2.1.1. 在实现具体方法前介绍几个 API 和优化性能的方法
浏览器可视窗口高度: Windows.innerHeight
滚动区域高度: document.body.scrollTop 和 document.documentElement.scrollTop
元素距离文档顶部距离: Element.offsetTop
函数节流:
事件触发后执行函数执行期间内事件再次触发, 执行函数将不会执行, 等规定时间之后事件触发, 执行函数方可再次执行.
函数防抖: 下文中未用到, 就和函数节流一起介绍了.
事件触发停止一定时间后才会执行响应的函数, 期间如果重复调用动作, 重新计算时间. 类似于, 按下一个弹簧, 只有你松手的时候弹簧才会弹起. 本质上是将多次操作合并为一次操作. 可用一个定时器维护, 事件触发后 wait 时间内如果事件重复触发, 那么取消当前定时器, 重新设置一个时延为 wait 的定时器.
2.1.2. 具体实现图片懒加载代码
- Windows.onload=function(){
- var imgs=document.querySelectorAll('img');
- lazyLoadImg(imgs)
- Windows.addEventListener('scroll',throttle(imgs,lazyLoadImg,200))
- }
- // 函数节流, 在规定时间内才执行一次图片加载函数, 解决耗费性能, 浏览器卡顿问题.
- function throttle(imgs,lazyLoadImg,wait){
- var lastTime=new Date().getTime()
- return function(){
- var curTime=new Date().getTime()
- if(curTime-lastTime<wait)return
- lastTime=curTime
- lazyLoadImg(imgs)
- }
- }
- // 图片懒加载
- function lazyLoadImg(imgs){
- // 浏览器可视窗口高度
- var windowActiveH=Windows.innerHeight;
- // 滚动区域高度
- var scrollTop=document.body.scrollTop||document.documentElement.scrollTop;
- for(var i=0;i<imgs.length;i++){
- // 如果满足图片可加载条件, 加载此图片
- if(isImgLoad(imgs[i],scrollTop,windowActiveH)){
- imgs[i].src=imgs[i].getAttribute('datasrc')
- imgs[i].style.cssText="animation:fadeIn .5s"// 类似 fadeIn 动画效果
- }
- }
- }
- // 是否满足图片加载条件
- function isImgLoad(imgItem,scrollTop,windowActiveH){
- //imgItem.offsetTop 图片距离文档顶部距离
- var isScrollBottom=imgItem.height+imgItem.offsetTop>scrollTop;// 满足从下向上滚动时显示图片的条件
- var isScrollTop=imgItem.offsetTop<scrollTop+windowActiveH// 满足从上向下滚动动显示图片的条件
- var isNoSrc=!imgItem.getAttribute("src")// 满足第一次加载图片条件
- if(isScrollTop && isScrollBottom && isNoSrc){
- return true
- }
- return false
- }
- 2.2.Intersection Observer API
提供了一种异步观察目标元素与祖先元素或顶级文档的交集中的变化的方法.
2.2.1.Intersection observer 概念和用法
概念
Intersection Observer API 允许你配置一个回调函数, 每当目标 (target) 元素与设备视窗或者其他指定元素发生交集的时候执行. 设备视窗或者其他元素我们称它为根元素或根 (root). 通常, 您需要关注文档最接近的可滚动祖先元素的交集更改, 如果元素不是可滚动元素的后代, 则默认为设备视窗. 如果要观察相对于根(root) 元素的交集, 请指定根 (root) 元素为 null.
目标 (target) 元素与根 (root) 元素之间的交叉度是交叉比 (intersection ratio). 这是目标(target) 元素相对于根 (root) 的交集百分比的表示, 它的取值在 0.0 和 1.0 之间.
用法, 创建一个 intersection observer
- var options = {
- root: document.querySelector('#scrollArea'),
- rootMargin: '0px',
- threshold: 1.0
- }
- var observer = new IntersectionObserver(callback, options);
参数 Intersection observer options
(1).root
指定根 (root) 元素, 用于检查目标的可见性. 必须是目标元素的父级元素. 如果未指定或者为 null, 则默认为浏览器视窗.
(2). rootMargin
root 元素的外边距. 类似于 CSS 中的 margin 属性, 比如 "10px 20px 30px 40px" (top, right, bottom, left). 如果有指定 root 参数, 则 rootMargin 也可以使用百分比来取值. 该属性值是用作 root 元素和 target 发生交集时候的计算交集的区域范围, 使用该属性可以控制 root 元素每一边的收缩或者扩张. 默认值为 0.
(3). threshold
可以是单一的 number 也可以是 number 数组, target 元素和 root 元素相交程度达到该值的时候 IntersectionObserver 注册的回调函数将会被执行. 如果你只是想要探测当 target 元素的在 root 元素中的可见性超过 50% 的时候, 你可以指定该属性值为 0.5. 如果你想要 target 元素在 root 元素的可见程度每多 25% 就执行一次回调, 那么你可以指定一个数组[0, 0.25, 0.5, 0.75, 1]. 默认值是 0(意味着只要有一个 target 像素出现在 root 元素中, 回调函数将会被执行). 该值为 1.0 含义是当 target 完全出现在 root 元素中时候 回调才会被执行.
Targeting an element to be observed
为每个观察者配置一个目标
- var target = document.querySelector('#listItem');
- observer.observe(target);
每当目标满足该 IntersectionObserver 指定的 threshold 值, 回调被调用.
只要目标满足为 IntersectionObserver 指定的阈值, 就会调用回调. 回调接收 IntersectionObserverEntry 对象和观察者的列表:
可去 https://developer.mozilla.org/zh-CN/docs/web/API/Intersection_Observer_API 自行查阅 Intersection Observer API
ok 话不多说, 咱们来看看这个新特性具体怎么实现图片懒加载的吧~
2.2.2. 具体实现图片懒加载代码
- Windows.onload=function(){
- // 这里 root 是浏览器视窗, target 元素是 img
- var options={
- root:null,// 指定根 (root) 元素, 用于检查目标的可见性. 必须是目标元素的父级元素. 如果未指定或者为 null, 则默认为浏览器视窗.
- rootMargin:'0px',//root 元素的外边距
- threshold:0.4//target 元素和 root 元素相交程度
- }
- var target=document.getElementsByTagName('img')
- var observer=new IntersectionObserver(callback,options)// 创建一个 IntersectionObserver 对象, 并传入相应参数和回调用函数
- for(var i=0;i<target.length;i++){
- observer.observe(target[i])// 为每个观察者配置一个目标
- }
- }
- function callback(entries,observer){
- for(var j=0;j<entries.length;j++){
- if(!entries[j].isIntersecting){//isIntersecting 为 true, 表示满足指定条件并显示在屏幕上
- continue;
- }
- var attriDatasrc=entries[j].target.attributes.datasrc.value
- var attriSrc=entries[j].target.attributes.src.value
- if(attriDatasrc && !attriSrc){
- var className=entries[j].target.className
- document.getElementsByClassName(className)[0].setAttribute("src",attriDatasrc)
- }
- }
- }
三, 总结
Intersection Observer API 缺点: 照比传统方法是浏览器新特性, 兼容性差一些. 只目前只兼容到 Chrome51.0 和 Android Webview51.0
Intersection Observer API 优点: 这种方式, 网站将不需要为了监听两个元素的交集变化而在主线程里面做任何操作, 并且浏览器可以帮助我们优化和管理两个元素的交集变化.
来源: http://www.jianshu.com/p/874fb2951c3d