• 多图片优化方式总结


    图片懒加载

    图片懒加载,长列表最大的问题就是图片太多,如果一次性把图片全部请求了,那么页面渲染速度会很慢,如果用户点不到,还会造成很大的浪费,甚至会有性能瓶颈。

    为什么要使用懒加载呢?为了加速页面的加载速度,减少不必要的请求,可以将未出现在可视区域的图片暂不加载,等到滚动到可视区域后再去加载。这样提升了性能和提高了用户体验。

    实现原理:初始状态,所有图片都有一个默认的 src, 指向本地的一个 默认图片 default.png , 并且把img的真实的地址放在 data-src上。当滚动时,判断元素是否在可视区域,如果在可视区域,那么再把 data-src 上的值写入真正的 src 中。

    实现方法

    判断元素是否在可视区域的方法

     方法一

          通过 document.documentElement.clientHeight 获取屏幕可视窗口高度

          通过 element.offsetTop 获取元素相对于文档顶部的距离

          通过 document.documentElement.scrollTop 获取浏览器窗口顶部与文档顶部之间的距离,也就是滚动条滚动的距离

        然后判断 offsetTop - scrollTop < clientHeight ,代表在可视区。

     方法二

       通过getBoundingClientRect()方法来获取元素的大小以及位置

    The Element.getBoundingClientRect() method returns the size of an element and its position relative to the viewport.

    这个方法返回一个名为ClientRectDOMRect对象,包含了toprightbottonleftwidthheight这些值。

    假设const bound = el.getBoundingClientRect();来表示图片到可视区域顶部距离;
    并设 const clientHeight = window.innerHeight;来表示可视区域的高度。

    随着滚动条的向下滚动,bound.top会越来越小,也就是图片到可视区域顶部的距离越来越小,当bound.top===clientHeight时,图片的上沿应该是位于可视区域下沿的位置的临界点,再滚动一点点,图片就会进入可视区域。

    也就是说,在bound.top<=clientHeight时,图片是在可视区域内的。

    function isInSight(el) {
        const bound = el.getBoundingClientRect();
        const clientHeight = window.innerHeight;
        //如果只考虑向下滚动加载
        //const clientWidth = window.innerWeight;
        return bound.top <= clientHeight + 100; // +100是为了提前加载。
    }

    特别简单的一个实例

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
    <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
    <head>
    <style type="text/css">
    *{margin:0;padding: 0;}
    img {display: block;width: 100%;height: auto;}
    </style>
    </head>
    <body>
    <img src="img/default.png" data-src="img/img1.png">
    <img src="img/default.png" data-src="img/img2.png">
    <img src="img/default.png" data-src="img/img3.png">
    <img src="img/default.png" data-src="img/img4.png">
    <img src="img/default.png" data-src="img/img5.png">
    <img src="img/default.png" data-src="img/img6.png">
    <img src="img/default.png" data-src="img/img7.png">
    <img src="img/default.png" data-src="img/img8.png">
    <img src="img/default.png" data-src="img/img9.png">
    <img src="img/default.png" data-src="img/img10.png">
    <script type="text/javascript"> 
        var imgs  = document.querySelectorAll('img'),len = imgs.length; 
        var n = 0;//存储图片加载到的位置,避免每次都从第一张图片开始遍历  
        window.onscroll = function() { 
            var seeHeight = document.documentElement.clientHeight; 
            var scrollTop = document.body.scrollTop || document.documentElement.scrollTop; 
            for (let i = n; i < len; i++) { 
                if(imgs[i].offsetTop < seeHeight + scrollTop) {
                     if (imgs[i].getAttribute('src') == 'img/default.png'){ 
                       imgs[i].src = imgs[i].getAttribute('data-src'); 
                    }
                     n = i + 1; 
                    console.log('n = ' + n); 
                }
            } 
        }; 
    </script> 
    </body>
    </html>

    函数节流

    在类似于滚动条滚动等频繁的DOM操作时,总会提到“函数节流、函数去抖”。

    所谓的函数节流,也就是让一个函数不要执行的太频繁,减少一些过快的调用来节流。

    基本步骤:

    1. 获取第一次触发事件的时间戳
    2. 获取第二次触发事件的时间戳
    3. 时间差如果大于某个阈值就执行事件,然后重置第一个时间
    function throttle(fn, mustRun = 500) {
      const timer = null;
      let previous = null;
      return function() {
        const now = new Date();
        const context = this;
        const args = arguments;
        if (!previous){
          previous = now;
        }
        const remaining = now - previous;
        if (mustRun && remaining >= mustRun) {
          fn.apply(context, args);
          previous = now;
        }
      }
    }

    这里的mustRun就是调用函数的时间间隔,无论多么频繁的调用fn,只有remaining>=mustRunfn才能被执行。

    简单封装

    const checkVisible = (ele) => {
      let scrollTop = document.documentElement.scrollTop || document.body.scrollTop
      let clientHeight = document.documentElement.clientHeight
      return ele.offsetTop - scrollTop < clientHeight
    }
    
    let imgs = document.getElementsByTagName("img"), nums = imgs.length
    
    const lazyLoad = () => {
      for (let i = 0; i < nums; i++) {
        if (checkVisible(imgs[i]) && imgs[i].getAttribute("src") === "default.png") {
          imgs[i].src = imgs[i].getAttribute("data-src")
        }
      }
    }
    
    function throttle(fn, mustRun = 500) {
      const timer = null;
      let previous = null;
      return function() {
        const now = new Date();
        const context = this;
        const args = arguments;
        if (!previous){
          previous = now;
        }
        const remaining = now - previous;
        if (mustRun && remaining >= mustRun) {
          fn.apply(context, args);
          previous = now;
        }
      }
    }

    从图片本身优化

    一般问题出在哪,就得追本溯源。目前我们能够用到的有以下几种方法。

    1. 更换图片格式;
    2. 通过特别的方式在保持清晰度的条件下来压缩图片,目前常用的是在https://tinypng.com/该网站压缩图片,一般压缩率很高;
    3. 由 png,jpg,jpeg,gif 转换为更好的 webp。具体webp详看(https://www.zhihu.com/question/27201061

    WebP 的优势在与它更好的图像数据压缩算法,能够将图片转换为更小的体积,具有无损和有损的压缩模式。如果是选择了有损压缩,也拥有肉眼无法识别差异的图像质量。虽然它在页面渲染的时候浏览器比jpg会花稍长的时间解析它的算法,但是权衡它所带来的体积的减少来看,WebP 还是最优秀的。目前Google、Facebook、腾讯、阿里、美团的等国内外互联网公司广泛应用了webp,超过70%的浏览器已经支持webp,Safari和Foxmail也在进行支持webp的测试。

    面临困难:

    1. 目前并不是所有浏览器都支持WebP,因此需要解决浏览器适配问题。
    2. 对于已上线运营的网站,采用WebP需要替换大量图片,工作量太大。

    针对 WebP 兼容性问题,可以从两个方面入手

    1.从服务端考虑:
    如果浏览器支持 WebP ,那么会在 request header accept里,发送image/webp, 服务器收到以后根据这个来去返回给客户端图片。请求头里有,则发送webp的,如果没有,就发送普通格式的图片。

    不过这个对服务器要求比较高,并且现在图片大多放在 CDN 上,CDN去做这种策略可能会稍微麻烦点。

    2.从前端考虑:
    前端去检测浏览器是否支持 WebP, 支持就发送 WebP 的图片请求,不支持就发送 jpg、png等。下面是一行代码判断是否支持WebP。

    var isSupportWebp = !![].map && document.createElement('canvas').toDataURL('image/webp').indexOf('data:image/webp') == 0;

    !![].map主要是判断是否是IE9+,以免toDataURL方法会挂掉。如果你直接对数组原型扩展了map方法,则需要使用!![].map以外的方法进行判断,例如!!window.addEventListener等。

    参考链接地址:https://github.com/axuebin/react-blog/issues/1

  • 相关阅读:
    从零开始学SQLSERVER-UNION
    从零开始学SQLSERVER-BETWEEN
    从零开始学SQLSERVER-LIKE
    从零开始学SQLSERVER-存储过程(基础用法)
    从零开始学SQLSERVER-TOP
    从零开始学SQLSERER-INNER JOIN
    从零开始学SQLSERVER-DELECT(删除)
    从零开始学SQLSERVER-ORDER BY(排序)
    从零开始学SQLSERVER-WHERE
    使用 C# 9 的records作为强类型ID
  • 原文地址:https://www.cnblogs.com/zuobaiquan01/p/8649184.html
Copyright © 2020-2023  润新知