vue
项目搭建参考《Webpack4 搭建 Vue 项目》前端
文档使用 vuepress
, 官方文档 https://vuepress.vuejs.orgvue
发布文档 github pages
+ gh-page
webpack
项目地址 https://github.com/zxpsuper/vui-vuegit
文档地址 https://zxpsuper.github.io/vui-vuegithub
处于自我摸索阶段,期待留下您的宝贵意见!web
图片懒加载的基本原理:npm
src
属性值offsetTop < innerHeight + scrollTop
时,即图片出如今窗口内部,此时修改 src
值为 data-src
的值var img = document.getElementsByTagName('img'); function lazyload() { //监听页面滚动事件 var seeHeight = window.innerHeight; //可见区域高度 var scrollTop = document.documentElement.scrollTop || document.body.scrollTop; //滚动条距离顶部高度 for (var i = 0; i < img.length; i++) { if (img[i].getAttribute('data-image-show')) continue; // 若是已经加载完成,则不需走下面流程 if (img[i].offsetTop < seeHeight + scrollTop) { console.log(img[i].offsetTop, seeHeight, scrollTop); if (img[i].getAttribute('src') == Vue.$vuiLazyLoad.img) { img[i].src = img[i].getAttribute('data-src'); img[i].setAttribute('data-image-show', 'true'); // 给个标识,表示已经加载完成 } } } }
滚动监听,不断滚动便会不断触发滚动监听的函数,影响性能,所以在此须要加入一个防抖函数canvas
// 防抖函数 function throttle(event, time) { let timer = null; return function(...args) { if (!timer) { timer = setTimeout(() => { timer = null; event.apply(this, args); }, time); } }; }
添加监听和去除监听segmentfault
window.removeEventListener('scroll', throttle(lazyload, 800)); window.addEventListener('scroll', throttle(lazyload, 800));
这里用到了自定义指令中的三个钩子函数 bind,inserted,unbind
, 咱们要让指令中占位图可修改,所以写成函数形式app
const lazyload = function(Vue) { var img = document.getElementsByTagName('img'); function lazyload() { //监听页面滚动事件 var seeHeight = window.innerHeight; //可见区域高度 var scrollTop = document.documentElement.scrollTop || document.body.scrollTop; //滚动条距离顶部高度 for (var i = 0; i < img.length; i++) { if (img[i].getAttribute('data-image-show')) continue; // 若是已经加载完成,则不需走下面流程 if (img[i].offsetTop < seeHeight + scrollTop) { console.log(img[i].offsetTop, seeHeight, scrollTop); if (img[i].getAttribute('src') == Vue.$vuiLazyLoad.img) { img[i].src = img[i].getAttribute('data-src'); img[i].setAttribute('data-image-show', 'true'); // 给个标识,表示已经加载完成 } } } } // vui中的默认配置,用户可经过修改 Vue.$vuiLazyLoad.img 进行修改占位图 Vue.$vuiLazyLoad = { img: 'https://github.com/zxpsuper/Demo/blob/master/images/avatar.jpg?raw=true', imgLength: 0, // 懒加载的图片数量,当数量为 0 的时候移除滚动监听 }; lazyload(); //页面载入完毕加载但是区域内的图片 window.removeEventListener('scroll', throttle(lazyload, 800)); window.addEventListener('scroll', throttle(lazyload, 800)); return { name: 'lazy', // 绑定 bind(el, binding) { el.setAttribute('src', Vue.$vuiLazyLoad.img); el.setAttribute('data-src', binding.value); Vue.$vuiLazyLoad.imgLength++; }, // 插入 inserted(el) { // 暂时空 }, // 解绑 unbind() { Vue.$vuiLazyLoad.imgLength--; // 每次解绑,自减 if (!Vue.$vuiLazyLoad.imgLength) window.removeEventListener('scroll', throttle(lazyload, 800)); }, }; }
IntersectionObserver
IntersectionObserver接口(从属于Intersection Observer API)为开发者提供了一种能够异步监听目标元素与其祖先或视窗(viewport)交叉状态的手段。
观察元素是否与视窗交叉,如果则修改 scr
为 data-src
值,并解除观察状态,固然这一切的前提是你在图片建立的时候观察图片自己,所以在图片插入时的钩子函数内
inserted(el) { if (IntersectionObserver) lazyImageObserver.observe(el); },
具体使用方法:
let lazyImageObserver if (IntersectionObserver) { lazyImageObserver = new IntersectionObserver((entries, observer) => { entries.forEach((entry, index) => { let lazyImage = entry.target; // 若是元素可见 if (entry.intersectionRatio > 0) { if (lazyImage.getAttribute('src') == Vue.$vuiLazyLoad.img) { lazyImage.src = lazyImage.getAtstribute('data-src'); } lazyImageObserver.unobserve(lazyImage); // 解除观察 } }); }); }
固然咱们优先使用 IntersectionObserver
, 若不支持则使用传统方法
import Vue from 'vue' Vue.directive('lazy', lazyLoad(Vue));
const lazyLoad = function(Vue) { var img = document.getElementsByTagName('img'); function lazyload() { //监听页面滚动事件 var seeHeight = window.innerHeight; //可见区域高度 var scrollTop = document.documentElement.scrollTop || document.body.scrollTop; //滚动条距离顶部高度 for (var i = 0; i < img.length; i++) { if (img[i].getAttribute('data-image-show')) continue; console.log(i); if (img[i].offsetTop < seeHeight + scrollTop) { console.log(img[i].offsetTop, seeHeight, scrollTop); if (img[i].getAttribute('src') == Vue.$vuiLazyLoad.img) { img[i].src = img[i].getAttribute('data-src'); img[i].setAttribute('data-image-show', 'true'); } } } } Vue.$vuiLazyLoad = { img: 'https://github.com/zxpsuper/Demo/blob/master/images/avatar.jpg?raw=true', imgLength: 0, }; var lazyImageObserver; if (IntersectionObserver) { lazyImageObserver = new IntersectionObserver((entries, observer) => { entries.forEach((entry, index) => { let lazyImage = entry.target; // 若是元素可见 if (entry.intersectionRatio > 0) { if (lazyImage.getAttribute('src') == Vue.$vuiLazyLoad.img) { lazyImage.src = lazyImage.getAttribute('data-src'); } lazyImageObserver.unobserve(lazyImage); } }); }); } else { lazyload(); //页面载入完毕加载但是区域内的图片 window.removeEventListener('scroll', throttle(lazyload, 800)); window.addEventListener('scroll', throttle(lazyload, 800)); } return { name: 'lazy', bind(el, binding) { el.setAttribute('src', Vue.$vuiLazyLoad.img); el.setAttribute('data-src', binding.value); Vue.$vuiLazyLoad.imgLength++; }, inserted(el) { if (IntersectionObserver) lazyImageObserver.observe(el); }, unbind() { Vue.$vuiLazyLoad.imgLength--; if (!Vue.$vuiLazyLoad.imgLength) window.removeEventListener('scroll', throttle(lazyload, 800)); }, }; }; export default lazyLoad; function throttle(event, time) { let timer = null; return function(...args) { if (!timer) { timer = setTimeout(() => { timer = null; event.apply(this, args); }, time); } }; }
本文是对vue自定义指令及懒加载原理的综合实现,如有错误,望指出共同进步。
Canvas 进阶(二)写一个生成带logo的二维码npm插件
Canvas 进阶(三)ts + canvas 重写”辨色“小游戏