lasy load图片的实现

无意中看到了这篇关于使用LQIP(Low Quality Image Placeholders) 原文链接,方案实现图片加载优化方案。在此实践一把。

1. 方案实现

  • 页面初始化时,img元素初始化时,src使用低质量的图片,显示出图片的大概轮廓
  • 页面滚动到当前图片位置,后台启动加载原图
  • 原图加载完成,替换掉之前的src显示出原图

监听页面是否滚动到图片位置使用的IntersectionObserver,减少使用scroll过程造成的页面卡顿。

2. 代码结构

index.tmpl

<!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.js

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.js

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

dealPic('.big-pic')

 

各文件关系是:

  • index.tmpl是HTML模板,编译之后生成index.html
  • lib/index.js 用于监听元素是否达到了页面位置
  • index.js用于生成bundle.js

 

3. 构建的实现

首先需要安装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

 

posted @ 2017-12-12 10:37 无梦灬 阅读(...) 评论(...) 编辑 收藏