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 重寫」辨色「小遊戲