响应式SPRITES图片实现方法探究

腾讯云正在走向全面支持响应式和Retina屏的道路上,我们广泛使用了SVG Sprites技术。在实践过程出现了“某些元素的背景图片来自于一张SVG Sprites,同时该元素也需要支持响应式”的情况,就需要解决“在元素尺寸发生变化时,来自Sprites的这张背景图片如何同步等比缩放?“的问题。

我曾经探讨过“响应式图像”的实现方法,当然这些方法都不适用背景图。我们来分解下这个问题,具体要完成哪些任务?

HTML元素在缩放时,如何保持宽高比不变?

对于这个问题,我在“使用PADDING-TOP:(PERCENTAGE)实现响应式背景图片”这篇文章中给出了解决方法,不再赘述。

See the Pen responsive image sprites 1 by wenjul (@wenjul) on CodePen.

Sprites中的背景图片如何跟随容器尺寸的变化而等比缩放?

background-size正是用来解决这个问题的。大家应该比较熟悉关键字“cover“(缩放背景图片以完全覆盖背景定位区,可能背景图片部分看不见)和”contain“(缩放背景图片以完全装入背景定位区,可能背景定位区部分空白)的效果,这两个关键字主要解决整张背景图如何适应容器,比较适用于单张图片,不适用于Sprites图。

如何计算background-size: <percentage>?

百分比值(<percentage>)是背景图相对于背景定位区(background positioning area)的百分比,可以控制在容器元素内仅显示Sprites图的部分内容。比如下图中,Sprites图是由四张头像拼成的,要想在容器内仅显示第一张头像,background-size的值应该多少呢?

demo-1

先看下100%的效果,也就是说Sprites图和容器是1:1的比例,显然不是我们要的效果。

demo-2

我们仅需要Sprites图的1/4显示在容器内,那么Sprites图与容器的比例应该是4:1,计算公式为:

background-size : ( Sprites width / image width) (Sprites height / image height)

demo-3

上面这个公式比较容易理解,但是如何在容器元素显示其他的头像呢?

当然是通过background-position来改变背景图片的初始位置。我们通常使用<length>(如px)控制背景图片相对于容器元素的偏移量,显然不适用我们这种情况,因为整张Sprites图片是可以等比缩放的,那么单张图片的左上角相对于Sprites图片的左上角的距离也是变化的,所以使用<length>是会出错的,如下例。

demo-4

如何计算background-position: <percentage>?

background-position值还可以使用 <percentage>和关键词(top, bottom, left, right, center),可以把关键词理解为<percentage>特殊形式,两者的计算方式是相同的。但是,<percentage>和<length>的计算方式是不同的:<length>的参照点是图片的左上角,<percentage>的参照点是随着取值变化的。

background-position:30px 40px; 为例,计算方法为:Sprites图的左上角相对于容器元素的左上角向右偏移30px,向下偏移40px。

demo-5

background-position:<percentage>就大不同了:如background-position:0% 0%(或top left)是将图片的左上角与容器元素的左上角对齐,background-position:100% 100%(或bottom right)是将图片的右下角与容器元素的右下角对齐,background-position:50% 50%(或center center)是将图片的中心点与容器元素的中心点对齐,background-position:33% 15%是将图片的横向33%和纵向15%的交汇点放置在容器元素的横向33%与15%的交汇点上。

demo-6

理解 <percentage>和<length>之间计算方式的差异是至关重要的。那么如果要在容器元素内显示第二张图片,怎么计算<percentage>的具体值呢?

demo-7

我们已知的信息如下:

  • 容器元素的尺寸:elW * elH
  • 单张图片的尺寸:imgW * imgH
  • Sprites图片的尺寸:spritesW * spritesH
  • 单张图片在Sprites图上的位置:imgPosX, imgPosY

我们假设:

  • 点的位置为 (x, y)
  • 容器上的(x, y)点与容器左上角的距离为 cX, cY
  • Sprites图上的(x, y)点与本张图片左上角的距离为 sX, sY

如果要把某张图片完全显示在容器元素内,我们可以推导出:

  • elW = imgW, elH = imgH
  • cX = sX, cY = sY

根据上面的信息,我就可以计算出具体的(x, y)值了,下面以 x% 为例:

  • cX = elW * x
  • sX = spritesW * x – imgPosX
  • elW * x = spritesW * x – imgPosX

解方程后就得到计算公式了:

  • x = imgPosX / (spritesW – elW) = imgPosX / (spritesW – imgW)
  • y = imgPosY / (spritesH – elH) = imgPosY / (spritesH – imgH)

把我们上面例子的信息代入方程式:

  • x = w / (4w -w) = 1/3
  • y = 0 / (h – h ) = 0

查看效果

手动计算累死人?Maxim来解救你!

Maxim是我们团队开发的可视化的构建工具,支持windows和Mac,开发时无需考虑background-sizebackground-position,发布时将文件拖入Maxim内,自动完成Sprites拼合,并补全background-sizebackground-position值,轻松实现响应式Sprites图。Maxim的更多功能也等你发现,赶快下载一个试试吧!!!

——
我的博客即将搬运同步至腾讯云+社区,邀请大家一同入驻:https://cloud.tencent.com/developer/support-plan?invite_code=ed2bo5axvfm8

深入理解视觉格式化模型( VISUAL FORMATTING MODEL)

“理论不懂就实践,实践不会就学理论”,非常赞同bluedavy的这句话。实践过程中经常会遇到某个属性的使用,浏览器渲染效果与预期效果不符,虽然通过死记硬背能避免或巧妙应用这种效果,但总感心虚发慌、毫无自信,因为不知晓背后的原理。这时就不要再用“就是这样的”的借口来搪塞自己,我们需要重新认识它。

实践与现象

绝对定位是一种常用的定位方式,也经常会看到一些使用技巧,轻松搞定一些不太容易实现的效果。现介绍两个绝对定位的使用技巧:

1. 绝对定位元素,水平方向(top和bottom)或和垂直方向(left和right)的定位值不设置时,其位置受其前面的兄弟元素影响,如同其在常规流中的位置。如下例所示:

  • 元素A,C绝对定位,不设置top,bottom值;
  • 元素B处于常规流中;

结果是:元素C的位置受元素B的影响,跟随在元素B的下方。

这种看似毫无用处的技巧,却能帮助我们解决一些项目实际问题。我们总希望我们的布局是自适应的,即不依赖与所处环境,当环境改变时,仍能完美工作。下面这个实例要求蓝色购买按钮水平居中,其后跟随一个链接。为了达到自适应布局,我们不能假设父级容器宽度固定,也不能假设蓝色按钮的文案固定,所以链接元素的位置也是根据上下文环境改变的。这种情况下,我们就可以对链接设置绝对定位,并且不用设置left 和right 值,两者的间距通过margin值实现,即可轻松达到预期效果。(当然,通过嵌套的方式也可实现,但不是最优解)

另一个案例是用以实现下拉菜单,下拉菜单通常由触发按钮和下拉列表组成,下拉列表的位置位于触发按钮的下方。同样,由于触发按钮的高度是可能变化的,那么下拉列表与触发按钮顶端的绝对距离是不固定的,使用单位px是无法达到自适应的,通常的技巧是设置top:100%,其实利用我们上面提到的技巧,对top和bottom不设置值也是可以实现的。

2. 绝对定位结合margin实现垂直居中

很多设计都可以抽象为“一个元素相对于父级(或包含块)在垂直方向或水平方向上居中对齐”的模式,根据实际情况又可分为该元素的尺寸未知和已知两种情况。这是个经久不衰的话题,实现方式也多种多样,这里我们讨论的是“尺寸已知元素在垂直方向上的居中对齐”问题。你可能看到过下面这种实现方式,绝对定位元素的4个值均为0,margin在垂直方向上也设置为了auto(支持IE8+)。一般为了水平居中会在水平方向上设置auto,为什么这种情况下,在垂直方向上设置auto,会导致垂直居中的效果呢?

规范与原理

为了解决这个疑虑,我重新学习了CSS 2.1规范中的9 Visual formatting model10 Visual formatting model details,现将相关章节译录于此。

这两章讲解了视觉格式化模型:用户代理在视觉媒体上如何处理文档树。在视觉格式化模型中,文档树中的每个元素根据框模型(box modal)生成0或多个框。这些框的布局由以下因素决定:

  • 框尺寸和类型
  • 定位方案(常规流、浮动和绝对定位)
  • 文档树中元素之间的关系
  • 外部信息(比如viewport尺寸、图像的固有尺寸等)

9.1.2 Containing blocks(包含块)

CSS 2.1中,许多框的位置和尺寸的计算是相对于一个矩形框的边缘,这个矩形框称为包含块。通常情况下,生成框是后代框的包含块(generated boxes act as containing blocks for descendant boxes;),称之为一个框为其后代创建了包含块。短语“一个框的包含块”指的是“这个框存在其中的包含块”,而非它生成的框。 每个框会相对于其包含块赋予位置,但它并不囿于包含块,可能会溢出(overflow)。包含块的尺寸计算细节在第10章有详细介绍。

9.2 Controlling box generation(控制框生成)

本节描述了CSS 2.1中可生成的框类型。一个框的类型部分地影响其在视觉格式化模型中的行为。

9.2.1 Block-level elements and block boxes

处在块格式化环境(BFC,block formatting context)中的框称之为块级框(block-level box)每个块级元素生成一个包含后代框和生成的内容的主体块级框,同时这个框与定位方案密切相关。有些块级元素除了生成主体框外,还会生成一个附加框,如’list-item’元素。附加框相对于主体框定位。 表框(table boxes)和替换元素(replaced elements)外,块级框同时也是块容器框(block container box)。块容器框要么仅包含块级框,要么建立一个行内格式化环境(IFC,inline formatting context),即仅包含行内级框。并非所有的块容器框都是块级框:非替换行内块(inline blocks)和非替换表格单元格都是块容器,但不是块级框。既是块级框也是块容器的框称为块框(block box)。 “块级框”、“块容器框”和“块框”这三个术语有时被简称为块(block)

9.2.3 Run-in boxes(插入型框)

CSS Level 3的CSS basic box model中定义。 run-in框的行为如下:

  1. 如果run-in框包含一个块框,那么run-in框变为块框。
  2. 如果run-in框的后继兄弟元素为块框(非浮动,非绝对定位),那么run-in框变为该块框的第一个行内框。run-in不能插入本身为run-in的块中,也不能插入块中已有run-in的块中。
  3. 否则,run-in框变为块框。

浏览器支持:IE8+(chrome不支持,难道是太鸡肋?) IE下查看效果
run-in-box
9.3.2 Box offsets: ‘top’, ‘right’, ‘bottom’, ‘left’

  • (绝对、固定)定位元素会生成一个定位框(positioned box),根据top,right,bottom,left布局。
  • 初始值为auto,非0。(文章开头的问题中未设置四值,等同设置为auto
  • 对于绝对定位元素,四值指定的是元素margin边与包含块的边之间的偏移量。对于相对定位元素,四值指定的是相对于自身框边的偏移量。

9.6 Absolute positioning

  • 从常规流中完全抽离,对其后继兄弟元素无影响。
  • 固定定位是绝对定位的特例,它的包含块是viewport。

9.7 Relationships between ‘display’, ‘position’, and ‘float’

这三个属性影响了框的生成和布局,相互影响如下:

  1. 如果’display’值为’none’,同时不设置’position’和’float’,那么该元素不生成框。
  2. 否则,如果’positon’值为’absolute’或’fixed’,即框为绝对定位,’float’的计算值为’none’,并且’display’根据下表设置。那么该框的位置由’top’,’right’,’bottom’,’left’和框的包含块决定。
  3. 否则,如果’float’的值不为’none’,那么该框会浮动,’display’根据下表设置。
  4. 否则,如果该元素为根元素,’display’根据下表设置。
  5. 否则,剩余的’display’属性值与指定值相同。
指定值 计算值
inline-table table
inline, table-row-group, table-column, table-column-group, table-header-group, table-footer-group, table-row, table-cell, table-caption, inline-block block
others same as specified

10.6 Calculating heights and margins(高度和margin值计算)

10.6.4 Absolutely positioned, non-replaced elements(绝对定位的非替换元素)

静态位置(static position),粗略地讲是指一个元素在常规流中的位置。精确地讲,一个元素的静态top值,是指包含块顶部边沿与该元素的假想框的顶部margin边沿之间的距离。假想框是指如果该元素的’position’值为’static’,以及’float’值为’non’且’clear’值为’none’时,该元素的第一个框。 对于绝对定位的元素,垂直尺寸的使用值必须满足下面约束:

‘top’ + ‘margin-top’ + ‘border-top-width’ + ‘padding-top’ + ‘height’ + ‘padding-bottom’ + ‘border-bottom-width’ + ‘margin-bottom’ + ‘bottom’ = height of containing block

如果’top’,’bottom’,’height’值均为auto,那么’top’值为元素的静态位置。(这也就回答了文章开头的问题) 如果三个值均不为auto,那么:

  • 如果’margin-top’和’margin-bottom’值均为’auto’,那么假定margin-top和margin-bottom两值相等,然后再解上面方程式。(上述的第二个垂直居中案例就是利用了这一点 
  • 如果’margin-top’和’margin-bottom’值中其一为’auto’,解上面方程式获取该margin值。
  • 如果数值超过限制,忽略’bottom’值,解方程式获取该值。

否则,从以下六种规则中挑选适用情况:

  1.  ‘top’和’height’为’auto’,’bottom’不为’auto’,那么’height’基于其内容根据10.6.7规则计算,’margin-top’值设为’auto’,’margin-bottom’值设为0,解方程式得’top’值。
  2.  ‘top’和’bottom’为’auto’,’height’不为’auto’,那么设置’top’值为其静态位置,’margin-top’值设为’auto’,’margin-bottom’值设为0,解方程式得’bottom’值。
  3.  ‘bottom’和’height’为’auto’,’top’不为’auto’,那么’height’基于其内容根据10.6.7规则计算,’margin-top’值设为’auto’,’margin-bottom’值设为0,解方程式得’bottom’值。
  4.  ‘top’值为’auto’,’bottom’和’height’不为’auto’,那么’margin-top’值设为’auto’,’margin-bottom’值设为0,解方程式得’top’值。
  5.  ‘height’值为’auto’,’bottom’和’top’不为’auto’,那么’margin-top’值设为’auto’,’margin-bottom’值设为0,解方程式得’height’值。
  6.  ‘bottom’值为’auto’,’height’和’top’不为’auto’,那么’margin-top’值设为’auto’,’margin-bottom’值设为0,解方程式得’bottom’值。

CSS表格布局实践

{D8A8EAC8-0326-4736-83B1-81AF692C45FA}

如何实现上图所示效果:左右两列的列宽由列内最宽单元格的宽度决定,进度条列占据剩余空间。(兼容到IE8就好了)

经分析需要处理一列的宽度,只有table布局才有列的概念,故采用display:table | table-row | table-cell来布局。

CSS属性table-layout定义了表格单元格、行和列的布局算法。默认值为auto,表格及其单元格的宽度由其内部的内容决定。而值为fixed时,表格的宽度取决于tabe元素的宽度值,列宽由对应col元素的宽度决定,或者由首行单元格的宽度决定,后续行内单元格不会影响列宽。使用fixed布局方法时,一旦表格的首行下载和解析完成,整个表格即可被渲染。相对于自动布局方法,这种方法可加速表格渲染,但可能会造成后续单元格的内容与列宽不适合。如果单元格的内容溢出,使用overflow属性来决定是否截断溢出内容。

一直强烈建议使用fixed布局方式,但从上文看,是无法实现我们所需效果。而auto布局,默认为各列平分表格的宽度。如果我们对左右两列设置一个看似合适的固定宽度(如10em),进度条列是可以占据表格的剩余空间,但无法实现列宽根据内容自适应改变(so sad)。

怎么办?怎么办?怎么办?

猜想:我们可否为单元格设置一个最小宽度,当单元格的内容超过最小宽度时自动撑开?

经分析和尝试,将左右两列的内容设置不换行white-space:nowrap,并将宽度设置为一个很小的值(如width:1px | 1%),即可实现我们期待的效果

再猜想:如果让需要占据剩余空间的列的宽度尽可能的大,大到100%,那么浏览器是否会为其他列按照其内容宽度来分配空间呢?经尝试,居然也可以达到我们期待的效果

哈哈哈……

但是 why? why? why?

[未完待续]

一种更好的网络图库图片预加载方法

流行的,或许并非最佳方式

网上的一个非正式调查展示了预加载一组图片的一个通用的标准方式。简化形式如下:

/* 'images' is an array with image metadata including a 'url' property */
for (var i = 0; i < images.length; ++i) {
    var img = new Image();
    img.src = images[‘url’];
}

这段代码遍历包含图片元数据的对象数组,为每个对象创建一个HTML Image对象,并为src属性设置url。一旦Image对象设置了src值,浏览器就会向服务器发起请求,并缓存返回的图片。

需要注意的是,浏览器请求是异步的。也就是说这段代码会遍历数组,每张图片几乎同时发起请求,并不需要等待服务器返回结果后顺序发起请求。对于现代浏览器而言,这段代码尝试并行下载4~8个张图片(当然如果来自不同域,会更多)。

并行下载的好处

网站通常会有一些资源,浏览器必须先下载后才能显式页面。HTML本身,一两个CSS文档,一些小的图片元素,字体,偶尔会有一些不可避免的JavaScript文件(需要在页面可以绘制前执行)。每一个文件通常都相当小,但是每个请求与服务器的往返都会导致延迟开销。虽然这种延迟通常很小,每个文档的延迟都是毫秒级的,但是如果浏览器需要等待一个请求完成后才能发起另一个请求的话,这些毫秒将依次累加,并迅速增至秒级,用户必须要等待这么长时间后才能浏览页面内容。如果可以同时发起所有请求,那么整体而言延迟时间会降低为一次的往返时延,从而使页面加载时间减少几秒钟。这并不会加快每个文档的实际下载速度,你仍受特定带宽的限制,所以4~8个的并行请求使下载速度降低4~8倍。但是总体下载速度的确加快了,因为你避免了连续的延迟开销。因为浏览器必须在下载了关键元素后才能绘制页面,避免顺序延迟时间累加意味着更快的页面绘制。

对预加载图片而言并非为一件好事

并行下载对于初始页面元素是非常有用的,因为浏览器在渲染页面前必须要先下载这些元素。一个CSS文件先于另一个CSS下载对于浏览器而言并无差别,因为浏览器需要两个文件都要下载后才能做其他事情。gallery里的图片预加载并非这种情况,你可以足够自信的预测哪个文件需要优先下载,你需要优先处理它,即使预加载的总体时间会稍长。

我网站的gallery的分析数据非常直观。虽然从一张图片切换至另一张图片的方式有多种,缩略图和上一张与下一张链接,90%的点击是在下一张链接上。几乎所有情况下,页面加载完成后的最关键因素是gallery里的下一张图片文件。如果使用标准的预加载方式,你无法控制这张图片何时加载。浏览器尝试加载gallery里的每张图片,以浏览器的最大并发请求组。这种技术很好的减少了gallery的总体加载时间,但也意味着加载最重要图片(下一张图片)的时间显著增长,因为它需要与其他并发请求竞争带宽。在相对缓慢的1.5Mbps DSL连接情况下,加载一张350K的图片需要2秒钟,浏览者有可能必须要等到4~6张图片加载完成后才能看到这种图片。也就是说gallery里的下一张图片可见前有一个潜在的12~15s的等待时间。有利的一面是这4~6张其他图片现在会被缓存起来,但是让用户盯着加载图片12秒钟,我们可能已经失去了这些用户。如果你以非「宽度连接」的方式测试你的网站的话,你可能会惊讶的发现,你的预加载器使你的网站对一些用户变得更糟。

一种更好的预加载方式

一旦理解了浏览者的行为,就可以设计一个预加载器,为大多数浏览者提供更好的体验。因为我知道几乎所有的浏览者是顺序浏览我的gallery,那么对我而言最好的策略是以相同的顺序加载图片。加载所有图片的总体时间可能会稍微长一些,因为我们会导致延迟开销累加,但此时的整体加载时间相对于页面的初始加载时间,并不是如此重要,因为在用户可以使用网站前,并不需要加载完所有的图片。我们只需要加载用户现在想看到的图片,javaScript如下:

function preload(imageArray, index) {
    index = index || 0;
    if (imageArray && imageArray.length > index) {
        var img = new Image();
        img.onload = function() {
            preload(imageArray, index + 1);
        }
        img.src = images[index][‘serving_url’];
    }
}
/* images is an array with image metadata */
preload(images);

注意:代码已做简化,生产环境代码会针对不同设备请求不同尺寸图片,并且会考虑用户进入gallery时非第一张图片的场景。

处理数组中第一张图片(index 0),添加onload事件处理函数,然后请求图片。只有当这张图片加载完成后,才会调用onload事件处理函数,然后为下一张图片做相同的操作,直到所有图片加载完成。

下图为使用Chrome开发者工具模拟2Mbps连接,下载序列和时间的比较:

并行预加载
(模拟的2Mbps连接,总体时间:24.01s,大图版本

串行预加载
(模拟的2Mbps连接,总体时间:25.44s,大图版本

标准预加载器的总体加载时间节省了1.5s。但是在这个案例中这个并非最重要的统计项。每个图表的最顶行表示gallery内下张图片的加载时间。在相对缓慢的连接环境下,使用标准的预加载器,页面加载完成14.27秒后,用户才能查看这张图片,即使这张图片处于第一行,主页面加载完成后就立即开始下载,这是因为它需要与图中所示的一些其他文档共享带宽。而顺序加载这些图片,会全带宽下载,于1.46秒内完成,提速接近于1000%。对于我们最常见的使用情况,这是一个巨大的进步。

这种策略需要关注非典型浏览者行为会产生什么效果?如果浏览者是另外10%用户,即点击了上一张图片链接或使用缩率图随机顺序跳转,会怎么样?因为我们的图片加载器每次仅加载一张图片,仍有可能并行加载另一张,并没有显著的性能损失。响应用户请求的JavaScript可以简单的请求图片,就像通常方式一样。因为它可能没有已经预加载和已缓存,浏览器会请求它,与当前正在加载的图片一起加载,如同让这条新请求加塞。由于在特定时间内仅有一张其他图片下载,浏览者的等待时间只比正常的等待时间稍长一点。在几乎所有情况下,相比在浏览器正在处理的6个并发请求之上再添加一个额外请求而言,这样的体验会更好。

另外一个好处是,由于浏览器一次仅处理一张图片,界面处于可响应状态。在标准预加载版本中,浏览器尝试同时下载和处理半打大型图片文档,这会消耗CPU时间,使页面的动画元素直到下载完成后才能处理。

避免过早预加载

在用户请求一个页面和浏览器可渲染这个页面之间会发生很多事情。这段时间内,你的用户只能坐在哪里看着一个空白页面。如果你珍惜你的用户,你应努力缩短这个时间。要做到这一点,需要尽可能快的传输浏览器所需的渲染页面的最少数据。你的预加载器不应参与其中。

至少对我而言,预加载代码最好放在window onload处理函数内,如果使用jQuery的:

$(window).load(function() {
    /* Preload code goes here */
});

 

————————————————————————————-

原文:A BETTER WAY TO PRELOAD IMAGES FOR WEB GALLERIES

[译]响应式图像

自从2010年Ethan Marcotte开始讨论响应式网页设计,开发者和设计师们竞相寻求处理响应式图片的方法。这的确是一个棘手的问题 ,因为我们对同一个网站在众多设备宽度下,使用同一图像源。你愿意在一个大显示屏上显示模糊地、马赛克状的图像?你愿意在你的手机上加载一个巨大的(虽然更漂亮的)图像?这个问题令人左右为难。

一群来自响应式问题社区组(RICG)的聪明家伙致力于解决这个难题,他们使picture元素和srcsetsizes属性纳入HTML 5.1规范草案 。因为我们无法预测用户在何地以及如何访问我们的网站,所以我们需要浏览器自身根据情况选择最好的图像。新规范将解决以下问题:

  • 基于设备象素比(device-pixel-radio)选择
  • 基于viewport选择
  • 基于美术设计(Art direction)选择
  • 基于图像格式选择

该规范中,img元素增加了两个新属性:srcsetsizessrcset用来声明一组图像源,浏览器根据我们使用描述符指定的条件来选择图像。描述符x表示图像的像素密度,描述符w表示图像的宽度;浏览器使用这些信息从列表中选择合适的图像。sizes属性为浏览器提供将要显示图像的尺寸信息,srcset使用w描述符时必须包含此属性。这种方法尤其适用于可变宽度的图像,我将在后面详细讨论。

我们现在可以根据用户的viewport,提供不同质量或art direction的图像,无需借助复杂的服务器端设置。响应式图像将成为HTML规范的重要组成部分,所有浏览器终将都会支持此解决方案。

固定宽度图像:基于设备像素比选择

视网膜屏幕的广泛应用,使我们不仅需要考虑屏幕分辨率,而且也需要考虑像素密度。视网膜屏幕,4K显示器,UltraHD-它们都比相同尺寸的标准分辨率显示器填充了更多的像素。更多的像素=更清晰的图像。

有些图片不管屏幕尺寸,始终以固定宽度显示-如站点logo或人物简介图像,也就是说需要根据设备像素比来选择。浏览器将根据设备像素比来选择加载哪张图像。

srcset属性列出了浏览器可以选择加载的源图像池,是一个由逗号分隔的列表。x描述符表示图像的设备像素比。浏览器根据运行环境,利用这些信息来选择适当的图像。不理解srcset的浏览器会直接加载src属性中声明的图像。

<img srcset="crest-383.jpg 1.5x, crest-510.jpg 2x" src="crest-255.jpg" alt="USWNT crest" />
USWNT crest

网站logo就是固定宽度图像的一个例子,不管viewport的宽度如何,始终保持相同的宽度。不过,与内容相关的图片,通常也需要响应式,它们的大小往往随viewport改变。对于这类图像,还有更好的处理方法。

可变宽度的图像:基于viewport选择

对于可变宽度的图像,我们使用srcset搭配w描述符以及sizes属性 。w描述符告诉浏览器列表中的每个图象的宽度。sizes属性是一个包含两个值的,由逗号分隔的列表。根据最新规范,如果srcset中任何图像使用了w描述符,那么必须要设置sizes属性。

sizes属性有两个值:第一个是媒体条件;第二个是源图尺寸值,在特定媒体条件下,此值决定了图片的宽度。需要注意是,源图尺寸值不能使用百分比,vw是唯一可用的CSS单位。

<img srcset="uswnt-480.jpg 480w, 
             uswnt-640.jpg 640w, 
             uswnt-960.jpg 960w,
             uswnt-1280.jpg 1280w" 
     sizes="(max-width: 400px) 100vw, 
            (max-width: 960px) 75vw, 
            640px" 
     src="uswnt-640.jpg" alt="USWNT World Cup victory">
USWNT World Cup victory

上列中,我们告诉浏览器在viewport宽度小于400像素时,使图像的宽度与viewport等宽。在viewport宽度小于960像素时,使图像的宽度为viewport宽度的75%。当viewport大于960像素时,使图像的宽度为640像素。如果你不熟悉vw ,可以看看Tim Severien的大文viewport单位详解 。

浏览器利用srcsetsizes信息来选择最符合规定条件的图像。如果浏览器的viewport是600像素,图像最可能以75vw的宽度显示。浏览器将尝试加载第一张大于450像素(600*0.75)的图像,也就是uswnt-480.jpg。如果我的是dpr为2的Retina显示屏,那么浏览器就会尝试加载第一张大于900像素(600*0.75*2)的图像,也就是uswnt-960.jpg。我们无法确定究竟显示哪张图像,因为每个浏览器根据我们提供的信息挑选适当图像的算法是有差异的。(译者注:srcset和size列表是对浏览器的一个建议(hint),而非指令。例如,设备像素比(dpr)为1.5的设备,亦可用1x也可用2x的图像,由浏览器根据其能力、网络等因素来决定。)

前两个例子都是以不同质量显示相同的图像,仅用srcset属性就足够了。不必担心老旧浏览器,老旧浏览器会把它看作为一个普通的图像并从src中加载。如果你想在不同宽度下显示稍微不同的图像,比如在较窄屏幕下仅显示图像的关键部分,那么要使用picture元素。

picture:基于美术设计(Art direction)选择

picture元素就像是图像和其源的容器。浏览器仍然需要img元素,用来表明需要加载图片,如果没有img,那么什么都不会渲染。source为浏览器提供了要显示图像的供选版本。基于art direction选择的适用场景为:在一个特定的转效点(breakpoint)需要显示一个特定的图像时。使用picture元素选择图像,不会有歧义。

<picture>
  <source media="(min-width: 960px)" srcset="ticker-tape-large.jpg">
  <source media="(min-width: 575px)" srcset="ticker-tape-medium.jpg">
  <img src="ticker-tape-small.jpg" alt="USWNT ticker-tape parade">
</picture>
USWNT ticker-tape parade

在本例中,当viewport大于960像素时,会加载图像的风景模式版本(ticker-tape-large.jpg)。当viewport宽度大于575像素时,浏览器会加载图像的裁剪过的肖像模式版本(ticker-tape-medium.jpg)。而当宽度小于575像素时,加载的图像( ticker-tape-small.jpg)已经被裁剪成焦点仅在一个球员上了。

picture元素是向后兼容的;不支持picture元素的浏览器将显示img。图像的所有标准属性(如alt),应该作用在img上而不是picture上。

source:基于图片格式选择

最近几年出现了一些新的图片格式,这些新图像格式在较小的文件大小情况下保证了较好的图片质量。听起来还不错,但残酷的事实是没有一个新格式被所有浏览器支持。谷歌的WebP表现不错,但只有Chrome和Opera原声支持。JPEG-XR,最初被称为高清照片,是微软发布的一个专有图像格式,仅Internet Explorer支持。如果你想了解更多信息,可以查看Zoltan Hawryluk对这些新格式的深入研究

<picture>
  <source type="image/vnd.ms-photo" src="wwc2015.jxr">
  <source type="image/jp2" src="wwc2015.jp2">
  <source type="image/webp" src="wwc2015.webp">
  <img src="wwc2015.png" alt="WWC 2015">
</picture>

  
source的type属性用来指定每个图像的MIME类型,浏览器会选择第一个含有其支持的MIME类型的源。源的顺序是至关重要的,如果浏览器无法识别所有的图象类型,它会回退至原来的img元素。

现在可以使用这些东东吗?

在写这篇文章的时候, Firefox,Chrome和Opera的最新稳定版本均支持picture。Safari和IE本身均不支持picturesrcset的情况稍微好一点,Firefox、Chrome和Opera的最新稳定版本完全支持,Safari8和Internet Explorer Edge部分支持,可以使用x描述符用于根据分辨率切换,但不支持w描述符。Safari9已经完全支持srcset了(译者注)。

现有不少polyfills解决支持性问题,最知名的恐怕是Scott Jehl的picturefill。目前我(原作者)在我自己的网站上使用Alexander Farkas的respimage。目前的状况是,我们已对响应式图像的处理方案达成一致,并且这些解决方案逐渐被所有的主流浏览器实现。尽管该规范仍在不断完善之中,但原生的响应式解决方案离我们越来越近了。

 

原文地址:Using Responsive Images (Now)

async vs defer 属性

<script>元素的async和defer属性支持度已经不错了,是时候详细了解它们了。

图例

719530

<script>

<script>脚本不设置任何属性。HTML文档解析过程中,遇到script文档时,会停止解析HTML文档,发送请求获取script文档(如果是外部文档的话)。脚本执行后,才恢复HTMl文档解析。

936468

<script async>

设置async属性后,在HTML解析的同时,下载script文档。script文档下载完成后,HTMl解析会暂停,来执行script文档。

712163

<script defer>

设置defer属性后,在HTML解析的同时,下载script脚本。但只有在HTML解析完成后,才执行script文档。同时,defer属性保证脚本按照其在文档中出现的顺序执行。

460821

如何选用?

通常情况下,尽可能的使用async属性,然后考虑defer,都不适用时才不设置任何属性。选用规则:

  • 如果脚本是模块化的,并且不依赖其他脚本,那么使用async。
  • 如果脚本依赖其他脚本或被其他脚本依赖,那么使用defer。
  • 如果脚本比较小,并且被一个async脚本依赖,那么使用行内脚本,并放置在async脚本之前。

支持情况

IE9及以下浏览器在实现defer属性上存在糟糕的bug,比如无法保证脚本的执行顺序。如果需要支持<=IE9,不建议使用defer,如果执行顺序非常重要的话,不要使用任何属性。查看规范

后记

Chrome为了更快的页面加载,引入了两项JavaScript新技术:script streaming 和 code caching。简而言之,前者优化脚本文档的解析(Chrome 41),后者缓存编译后的代码(Chrome 42)。

streaming

ref:

  1. async vs defer attributes
  2. New JavaScript techniques for rapid page loads

box-sizing最佳实践

/* apply a natural box layout model to all elements, but allowing components to change */
html{
    box-sizing:border-box;
}
*, 
*:before, 
*:after{
    box-sizing:inherit;
}

组件需要重置时,只需在组件容器上重置即可:

.component {
    /* designed to work in default box-sizing */
    /* in your page, you could reset it to normal */
    box-sizing: content-box;
}

参考资料

  1. * { Box-sizing: Border-box } FTW
  2. Inheriting box-sizing Probably Slightly Better Best-Practice

创建高效media queries的七个好习惯

1. 内容决定转效点(Let content determine breakpoints)

从最小屏幕开始,逐渐扩大窗口,当效果丑陋不堪(like shit)时,此处就是breakpoint。

2. 把布局看作为增强行为(Treat layout as an enhancement)

作为移动优先的响应式设计策略的一部分,以移动优先的方式书写样式是非常重要的。样式代码更少、更简洁、更易维护。

/* Desktop-first styles: Avoid */
.column {
    float: left;
    width: 50%;
}

@media all and (max-width: 50em) {
    .column {
        float: none;
        width: auto;
    }
}
/* Mobile-first styles FTW */
@media all and (min-width: 50em) {
    .column {
        float: left;
        width: 50%;
    }
}

3. 使用主、次转效点(Use major and minor breakpoints)

  • 主转效点:效果显著变化,如一列变两列、三列。
  • 次转效点:某些特定元素的调整,或称为tweakpoint

使用Sass管理转效点

$bp-small : 24em;
$bp-small-2 : 29.75em;
$bp-small-3 : 39.8em;
$bp-med : 46.8em;
$bp-med-2 : 48em;
$bp-large : 50em;
$bp-large-2 : 54.5em;
$bp-xl : 60em;
$bp-xl-2 : 67em;

4. 使用相对单位(Use relative units)

media queries中使用相对单位,浏览器可以根据用户的缩放等级调整设计效果,避免出现横向滚动条,提供一个更加舒适和易于阅读的体验。

ems

5. 超越宽度(Go beyond width)

检测viewport width不是media queries的全部,可检测的媒体特性非常多,包括color, color index, aspect ratio, device aspect ratio, width, device width, height, device height, orientation, monochrome, resolution, scan, pixel-density等。

  • 使用pixel-density可以为视网膜屏或其他高分辨率屏幕有条件的提供大背景图片或icon sprites。
  • 使用height检测可用的屏幕高度,并相应的调整样式。
  • 使用orientation检测屏幕是处在横向还是竖向模式。

6. 条件装载(Use media queries for conditional loading)

7. 过犹不及(Don’t Go Overboard)

参考文献:

纯CSS实现响应式表格

先看DEMO

自从转岗至腾讯云后,项目中接触到大量的数据表格。多列数据表格在空间有限的手机屏幕下,难以完美呈现,需要做响应式处理。本文介绍一种使用纯CSS实现响应式表格的方法。

通常表格中的一行代表一条项目,每列代表项目的一个属性方面(即字段)。在屏幕空间充足的情况下,我们可以将表格的每列都完全显式在屏幕上,但在手机下,每列内容会拥挤不堪,甚至出现横向滚动条,用户快速了解每条项目基本情况的效率会大打折扣。

Snip20150407_5

Snip20150407_6

一种优化方法是:使用media queries监测屏幕viewport小于568px时,让单元格(td)独占一行,每行(tr)相互隔离,如同每条项目为一独立的表格,如下图:

Snip20150407_7

现在每条项目便于阅读了,但表头(th)与对应的单元格(td)隔离开了,单元格的具体意义难以理解。那么,隐藏掉表格的thead,单元格内容右对齐,在每个单元格前面插入对应的表头(th)即可解决这个问题,如下图:

Snip20150407_9

伪元素(:before)结合att()表达式(获取伪元素所依附元素的HTML属性值)可完成此任务。当然,我们首先要对每个单元格(td)元素改造:

Snip20150407_10

样式如下:

table {
    border: 1px solid #ccc;
    width: 100%;
    margin: 0;
    padding: 0;
    border-collapse: collapse;
    border-spacing: 0;
}

table tr {
    border: 1px solid #ddd;
    padding: 5px;
}

table th,
table td {
    padding: 10px;
    text-align: center;
}

table th {
    text-transform: uppercase;
    font-size: 14px;
    letter-spacing: 1px;
}

/* <= 568px */
@media screen and (max-width: 35.5em) {
    table {
        border: 0;
    }
    table thead {
        display: none;
    }
    table tr {
        margin-bottom: 10px;
        display: block;
        border-bottom: 2px solid #ddd;
    }
    table td {
        display: block;
        text-align: right;
        font-size: 13px;
        border-bottom: 1px dotted #ccc;
    }
    table td:last-child {
        border-bottom: 0;
    }
    table td:before {
        content: attr(data-label);
        float: left;
        text-transform: uppercase;
        font-weight: bold;
    }
}

大功告成了吗?某些屏幕阅读器(如OSX 和iOS下voiceOver)可朗读伪类插入的内容,这 是否造成了过度提示,会不会影响屏幕阅读器用户的访问效率。而某些屏幕阅读器与浏览器的组合又不会朗读,如何优化呢?

参考资料:
Responsive Tables in Pure CSS