关于图片的开发总结

前言

设置图片有两种方法,一种是只用<img /> 标签,另一种是给div或者其他元素设置背景background-image。

前者可以理解为前景图,后者理解为背景图。

下面我们来对两者进行分析。

一:background-image方式

列出background-image的几个属性帮助理解:

  1. background-size: 设置整个背景图片的大小

    background-size: 100px 100px;

    是指把背景图片的大小设置为宽100px 高100px;

    background-size: cover/contain

    cover是指将图片按图片原本比例放大,让图片覆盖住div容器,多出来的部分裁剪。

    contain是指在保持原比例的情况下,尽量撑满容器的情况下不超出容器,所以这种情况下,如果容器宽高比和图片宽高比不一致的话,图片不会填满容器,容器会留白。

  2. background-position: 设置背景图片左上角相对于div左上角的位置

    (1)background-position: -10px -10px;

    是指以div左上角为(0,0)起始点,图片向左移动10px,向上移动10px。此时div的(0,0)就和背景图的(10,10)重合,所以在div内只能看到背景图(10,10)位置开始的图像

    反之 background-position: 10px 10px;

    是指以div左上角为(0,0)起始点,图片向右移动10px,向下移动10px。此时背景图的(0,0)就和div的(10,10)重合,所以背景图起始点在div的(10,10)位置,这时候div的(10,10)才出现背景图,(5,5),(0,0)等位置是没有背景图的

    (2) background-position:50% 0%

    positionX = (容器的宽度 - 背景图片宽度) * 百分比

    positionY = (容器的高度 - 背景图片高度) * 百分比

    (3) background-position:center top

    与background-position:50% 0% 效果一致。

    关键字:left或者top,bottom,center等值可以对应到百分比,和百分比的处理方式一致。

  3. background-attachment:scroll/local/fixed

    这个属性是在内容很多,出现滚动条的时候,用来确定背景图和内容之间的滚动关系的

    可以参考这个来理解:https://codepen.io/Chokcoco/pen/xJJorg

    scroll: 背景相对于元素本身固定,不随内容滚动。(默认值)

    local: 背景相对于元素内容固定,内容很多时,背景岁随着内容滚动。

    fixed: 背景相对于视口固定。即使一个元素拥有滚动机制,背景也不会随着元素的内容滚动。

二. <img> 和background-image区别

  1. 是否占位

    img是html标签 ,占位;background-image是css样式,不占位;

    但是使用img的时候如果我们希望宽度百分百,高度由图片宽高比例还确定的时候,就会出现高度不确定,导致图片在加载完之后,网页布局发生变化的情况。

    使用background-image的时候我们一般会将容器大小确定,所以图片是否加载完成不会影响容器大小,所以网页布局不会 变化。

  2. 加载顺序

    img是html元素,是网页结构的一部分,所以会在加载结构的过程中加载,而background-image是css样式,会等到结构加载完成(网页内容全部显示以后)才开始加载。所以img的图片会比background-image先加载。

三. 图片居中问题

第一种情况:图片本身比例不变(即不能让图片变形),让图片固定宽高比4:3的方式显示。

  1. 使用background-image很简单

    设置容器宽高比为4:3,再给图片设置

    1
    2
    3
    4
    5
    6
    .bg-img {
    width: 100%;
    height: 100%;
    background-size: cover;
    background-position: center center;
    }

    所以重点就在于怎么让容器比例为4:3。如果 容器宽度确定,那么直接写死宽度和高度就可以了。如果容器宽度不确定(根据屏幕宽度来决定),那么可以用这些方式来保证容器宽高比

    https://yangxiagithub.github.io/2018/03/18/%E5%85%B3%E4%BA%8Ecss%E7%9A%84%E4%B8%80%E4%BA%9B%E5%AD%A6%E4%B9%A0%E8%AE%B0%E5%BD%95/

    里面的第3点讲到了如何保证容器宽高比。

  2. 使用img

    1️⃣ 我们让img外层套一个容器div,这个容器是图片要显示的范围。首先比较容器宽高比boxRate和图片宽高比imgRate。(注意我使用的是宽高比,即宽/高)

    如果picRate > boxRate ,说明图片偏宽,那么让图片高度设置为容器高度,用此高度和picRate相乘得出来图片的宽度。这样图片宽度 就会大于容器宽度,再将图片宽度 宽出来的部分裁剪,就达到 将图片等比例扩大之后盖住容器的效果了,也就是cover的效果。

    反之,如果picRate < boxRate,说明图片偏高,那么让图片宽度等于容器宽度,用此宽度除以picRate来得到图片高度,这样得到的图片高度会比容器高度大,再将图片高出来的部分 裁剪 。

    2️⃣ 设置好图片宽度和高度之后 ,第二步是将图片 挪动到容器中间,让图片 显示中间部分,将两边多出来的部分裁剪掉。

    第一种方式:可以通过 计算图片宽度和高度来计算margin-left或者margin-top。

    代码如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    <img @load="adaptImg"></img>
    function adaptImg(event) {
    const currentTarget = event.currentTarget;
    // 使用jquery
    const picWidth = currentTarget.naturalWidth;
    const picHeigt = currentTarget.naturalHeight;
    const picRate = picWidth / picHeigt;
    const box = $(currentTarget).parent();
    const boxHeight = $(box).height();
    const boxWidth = $(box).width();
    const boxRate = boxWidth / boxHeight;
    // 不使用 jquery
    // const picWidth = currentTarget.naturalWidth;
    // const picHeigt = currentTarget.naturalHeight;
    // const picRate = picWidth / picHeigt;
    // const box = currentTarget.parentNode;
    // const boxHeight = box.clientHeight;
    // const boxWidth = box.clientWidth;
    // const boxRate = boxWidth / boxHeight;
    let finalWidth = 0;
    let finalHeight = 0;
    let marginLeft = 0;
    let marginTop = 0;
    if (picRate > boxRate) {
    // asHeight
    finalHeight = boxHeight;
    finalWidth = picRate * boxHeight;
    marginLeft = -(finalWidth - boxWidth) / 2;
    currentTarget.style.height = '100%';
    currentTarget.style.marginLeft = marginLeft + 'px';
    } else {
    // asWidth
    finalWidth = boxWidth;
    finalHeight = boxWidth / picRate;
    marginTop = -(finalHeight - boxHeight) / 2;
    currentTarget.style.width = '100%';
    currentTarget.style.marginTop = marginTop + 'px';
    }
    }

    第二种方式:可以通过position:absolute配合margin: auto; 再设置left,top,right,bottom4个值来达到图片和 容器居中的效果。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    .center-img-container {
    position: relative;
    overflow: hidden;
    }
    // 下面的是重点
    .center-img-container img {
    position: absolute;
    left: -9999px;
    right: -9999px;
    top: -9999px;
    bottom: -9999px;
    margin: auto;
    }
    // 当设置left: -9999px;right: -9999px;right: -9999px;top: -9999px; bottom: -9999px;
    // 四个数值要一样。记得margin要设置成auto
    // 当四个值都设置成-9999px作用于图片比容器大的时候。
    // 当四个值都设置成0作用于图片比容器小的时候。

    四. 图片模糊加载问题

    思路: 一开始的时候,将img的src赋值为一个尺寸比较小的图片地址,并在img元素上添加一个data-src属性(清晰图片的地址),当元素处于视图中时,创建一个新的图片元素,将此新图片元素src赋值为之前img的data-src值,当新建的图片加载完成的时候,对之前img的src进行替换,这样就将模糊图片替换成了清晰图片

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    export const lazyLoadImg = () => {
    // 滚动加载出来的元素可以懒加载
    $(window).on('scroll', function() {
    clearTimeout(loadInterval);
    var loadInterval = setTimeout(function() {
    $('.lazy-image').each((index, item) => {
    isElementInViewport(item) && loadImg(item);
    });
    }, 100);
    });

    $('.lazy-image').each((index, item) => {
    loadImg(item);
    });
    };

    function loadImg(item) {
    const $item = $(item);
    if ($item.data('status') === 'loading' || $item.data('status') === 'loaded') {
    return;
    }
    $item.data('status', 'loading');
    const img = new Image();
    const dataSrc = $item.attr('data-src');
    img.onload = () => {
    $item
    .attr('src', dataSrc)
    .removeClass('lazy-image')
    .data('status', 'loaded');
    };
    img.src = dataSrc;
    }

    function isElementInViewport(el) {
    var rect = el.getBoundingClientRect();
    var threshold_x = 0;
    var threshold_y = 100;
    return rect.top >= 0 - threshold_y && rect.left >= 0 - threshold_x && rect.top <= window.innerHeight + threshold_y && rect.left <= window.innerWidth + threshold_x;
    }