lasy load圖片的實現

無心中看到了這篇關於使用LQIP(Low Quality Image Placeholders) 原文連接,方案實現圖片加載優化方案。在此實踐一把。javascript

1. 方案實現html

  • 頁面初始化時,img元素初始化時,src使用低質量的圖片,顯示出圖片的大概輪廓
  • 頁面滾動到當前圖片位置,後臺啓動加載原圖
  • 原圖加載完成,替換掉以前的src顯示出原圖

監聽頁面是否滾動到圖片位置使用的IntersectionObserver,減小使用scroll過程形成的頁面卡頓。java

2. 代碼結構node

index.tmplgit

<!DOCTYPE html>
<html>

<head>
  <meta charset="utf-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <title>lasyload and LQIP</title>
  <meta name="viewport" id="viewport" content="width=device-width,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no">
  <link rel="shortcut icon" href="/favicon.ico">
  <meta name="apple-mobile-web-app-capable" content="yes">
  <meta name="apple-mobile-web-app-status-bar-style" content="black">
  <meta name="format-detection" content="telephone=no">
</head>

<body>
  <p>You may need to install go and Primitive</p>
  <p>
    This is long article.
    <br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/>
    <img src="./images/1.jpg" class="big-pic" alt="">
    <br/><br/><br/>
    next line
    <br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/>
    <img src="./images/2.jpg" class="big-pic" alt="">
    <br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/>
    3th picture
    <br/>
    <br/>
    <img src="./images/3.jpg" class="big-pic" alt="">

  </p>
  <script type='text/javascript' src='./bundle.js'></script>
</body>

</html>

lib/index.jsgithub

function replaceSrc (changes) {
  changes.forEach(change => {
    if (change.intersectionRatio <= 0) return
    let item = change.target

    let src = item.getAttribute('data-src')
    let img = new Image()
    img.onload = function () {
      item.setAttribute('src', src)
    }
    img.src = src

    // observer.unobserve(item)
  })
}

module.exports = function (selector) {
  let els = document.querySelectorAll(selector)

  let observer = new IntersectionObserver(replaceSrc.bind());
  [].forEach.call(els, (item) => {
    observer.observe(item)
  })
}

index.jsgolang

let dealPic = require('../lib/index')

dealPic('.big-pic')

 

各文件關係是:web

  • index.tmpl是HTML模板,編譯以後生成index.html
  • lib/index.js 用於監聽元素是否達到了頁面位置
  • index.js用於生成bundle.js

 

3. 構建的實現npm

首先須要安裝go和primitive. primitive庫: https://github.com/fogleman/primitive服務器

安裝primitive時有些網站被牆了,因此要下載安裝包安裝。網上搜索到了一個第三方下載地址:https://www.golangtc.com/download/package

按提示下載解壓至go目錄下的src,而後執行

 go install github.com/fogleman/primitive

安裝完成環境。

安裝npm庫依賴包:

npm i glob sqip browserify

使用glob檢索全部的圖片,sqip用於轉換圖片爲最小格式的svg,browserify爲了使用require模塊。

使用正則檢測出html模板中全部的img, 匹配已經轉爲svg的文件,文件相同,使用base64格式替換掉原src,新加data-src爲原src, 設置width height等。最後輸出文件爲index.html

構建的代碼以下:

const sqip = require('sqip')
const glob = require('glob')
const path = require('path')
const fs = require('fs')

function getSvgList (folder) {
  return new Promise((resolve, reject) => {
    glob(folder + '/**/*.jpg', {}, function (err, files) {
      if (err) {
        reject(err)
      }
      let list = []
      files.forEach(file => {
        const result = sqip({
          filename: file,
          numberOfPrimitives: 10,
        })
        list.push({
          file: path.join(__dirname, file),
          result,
        })
      })
      resolve(list)
    })
  })
}

function replaceHtml (html, list) {
  if (!path.isAbsolute(html)) {
    html = path.join(__dirname, html)
  }
  let str = fs.readFileSync(html, 'utf-8')
  let htmlPath = path.dirname(html)
  const REG = /(<img .*?src=\")(.*?)\"( .*?>)/g

  let imgSrc = REG.exec(str)

  while (imgSrc) {
    let src = imgSrc[2]
    let file
    if (path.isAbsolute(src)) {
      file = path.join(__dirname, src)
    } else {
      file = path.join(htmlPath, src)
    }
    list.forEach(item => {
      if (item.file === file) {
        var imgInfo = item.result.img_dimensions
        str = str.replace(imgSrc[0], function (str) {
          return imgSrc[1] + 'data:image/svg+xml;base64,' + item.result.svg_base64encoded + `" data-width="${imgInfo.width}" data-height="${imgInfo.height}"`
            + ` data-src="${src}"` + imgSrc[3]
        })
      }
    })

    imgSrc = REG.exec(str)
  }

  fs.writeFileSync('./example/index.html', str)
}

getSvgList('./example/images/')
  .then(list => {
    replaceHtml('./example/index.tmpl', list)
  })

 

4. 執行構建並檢測結果

在npm中加入scripts:

{
    "build": "npm run sqip & browserify example/index.js -o example/bundle.js",
    "sqip": "node sqip.js"
}

執行build後,example文件目錄下會生成index.html。開啓web服務器(我是使用的本身寫的xmocker),訪問example/index.html,使用throttle進行查看效果。

5. 總結

方案只是針對普通Html,對於其餘模塊,應該有現成的方案。訪問體驗確實好了不少。

 

附:代碼地址 https://github.com/wenlonghuo/code-test/tree/master/001_lasyload

相關文章
相關標籤/搜索