zoukankan      html  css  js  c++  java
  • 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

  • 相关阅读:
    纯js实现字符串formate方法
    C#实现json压缩和格式化
    简单的前端校验框架实现
    快速拷贝文件
    0012 移除元素
    0011 删除链表的倒数第N个节点
    0010 最长公共前缀
    0009 合并两个有序链表
    0008 合并K个排序链表
    0007 回文数
  • 原文地址:https://www.cnblogs.com/dreamless/p/8026656.html
Copyright © 2011-2022 走看看