• HTML & CSS – 实战 RWD Image 响应式图片


    前言

    之前写过 HTML & CSS – Responsive Image 响应式图片 (完整版), 里面解释了原理和一些具体做法, 但是并不是以真实场景作为例子带入.

    由于 RWD Image 挺复杂, 所以特地再写一篇以实战的角度来解释.

    建议先阅读之前的相关文章:

    CSS – RWD (Responsive Web Design) 概念篇

    CSS – 屏幕, 打印, 分辨率, 物理像素, 逻辑像素, Retina, DPI, PPI 是什么?

    HTML & CSS – Responsive Image 响应式图片 (完整版)

    屏幕 Resolution Research

    RWD 的要求是在不同的屏幕尺寸下自适应设计. 那么第一步就是要先搞清楚, 到底屏幕尺寸有哪些.

    市场虽然已经有了 standard 的 Breakpoint (Bootstrap 和 Tailwind CSS 各一个版本), 但是这种全世界通用的尺寸, 不一定适合所有项目.

    更何况这个 Breakpoint 是 for RWD Design 而不是 RWD Image.

    如果网站已经有访问数据, 那么可以直接从 Google Analytics 里面查看, 用户使用的设备尺寸. 如果网站还没有数据, 那么可以通过 statcounter 查看

    电脑屏幕 resolution: 

    新加坡:

    马来西亚:

    1920 x 1080 是 desktop 用的屏幕. DPR (device pixel ratio) = 1

    1280 x 720   是 laptop 1920 x 1080 的屏幕但是 display resolution 调成了 150%. 所以它的 DPR = 1.5 (1280 是逻辑像素, 1920 是物理像素)

    1536 x 864   是 laptop 1920 x 1080 的屏幕但是 display resolution 调成了 125%. 所以它的 DPR = 1.25 (1536 是逻辑像素, 1920 是物理像素)

    1366 x 768   是 laptop 1366 x 768 的屏幕, 这个是廉价屏幕, 所以它物理像素本来就很少 (不是高清)

    1440 x 900   是 MacBook 苹果电脑 2880 x 1800 的屏幕但是 display resolution 调成了 200%. 所以它的 DPR = 2 (1440 是逻辑像素, 2880 是物理像素) 

    2560 x 1440 我不清楚, 1600 x 900 是廉价的 desktop 屏幕 (这 2 个太少了, 我就不打算支持了)

    手机屏幕 resolution: 

    新加坡:

    马拉西亚

    414 x 896 是 iPhone (XR, XS Max, 11, 11 Pro Max), DPR = 2 和 3 都有 (看机型)

    390 x 844 是 iPhone (12, 12 Pro), DPR = 3

    375 x 812 是 iPhone (X, 11 Pro), DPR = 3

    428 x 926 是 iPhone 12 Pro Max, DPR = 3

    360 x 800 是 LG, Samsung, Hua Wei, DPR = 2, 3 ,4

    360 x 780 是 iPhone 12 mini 和各种 Android 机, DPR = 2, 3, 4

    393 x 873 是 Xiaomi, DRP = 2.75

    平板屏幕 resolution:

    我只关注 iPad

    768 x 1024 是 iPad 6th gen portrait, DPR = 2

    810 x 1080 是 iPad 7th gen portrait (到目前的 9th 都是), DPR = 2

    1024 x 768 是 iPad 6th gen landscape, DPR = 2

    1080 x 810 是 iPad 7th gen landscape (到目前的 9th 都是), DPR = 2

    尺寸资料来源

    尺寸资料是从这里这里这里获取的.

    Intrinsic Size & Rendered Size

    图片有两个尺寸, 一个是 Intrinsic size (固有尺寸), 另一个是 Rendered size (显示尺寸).

    使用 Chrome dev tool 就可以看见了

     

    Intrinsic size 指的是这张图的 physical dimensions pixel (物理像素). 它不会受 CSS style 影响

    Rendered size 指的是这张图最终显示出来的 dimension pixel (逻辑像素). 它会受 CSS style 影响

    例子说明: 

    <img src="https://via.placeholder.com/3200x1800" />

    一张 3200 x 1800 的图放到 img 里, width heght 默认是 auto

    这时 intrinsic 和 rendered size 都是一样的, 3200 x 1800, 因为 width height auto 的意思是显示完整的图片.

    但如果加上 CSS 300px

    intrinsic size 依然是 3200 x 1800 (它不受 CSS 影响), 但是 rendered size 就变成了 300 x 168.75.

    因为 CSS 声明了 width 是 300px 而 height auto 则表示图片按照原始比例缩小, height 就变成了 168.75

    按比例缩小的算法是 new height = new width * old height / old width (我的背法是 新的 乘于对角 然后 除)

    The Problem

    intrinsic 大于 rendered 就表示浪费了带宽. (SEO performance 会扣分)

    intrinsic 小于 rendered 就表示图片太小, 图片会变蒙 (失真)

    除了上面这种同比例缩小的情况, 不同比例也会导致 intrinsic != rendered size

    举例: 

    原图 320 x 180

    CSS 160px; height: 180px; object-fit: cover;

    最终图片只显示了 horizontal 中间的部分. 

    rendered size: 160 x 180 (160px 的 width 浪费了)

    The Solution

    RWD Image 要解决的问题就是这些. 它的思路很简单, 就是通过 media query 去判断当前屏幕.

    然后换一张合适的图片, 尽可能让图片 intrinsic 和 rendered 一致, 既不失真也不浪费带宽

    有几个维护成本需要被考虑: 

    1. 这个方案是拿空间换时间, 准备多张不同尺寸的图片并不容易. 而且浪费 disk space.

    2. RWD Image 是不依赖 CSS 渲染的, 也就是说, 虽然 rendered size 是 CSS 搞出来的. 但是当我们在做 RWD Image 时却无法依赖 CSS

    需要直接给出最终的数字. CSS 可以写 100%, 但 RWD 不能写 100%, 它只能依据不同尺寸的屏幕 hardcode 写 rendered 多少 px.

    这是因为 browser 在处理 img srcset 或者 picture source srcset 时是没有去解析或依赖 CSS style 的.

    简单案例 img + srcset + size

    HTML

    <header>Header</header>
    <main>
      <img src="https://via.placeholder.com/3200x1800" />
      <img src="https://via.placeholder.com/3200x1800" />
    </main>

    CSS Style

    header {
      font-size: 2rem;
      padding-block: 1rem;
      text-align: center;
      background-color: crimson;
      color: white;
    }
    main {
      display: grid;
      gap: 1rem;
      grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
      img {
        max-width: 100%;
      }
    }

    header 只是为了美而已.

    效果

    中间的 2 张图, 在不同的屏幕尺寸下, 它们的 dimension 都不一样. 

    每一个 rendered size 和 intrinsic size 也都不一致.

    图片准备

    我挑出几个 size 来准备 for example 就好.

    Android (360w, DPR: 2, 3, 4)

    rendered size 最好的获取方式是用 dev tool 去查看 (如果 CSS 很简单的话, 也可以自己算)

    rendered size = 360 x 202.5

    2x = 720 x 406 (注: 这里微差的算法因人而异, 我的做法是 202.5 ceil to 203 then x2 = 406)

    3x = 1080 x 609 (注: 203 x 3 = 609)

    4x = 1440 x 812

    iPhone (414w, DRP: 2, 3)

    rendered size = 414 x 232.88

    2x = 828 x 466

    3x = 1242 x 699

    iPad (810w, DPR: 2)

    rendered size = 397 x 223.31

    2x = 794 x 448

    图片的比例都是 16:9. 一共 6 张图 720, 1080, 1440, 828, 1242, 794.

    srcset + size

    所有图片的比例都是一样的 16:9 只是 dimension 不同, 这个属于 Resolution switching: Different sizes and different resolutions, 不是 Art direction 所以不需要用 picture element 也可以解决.

    <img
      srcset="
        https://via.placeholder.com/720x406    720w,
        https://via.placeholder.com/1080x609  1080w,
        https://via.placeholder.com/1440x812  1440w,
        https://via.placeholder.com/828x466    828w,
        https://via.placeholder.com/1242x699  1242w,
        https://via.placeholder.com/794x448    794w,
        https://via.placeholder.com/3200x1800 3200w
      "
      sizes="(max- 360px) 360px, (max- 414px) 414px, (max- 810px) 397px, 3200px"
      src="https://via.placeholder.com/3200x1800"
      width="3200"
      height="1800"
    />

    srcset 把所有图片列出来. 720w 是 phisical pixel.

    sizes 的匹配顺序是 if else 哦, 第一个进到了就不会往下走了, 所以合理的匹配方式是使用 max-width 也就是 <= 360px, <=414px, <=810px 

    (max- 810px) 397px 的意思是当 viewport <= 810px 的时候, 图片的 rendered size 是 397px. 

    810 是 iPad 所以它的 RDP 是 2, rendered size 397px x 2 = 794px. 所以最终会去 srcset 里面找 794px 的图作为显示.

    width dynamic 写法

    上面这种写法是完全 hardcode 的, 当 media 是多少, image rendered size 是多少 px

    其实也可以写成 dynamic 的 比如

    <img
      sizes="(max- 414px) 100vw, (max- 810px) calc((100vw - 1rem) / 2), 3200px"
    />

    这个和上面是完全等价的效果. 

    当 viewport 414px 时, width 时 100vw

    当 viewport 810px 时, width = vw 扣掉中间的 gutter (1rem) 然后 50% 就是 image rendered size

    注意 : 不能写 % 哦

    一个比较好的管理方式是, follow RWD 的 Breakpoint 然后里面写 dynamic 算法 depend on vw. 然后准备几张图. 大概一个间隔就可以了.

    虽然这做法无法到达 100% rendered size 和 intrinsic 一致, 但是 trade-off 到很合理. 维护也比较容易.

    具体做法是这样的, 把所有 rendered size 列出来, 然后每间隔 100px 就保留一张图, 然后 media <= 639 就 100vw.

    复杂案例 picture > source

  • 相关阅读:
    分布式配置中心选型
    springboot中后端返回数据给前端的设计模式
    java中泛型在静态方法中的使用
    java在开发中DO、DTO、BO、AO、VO、POJO定义
    linux中find命令的使用详解(转载)
    Centos7配置ssh免密登录群发
    关于oracle PL/SQL存储过程 PLS-00905 object is invalid,statement ignored问题的解决
    Django-ModelFrom中修改save后的字段值
    MongoDB的集群模式--Sharding(分片)
    MongoDB用户和密码登录
  • 原文地址:https://www.cnblogs.com/keatkeat/p/16194819.html
Copyright © 2020-2023  润新知