头部压缩技术介绍,移动端自适应方案

移动端自适应方案

2015/09/14 · JavaScript, 基础技术 · 移动端, 自适应

原文出处: 大搜车前端团队博客   

前方依旧高能 ^_^ , 本文主要解决以下问题:

  • 真的需要动态生成viewport吗?
  • 如何自适应?

然后给出主观的最佳实践。

  • 最帅的flex

赶时间戳这里传送门

比较无聊干燥的文章,看前请喝水。

研究样本

  1. 手淘 ml.js
  2. 天猫首页
  3. 手机携程

一个月前去了css开发者大会,听到了手淘的自适应方案,想起之前一直就想了解ml.js到底干了什么事。回来仔细研究了一下,抱着好奇心一并看了同样类型的网站的方案,深入学习一下。

研究结论

  1. 手淘

    • 获取手机dpr(window.devicePixelRatio),动态生成viewport。
    • 换取手机宽度,分成10份,每一份的宽度即是rem的尺寸。
    • 根据设计稿尺寸(px)通过计算,转换成rem去布局。

    ps:海外淘宝并没有这样做,而是scale1.0并且图片大概都是2倍图。

  2. 天猫

    • 采用scale=1.0 写死viewport。
    • flex布局,笃定认为布局尺寸是375 (iPhone6)
    • rem 确定非flex的元素
  3. 手机携程
    • 采用scale=1.0 写死viewport
    • px + 百分比布局

实现之前

提及实现之前,先简单过一些概念。

完美视口

完美视口的概念已经街知巷闻了,如果不知道可以先戳这里。

在这几篇文章里,还会学会设备像素,css像素等概念,大神讲的很透彻,这里就不献丑了。

ppk 谈 viewport其1 ppk 谈 viewport其2 ppk 谈 viewport其3

这里给出完美视口

XHTML

<meta name="viewport" content="initial-scale=1.0,width=device-width,user-scalable=0,maximum-scale=1.0"/>

1
<meta name="viewport" content="initial-scale=1.0,width=device-width,user-scalable=0,maximum-scale=1.0"/>

在移动端,低端无定制的需求,都可以用这个完美视口完成。然而看到这篇文章的你,显然完美视口还不能满足。

dpr

dpr是devicePixelRatio的简写,也就是屏幕分辩比

历史原因,由于苹果retina的产生,使得清晰度提升,主要是因为`设备像素`提升了一倍,因此可以用更多像素去绘画更清晰的图像。#我乱说的#

1
历史原因,由于苹果retina的产生,使得清晰度提升,主要是因为`设备像素`提升了一倍,因此可以用更多像素去绘画更清晰的图像。#我乱说的#

坊间对于dpr更通俗的说法叫

  • 一倍屏
  • 两倍屏
  • 三倍屏

scale

scale是屏幕拉伸比。也就是视口上的initial-scale , maximum-sacle 等属性。

scale 和 dpr的关系是倒数。

1
scale 和 dpr的关系是倒数。

直观感受

这是我对dpr的直观感受图片 1

同样去展示 1 x 1 像素的点,虽然在屏幕上看到的大小是一样,但背后表现它的像素数量是不同。

这也意味着,在一样大小的面积内,更多物理像素的屏幕上展现色彩的能力越强。

但这不是我要关注的点,我们关注的是。

1. 是否需要根据倍屏去切换scale达到伸缩的目的

2. 切换scale的成本和回报

下面根据几个实验来回答这两个问题。

自适应问题

实验1 - 传说中的1px

大多数给出要动态切换scale的理由有以下两个。

  1. 1px并不是 [ 真实的1px ] , 2. 为了充分利用屏幕的分辨率,使用符合屏幕的图片。
1
2
3
1. 1px并不是 [ 真实的1px ] ,
 
2. 为了充分利用屏幕的分辨率,使用符合屏幕的图片。

HTTP/2 头部压缩技术介绍

2016/04/13 · 基础技术 · HTTP/2

本文作者: 伯乐在线 - JerryQu 。未经作者许可,禁止转载!
欢迎加入伯乐在线 专栏作者。

我们知道,HTTP/2 协议由两个 RFC 组成:一个是 RFC 7540,描述了 HTTP/2 协议本身;一个是 RFC 7541,描述了 HTTP/2 协议中使用的头部压缩技术。本文将通过实际案例带领大家详细地认识 HTTP/2 头部压缩这门技术。

减少HTTP请求之合并图片详解(大型网站优化技术)

2015/11/26 · HTML5 · HTTP

原文出处: Kelly   

一、相关知识讲解

看过雅虎的前端优化35条建议,都知道优化前端是有多么重要。页面的加载速度直接影响到用户的体验。80%的终端用户响应时间都花在了前端上,其中大部分时间都在下载页面上的各种组件:图片,样式表,脚本,Flash等等。

减少组件数必然能够减少页面提交的HTTP请求数。这是让页面更快的关键。减少页面组件数的一种方式是简化页面设计。但有没有一种方法可以在构建复杂的页面同时加快响应时间呢?嗯,确实有鱼和熊掌兼得的办法。

这里我们就拿雅虎的第一条建议:尽量减少HTTP请求数里的减少图片请求数量 进行讲解。

我们都知道,一个网站的一个页面可能有很多小图标,例如一些按钮、箭头等等。当加载html文档时,只要遇到有图片的,都会自动建立起HTTP请求下载,然后将图片下载到页面上,这些小图片可能也就是十几K大甚至1K都不到,假如我们的一个页面有一百个小图标,我们在加载页面时,就要发送100个HTTP请求,如果你的网站访问量很大并发量也很高,假如上百万访问量,那发起的请求就是千万级别了,服务器是有一定的压力的,并且一个用户的一个页面要发起那么多请求,是很耗时的。

所以,我们优化的方案就是:将这些十几K、几K的小图标合并在一张图片里,然后用CSS的background-imagebackground-position属性来定位要显示的部分。

二、代码实现

1、思路:

将一个文件夹里的图标,自动生成在一张图片里面,同时自动生成对应的css文件,我们只要在HTML里的标签中添加相应的属性值就能显示图片了。

2、实现过程:

XHTML

<?php //自己定义一个根目录 define('ROOT', $_SERVER['DOCUMENT_ROOT'].'iconwww'); //这个是图片的目录 define('RES_BASE_URL', ''); /** * 生成背景图的函数 */ function generateIcon() { //网站根目录 $webRoot = rtrim(ROOT, '/'); //背景图目录 $root = "$webRoot/img/bg"; //Php-SPL库中 的 目录文件遍历器 $iterator = new DirectoryIterator($root); //开始遍历该背景图目录下的目录,我们是把想生成背景图的目录,放在bg目录中以各个模块的目录分类存放 foreach ($iterator as $file) { //遇到目录遍历 if (!$file->isDot() && $file->isDir()) { //取得文件名 $fileName = $file->getFilename(); generateIconCallback("$root/$fileName", "$webRoot/img/$fileName", "$webRoot/css/$fileName.css"); } } } /** * 用户生成合并的背景图和css文件的函数 * @param string $dir 生成背景图的图标所在的目录路径 * @param string $bgSavePath 背景图所保存的路径 * @param string $cssSavePath css保存的路径 */ function generateIconCallback($dir, $bgSavePath, $cssSavePath) { $shortDir = str_replace('\', '/', substr($dir, strlen(ROOT-1))); //返回文件路径信息 $pathInfo = pathinfo($bgSavePath.'.png'); $bgSaveDir = $pathInfo['dirname']; //确保目录可写 ensure_writable_dir($bgSaveDir); //背景图名字 $bgName = $pathInfo['filename']; //调用generateIconCallback_GetFileMap()函数生成每一个图标所需要的数据结构 $fileMap = array('a' => generateIconCallback_GetFileMap($dir)); $iterator = new DirectoryIterator($dir); foreach ($iterator as $file) { if ($file->isDot()) continue; if ($file->isDir()) { //二级目录也要处理 $fileMap['b-'.$file->getFilename()] = generateIconCallback_GetFileMap($file->getRealPath()); } } ksort($fileMap); //分析一边fileMap,计算整个背景图的大小和每一个图标的offset //初始化偏移量和背景图 $offsetX = $offsetY = $bgWidth = 0; //设定每个小图标之间的距离 $spaceX =$spaceY = 5; //图片最大宽度 $maxWidth = 800; $fileMd5List =array(); //这里需要打印下$fileMap就知道它的数据结构了 foreach ($fileMap as $k1 => $innerMap) { foreach ($innerMap as $k2 => $itemList) { //行高姐X轴偏移量初始化 $offsetX = $lineHeight = 0; foreach ($itemList as $k3 => $item) { //变量分别是:图标的宽度,高度,类型,文件名,路径,MD5加密字符串 list($imageWidth, $imageHeight, $imageType, $fileName, $filePathname, $fileMd5) = $item; $fileMd5List []= $fileMd5; //如果图片的宽度+偏移量 > 最大宽度(800) 那就换行 if ($offsetX !== 0 && $imageWidth + $offsetX > $maxWidth) { $offsetY += $spaceY + $lineHeight; $offsetX = $lineHeight = 0; } //如果图片高度 > 当前行高 那就讲图片高度付给行高我们这的 if ($imageHeight > $lineHeight) $lineHeight = $imageHeight; $fileMap[$k1][$k2][$k3] = array($imageWidth, $imageHeight, $offsetX, $offsetY, $imageType, $fileName, $filePathname); //X轴偏移量的计算 $offsetX += $imageWidth + $spaceX; if ($offsetX > $bgWidth) $bgWidth = $offsetX; } //Y轴偏移量的计算 $offsetY += $lineHeight + $spaceY; } } //把右下两边多加了的空白距离给干掉 $bgWidth -= $spaceX; $bgHeight = $offsetY - $spaceY; $fileMd5List = implode("n", $fileMd5List); //生成背景图和 css文件 //资源路径 $resBaseUrl = RES_BASE_URL; $suffix = base_convert(abs(crc32($fileMd5List)), 10, 36); $writeHandle = fopen($cssSavePath, 'w'); fwrite($writeHandle, "/** bg in dir: $shortDir/ */n[icon-$bgName]{background:url({$resBaseUrl}/$bgName.png?$suffix) no-repeat;display:inline-block;}"); //做图片,这些函数具体可以查看PHP手册 $destResource = imagecreatetruecolor($bgWidth, $bgHeight); imagealphablending($destResource, false); imagesavealpha($destResource, false); $color = imagecolorallocatealpha($destResource, 255, 255, 255, 127); imagefill($destResource, 0, 0, $color); //对每一张小图片进行处理,生成在大背景图里,并生成css文件 foreach ($fileMap as $innerMap) { foreach ($innerMap as $itemList) { foreach ($itemList as $item) { list($imageWidth, $imageHeight, $offsetX, $offsetY, $imageType, $fileName, $filePathname) = $item; if ($imageType === IMAGETYPE_PNG) { $srcResource = imagecreatefrompng($filePathname); } else if ($imageType === IMAGETYPE_JPEG) { $srcResource = imagecreatefromjpeg($filePathname); } imagecopy($destResource, $srcResource, $offsetX, $offsetY, 0, 0, $imageWidth, $imageHeight); imagedestroy($srcResource); //写入css $posX = $offsetX === 0 ? 0 : "-{$offsetX}px"; $posY = $offsetY === 0 ? 0 : "-{$offsetY}px"; fwrite($writeHandle, "n[icon-$bgName="$fileName"]{width:{$imageWidth}px;height:{$imageHeight}px;background-position:$posX $posY;}"); } } } //压缩级别 7 imagepng($destResource, "$bgSavePath.png", 7); imagedestroy($destResource); fclose($writeHandle); $shortCssSavePath = substr($cssSavePath, strlen(ROOT)); } /** * 将图片的信息处理成我们想要的数据结构 * @param [type] $dir [description] * @return [type] [description] */ function generateIconCallback_GetFileMap($dir) { $map = $sort = array(); $iterator = new DirectoryIterator($dir); foreach($iterator as $file) { if(!$file->isFile()) continue; $filePathname = str_replace("\", '/', $file->getRealPath()); //这些函数可以查看PHP手册 $imageInfo = getimagesize($filePathname); $imageWidth = $imageInfo[0]; $imageHeight = $imageInfo[1]; $imageType = $imageInfo[2]; if(!in_array($imageType, array(IMAGETYPE_JPEG, IMAGETYPE_PNG))) { $fileShortName = substr($filePathname, strlen(ROOT) - 1); echo "<p> $fileShortName 图片被忽略: 因为图片类型不是png|jpg.</p>"; continue; } //这是我们的图片规格,行高分别有 16 32 64 128 256 99999 foreach(array(16, 32, 64, 128, 256, 99999) as $height) { if($imageHeight <= $height) { $mapKey = $height; break; } } if(!isset($map[$mapKey])) $map[$mapKey] = array(); $filePathInfo = pathinfo($filePathname); $map[$mapKey] []= array($imageWidth, $imageHeight, $imageType, $filePathInfo['filename'], $filePathname, md5_file($filePathname)); $sort[$mapKey] []= str_pad($imageHeight, 4, '0', STR_PAD_LEFT) . $filePathInfo['filename']; } foreach($map as $k => $v) array_multisort($map[$k], SORT_ASC, SORT_NUMERIC, $sort[$k]); ksort($map, SORT_NUMERIC); return $map; } /** * 判断目录是否可写 * @param string $dir 目录路径 */ function ensure_writable_dir($dir) { if(!file_exists($dir)) { mkdir($dir, 0766, true); @chmod($dir, 0766); @chmod($dir, 0777); } else if(!is_writable($dir)) { @chmod($dir, 0766); @chmod($dir, 0777); if(!@is_writable($dir)) { throw new BusinessLogicException("目录不可写", $dir); } } } generateIcon(); ?> <!DOCTYPE html> <html> <head> <link rel="stylesheet" type="text/css" href="css/Pink.css"> <title></title> </head> <body> <div>我们直接引入所生成的css文件,并测试一下是否成功</div> <br> <div>这里在span标签 添加属性 icon-Pink ,值为About-40,正常显示图片</div> <span icon-Pink="About-40"></span> </body> </html>

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
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
<?php
    //自己定义一个根目录
    define('ROOT', $_SERVER['DOCUMENT_ROOT'].'iconwww');
    //这个是图片的目录
    define('RES_BASE_URL', 'http://localhost:8080/iconwww/img');
 
    /**
     * 生成背景图的函数
     */
    function generateIcon() {
        //网站根目录
        $webRoot = rtrim(ROOT, '/');
        //背景图目录
        $root = "$webRoot/img/bg";
        //Php-SPL库中 的 目录文件遍历器
        $iterator = new DirectoryIterator($root);
        //开始遍历该背景图目录下的目录,我们是把想生成背景图的目录,放在bg目录中以各个模块的目录分类存放
        foreach ($iterator as $file) {
            //遇到目录遍历
            if (!$file->isDot() && $file->isDir()) {
                //取得文件名
                $fileName = $file->getFilename();
                generateIconCallback("$root/$fileName", "$webRoot/img/$fileName", "$webRoot/css/$fileName.css");
            }
        }
    }
 
    /**
     * 用户生成合并的背景图和css文件的函数
     * @param  string $dir         生成背景图的图标所在的目录路径
     * @param  string $bgSavePath  背景图所保存的路径
     * @param  string $cssSavePath css保存的路径
     */
    function generateIconCallback($dir, $bgSavePath, $cssSavePath) {
        $shortDir = str_replace('\', '/', substr($dir, strlen(ROOT-1)));
        //返回文件路径信息
        $pathInfo = pathinfo($bgSavePath.'.png');
 
        $bgSaveDir = $pathInfo['dirname'];
        //确保目录可写
        ensure_writable_dir($bgSaveDir);
        //背景图名字
        $bgName = $pathInfo['filename'];
        //调用generateIconCallback_GetFileMap()函数生成每一个图标所需要的数据结构
        $fileMap = array('a' => generateIconCallback_GetFileMap($dir));
 
        $iterator = new DirectoryIterator($dir);
        foreach ($iterator as $file) {
            if ($file->isDot()) continue;
            if ($file->isDir()) {
                //二级目录也要处理
                $fileMap['b-'.$file->getFilename()] = generateIconCallback_GetFileMap($file->getRealPath());
            }
        }
        ksort($fileMap);
 
        //分析一边fileMap,计算整个背景图的大小和每一个图标的offset
        //初始化偏移量和背景图    
        $offsetX = $offsetY = $bgWidth = 0;
        //设定每个小图标之间的距离
        $spaceX =$spaceY = 5;
        //图片最大宽度
        $maxWidth = 800;
        $fileMd5List =array();
        //这里需要打印下$fileMap就知道它的数据结构了
        foreach ($fileMap as $k1 => $innerMap) {
            foreach ($innerMap as $k2 => $itemList) {
                //行高姐X轴偏移量初始化
                $offsetX = $lineHeight = 0;
                foreach ($itemList as $k3 => $item) {
                    //变量分别是:图标的宽度,高度,类型,文件名,路径,MD5加密字符串
                    list($imageWidth, $imageHeight, $imageType, $fileName, $filePathname, $fileMd5) = $item;
                    $fileMd5List []= $fileMd5;
                    //如果图片的宽度+偏移量 > 最大宽度(800) 那就换行
                    if ($offsetX !== 0 && $imageWidth + $offsetX > $maxWidth) {
                        $offsetY += $spaceY + $lineHeight;
                        $offsetX = $lineHeight = 0;
                    }
                    //如果图片高度 > 当前行高  那就讲图片高度付给行高我们这的
                    if ($imageHeight > $lineHeight) $lineHeight = $imageHeight;
                    $fileMap[$k1][$k2][$k3] = array($imageWidth, $imageHeight, $offsetX, $offsetY, $imageType, $fileName, $filePathname);
                    //X轴偏移量的计算
                    $offsetX += $imageWidth + $spaceX;
                    if ($offsetX > $bgWidth) $bgWidth = $offsetX;
                }
                //Y轴偏移量的计算
                $offsetY +=  $lineHeight + $spaceY;
            }
        }
        //把右下两边多加了的空白距离给干掉
        $bgWidth -= $spaceX;
        $bgHeight = $offsetY - $spaceY;
        $fileMd5List = implode("n", $fileMd5List);
 
        //生成背景图和 css文件
 
        //资源路径
        $resBaseUrl = RES_BASE_URL;
        $suffix = base_convert(abs(crc32($fileMd5List)), 10, 36);
        $writeHandle = fopen($cssSavePath, 'w');
        fwrite($writeHandle, "/** bg in dir: $shortDir/ */n[icon-$bgName]{background:url({$resBaseUrl}/$bgName.png?$suffix) no-repeat;display:inline-block;}");
 
        //做图片,这些函数具体可以查看PHP手册
        $destResource = imagecreatetruecolor($bgWidth, $bgHeight);
        imagealphablending($destResource, false);
        imagesavealpha($destResource, false);
        $color = imagecolorallocatealpha($destResource, 255, 255, 255, 127);
 
        imagefill($destResource, 0, 0, $color);
 
        //对每一张小图片进行处理,生成在大背景图里,并生成css文件
        foreach ($fileMap as $innerMap) {
            foreach ($innerMap as $itemList) {
                foreach ($itemList as $item) {
                     list($imageWidth, $imageHeight, $offsetX, $offsetY, $imageType, $fileName, $filePathname) = $item;
                     if ($imageType === IMAGETYPE_PNG) {
                        $srcResource = imagecreatefrompng($filePathname);
                     } else if ($imageType === IMAGETYPE_JPEG) {
                        $srcResource = imagecreatefromjpeg($filePathname);
                     }
                     imagecopy($destResource, $srcResource, $offsetX, $offsetY, 0, 0, $imageWidth, $imageHeight);
                     imagedestroy($srcResource);
 
                     //写入css
                     $posX = $offsetX === 0 ? 0 : "-{$offsetX}px";
                     $posY = $offsetY === 0 ? 0 : "-{$offsetY}px";
                     fwrite($writeHandle, "n[icon-$bgName="$fileName"]{width:{$imageWidth}px;height:{$imageHeight}px;background-position:$posX $posY;}");
                 }
            }
        }
 
        //压缩级别 7
        imagepng($destResource, "$bgSavePath.png", 7);
        imagedestroy($destResource);
        fclose($writeHandle);
 
        $shortCssSavePath = substr($cssSavePath, strlen(ROOT));
    }
 
    /**
     * 将图片的信息处理成我们想要的数据结构
     * @param  [type] $dir [description]
     * @return [type]      [description]
     */
    function generateIconCallback_GetFileMap($dir) {
        $map = $sort = array();
        $iterator = new DirectoryIterator($dir);
        foreach($iterator as $file) {
            if(!$file->isFile()) continue;
            $filePathname = str_replace("\", '/', $file->getRealPath());
            //这些函数可以查看PHP手册
            $imageInfo = getimagesize($filePathname);
            $imageWidth = $imageInfo[0];
            $imageHeight = $imageInfo[1];
            $imageType = $imageInfo[2];
 
            if(!in_array($imageType, array(IMAGETYPE_JPEG, IMAGETYPE_PNG))) {
                $fileShortName = substr($filePathname, strlen(ROOT) - 1);
                echo "<p> $fileShortName 图片被忽略: 因为图片类型不是png|jpg.</p>";
                continue;
            }
 
            //这是我们的图片规格,行高分别有 16 32 64 128 256 99999
            foreach(array(16, 32, 64, 128, 256, 99999) as $height) {
                if($imageHeight <= $height) {
                    $mapKey = $height;
                    break;
                }
            }
            if(!isset($map[$mapKey])) $map[$mapKey] = array();
            $filePathInfo = pathinfo($filePathname);
            $map[$mapKey] []= array($imageWidth, $imageHeight, $imageType, $filePathInfo['filename'], $filePathname, md5_file($filePathname));
            $sort[$mapKey] []= str_pad($imageHeight, 4, '0', STR_PAD_LEFT) . $filePathInfo['filename'];
        }
        foreach($map as $k => $v) array_multisort($map[$k], SORT_ASC, SORT_NUMERIC, $sort[$k]);
        ksort($map, SORT_NUMERIC);
        return $map;
    }
 
    /**
     * 判断目录是否可写
     * @param  string $dir 目录路径
     */
    function ensure_writable_dir($dir) {
        if(!file_exists($dir)) {
            mkdir($dir, 0766, true);
            @chmod($dir, 0766);
            @chmod($dir, 0777);
        }
        else if(!is_writable($dir)) {
            @chmod($dir, 0766);
            @chmod($dir, 0777);
            if(!@is_writable($dir)) {
                throw new BusinessLogicException("目录不可写", $dir);
            }
        }
    }
 
    generateIcon();
?>
<!DOCTYPE html>
<html>
<head>
    <link rel="stylesheet" type="text/css" href="css/Pink.css">
    <title></title>
 
</head>
<body>
<div>我们直接引入所生成的css文件,并测试一下是否成功</div>
<br>
<div>这里在span标签 添加属性 icon-Pink ,值为About-40,正常显示图片</div>
<span icon-Pink="About-40"></span>
</body>
</html>

调用以上代码,我们的浏览器是这样显示的:

图片 2

然后css目录生成了Pink.css文件:

图片 3

img目录下生成了Pink.png文件:

图片 4

看看生成的背景图是长啥样子:

图片 5

 

接下来我们再看一下所生成的图片大小与Pink文件夹里所有小图片总和的大小,对它们做个比较:

图片 6

 

图片 7

从上图可以看出,我们生成的图片的大小明显小于文件夹所有图片的大小,所以在将100个小图标下载下来的速度 会明显小于 将背景图下载下来和将CSS下载下来的速度。

当访问量大时,或者小图片的量大时,会起到很明显的优化效果!!!

代码中的每一个点都基本上有注释,很方便大家去理解,只要大家用心去看,肯定能将这一网站优化技术用到自己的项目中。

本次博文就写到这!!!

如果此博文中有哪里讲得让人难以理解,欢迎留言交流,若有讲解错的地方欢迎指出。

如果您觉得您能在此博文学到了新知识,请为我顶一个,如文章中有解释错的地方,欢迎指出。

  互相学习,共同进步!

2 赞 3 收藏 评论

图片 8

真实的1px

这一条和设计稿密切想关,要讨论它不能抛开设计稿不谈。

这里先补一下切图课,如果自己要做1x , 2x, 3x 的设计稿。如何去实现?

尺寸!!!

大多数情况下,设计师产出各种尺寸的稿子(事实上一般只是2倍稿子),都是先画出大尺寸的稿子,再去缩小尺寸,最后导出。 这样会带来问题:

如果设计师在2倍稿子里画了一条1px的线,这时候假如我们要在scale=1.0里呈现的话,就会变成0.5px,如下图。

图片 9

而很大一部分手机是无法画出0.5px的,因此这里一般有一个hack

CSS

transform:scaleX(0.5)或transform:scaleY(0.5)

1
transform:scaleX(0.5)或transform:scaleY(0.5)

但是有人提出了, 既然可以改变viewport的scale达到合理利用不同倍屏的优势,为什么不这么写呢。

XHTML

<meta name="viewport" content="initial-scale=2.0,width=device-width/>

1
<meta name="viewport" content="initial-scale=2.0,width=device-width/>

等等,为了设计稿的尺寸我们如此大费周章?

事实上,即使2x设计稿避免了1px。3x设计稿也可能出现2px。

而且这里如果写死scale可能造成部分地方和稿子出入较大,无法还原设计稿,界面的显示会打折扣。

解决这个问题的关键在于:交流

  • 如果你的设计师是个要求严格,而且产品界面把控非常严格的话,应该动态去实现viewport或使用scale的hack去改变。
  • 如果部分区域实在没有必要[ 过度优化 ], scale=1.0 实在是非常低成本还原的方案,未尝不可。

为什么要压缩

在 HTTP/1 中,HTTP 请求和响应都是由「状态行、请求 / 响应头部、消息主体」三部分组成。一般而言,消息主体都会经过 gzip 压缩,或者本身传输的就是压缩过后的二进制文件(例如图片、音频),但状态行和头部却没有经过任何压缩,直接以纯文本传输。

随着 Web 功能越来越复杂,每个页面产生的请求数也越来越多,根据 HTTP Archive 的统计,当前平均每个页面都会产生上百个请求。越来越多的请求导致消耗在头部的流量越来越多,尤其是每次都要传输 UserAgent、Cookie 这类不会频繁变动的内容,完全是一种浪费。

以下是我随手打开的一个页面的抓包结果。可以看到,传输头部的网络开销超过 100kb,比 HTML 还多:

图片 10

下面是其中一个请求的明细。可以看到,为了获得 58 字节的数据,在头部传输上花费了好几倍的流量:

图片 11

HTTP/1 时代,为了减少头部消耗的流量,有很多优化方案可以尝试,例如合并请求、启用 Cookie-Free 域名等等,但是这些方案或多或少会引入一些新的问题,这里不展开讨论。

对应倍图

对于这一点,争议较多,因为如果要做到对应倍图的话,意味着图片都需要做三份。成本太高了。

这里通常有两种做法

  1. 图片服务

    例如在100×100的图片容器中。

1倍图 http:// img.xxx.com/abc.jpg_100x100 2倍图 http://
img.xxx.com/abc.jpg_200x200 3倍图 http://
img.xxx.com/abc.jpg_300x300

<table>
<colgroup>
<col style="width: 50%" />
<col style="width: 50%" />
</colgroup>
<tbody>
<tr class="odd">
<td><div class="crayon-nums-content" style="font-size: 13px !important; line-height: 15px !important;">
<div class="crayon-num" data-line="crayon-5b8f19d520d5d723297616-1">
1
</div>
<div class="crayon-num crayon-striped-num" data-line="crayon-5b8f19d520d5d723297616-2">
2
</div>
<div class="crayon-num" data-line="crayon-5b8f19d520d5d723297616-3">
3
</div>
<div class="crayon-num crayon-striped-num" data-line="crayon-5b8f19d520d5d723297616-4">
4
</div>
<div class="crayon-num" data-line="crayon-5b8f19d520d5d723297616-5">
5
</div>
<div class="crayon-num crayon-striped-num" data-line="crayon-5b8f19d520d5d723297616-6">
6
</div>
<div class="crayon-num" data-line="crayon-5b8f19d520d5d723297616-7">
7
</div>
<div class="crayon-num crayon-striped-num" data-line="crayon-5b8f19d520d5d723297616-8">
8
</div>
</div></td>
<td><div class="crayon-pre" style="font-size: 13px !important; line-height: 15px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;">
<div id="crayon-5b8f19d520d5d723297616-1" class="crayon-line">
1倍图
</div>
<div id="crayon-5b8f19d520d5d723297616-2" class="crayon-line crayon-striped-line">
 http:// img.xxx.com/abc.jpg_100x100
</div>
<div id="crayon-5b8f19d520d5d723297616-3" class="crayon-line">
 
</div>
<div id="crayon-5b8f19d520d5d723297616-4" class="crayon-line crayon-striped-line">
 2倍图
</div>
<div id="crayon-5b8f19d520d5d723297616-5" class="crayon-line">
 http:// img.xxx.com/abc.jpg_200x200
</div>
<div id="crayon-5b8f19d520d5d723297616-6" class="crayon-line crayon-striped-line">
 
</div>
<div id="crayon-5b8f19d520d5d723297616-7" class="crayon-line">
 3倍图
</div>
<div id="crayon-5b8f19d520d5d723297616-8" class="crayon-line crayon-striped-line">
 http:// img.xxx.com/abc.jpg_300x300
</div>
</div></td>
</tr>
</tbody>
</table>
  1. 定死尺寸

    放弃1屏手机,全部启用2倍图,由于流量会消耗比较大(低端机),因此滚动加载等优化手段就会显得比较重要了。

实验1 – scale对倍图重要吗

这里看一下不同scale下图片的差异。

  • 测试样本:160×160凯尔特人队标logo(一不小心暴露了绿色的血液)
  • 测试容器:160×160 img标签
  • 测试环境: intial-scale分别为1.0 / 0.5 / 0.3333
  • 图片尺寸: 1x(160×160) 2x(320×320) 3x(480×480)

图片 12

测试结论:不同scale下使用不同图片差异非常大。

但是这里需要验证,是否不同scale同一图片差异起到绝对作用。

图片 13

  • 肉眼观看基本无区别,除了用取色器去获取,会发现有色差和部分像素被分割(下面会说到),之外,用不同scale显示同一图片基本没有什么区别。

实验2 – DownSampling

由于上一个实验最后的图片,使用同一scale下,不同倍数的图片,存在色差,这里验证一下。

  • 测试方案

    测试图片:

 图片 14

图片尺寸: 400×300 , 300×225 , 200×150 , 100×75

测试环境: scale = 1.0

测试容器: 100×75的 img元素

由于之前知道了DownSampling概念的存在,这里只是好奇心驱动试验一下。(对自适应其实没有卵用)

DownSampling是说大图放入比图片尺寸小的容器中的时候,出现像素分割成就近色的情况。

测试结果:

图片 15

注:6plus貌似和其他机型不同。

触发情况: 不同颜色像素接触的地方,会出现DownSampling。

图片 16

rem

对于rem要说的不多,看这张图。对于用到px的元素,使用rem统一去管理是很灵活的!

图片 17

字体

无论是采用动态生成viewport或者写死scale,字体都需要适配大屏。之前提出的rem方案被证实在不同手机上显示不一致,这里还是回归成了px。

px最好用双数

两种方案(这里不考虑媒体查询,因为Android碎..,嗯,不说了…)

  1. JS动态计算(常见做法)
根据不同屏幕宽度计算不同字号大小。 1.
定基准值,设计稿是750宽度(2倍屏),字体的大小是24px. 2.
计算指定宽度的字体大小。 var fontSize = width / 750 * 24 ;

<table>
<colgroup>
<col style="width: 50%" />
<col style="width: 50%" />
</colgroup>
<tbody>
<tr class="odd">
<td><div class="crayon-nums-content" style="font-size: 13px !important; line-height: 15px !important;">
<div class="crayon-num" data-line="crayon-5b8f19d520d62124238623-1">
1
</div>
<div class="crayon-num crayon-striped-num" data-line="crayon-5b8f19d520d62124238623-2">
2
</div>
<div class="crayon-num" data-line="crayon-5b8f19d520d62124238623-3">
3
</div>
<div class="crayon-num crayon-striped-num" data-line="crayon-5b8f19d520d62124238623-4">
4
</div>
<div class="crayon-num" data-line="crayon-5b8f19d520d62124238623-5">
5
</div>
<div class="crayon-num crayon-striped-num" data-line="crayon-5b8f19d520d62124238623-6">
6
</div>
<div class="crayon-num" data-line="crayon-5b8f19d520d62124238623-7">
7
</div>
</div></td>
<td><div class="crayon-pre" style="font-size: 13px !important; line-height: 15px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;">
<div id="crayon-5b8f19d520d62124238623-1" class="crayon-line">
根据不同屏幕宽度计算不同字号大小。
</div>
<div id="crayon-5b8f19d520d62124238623-2" class="crayon-line crayon-striped-line">
 
</div>
<div id="crayon-5b8f19d520d62124238623-3" class="crayon-line">
1. 定基准值,设计稿是750宽度(2倍屏),字体的大小是24px.
</div>
<div id="crayon-5b8f19d520d62124238623-4" class="crayon-line crayon-striped-line">
 
</div>
<div id="crayon-5b8f19d520d62124238623-5" class="crayon-line">
2. 计算指定宽度的字体大小。
</div>
<div id="crayon-5b8f19d520d62124238623-6" class="crayon-line crayon-striped-line">
 
</div>
<div id="crayon-5b8f19d520d62124238623-7" class="crayon-line">
var fontSize = width / 750 * 24 ;
</div>
</div></td>
</tr>
</tbody>
</table>
  1. 根据dpr设定 (比较好的做法)

    ps : 一般时初始化时设置为根元素html的attribute,

JavaScript

window.document.documentElement.setAttribute('dpr',window.devicePixelRatio)

<table>
<colgroup>
<col style="width: 50%" />
<col style="width: 50%" />
</colgroup>
<tbody>
<tr class="odd">
<td><div class="crayon-nums-content" style="font-size: 13px !important; line-height: 15px !important;">
<div class="crayon-num" data-line="crayon-5b8f19d520d65248160001-1">
1
</div>
<div class="crayon-num crayon-striped-num" data-line="crayon-5b8f19d520d65248160001-2">
2
</div>
</div></td>
<td><div class="crayon-pre" style="font-size: 13px !important; line-height: 15px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;">
<div id="crayon-5b8f19d520d65248160001-1" class="crayon-line">
   window.document.documentElement.setAttribute('dpr',window.devicePixelRatio)
</div>
<div id="crayon-5b8f19d520d65248160001-2" class="crayon-line crayon-striped-line">
 
</div>
</div></td>
</tr>
</tbody>
</table>

然后css这样写



CSS

[dpr=1] { font-size=16px; } [dpr=2] { font-size=32px; }

<table>
<colgroup>
<col style="width: 50%" />
<col style="width: 50%" />
</colgroup>
<tbody>
<tr class="odd">
<td><div class="crayon-nums-content" style="font-size: 13px !important; line-height: 15px !important;">
<div class="crayon-num" data-line="crayon-5b8f19d520d69092077898-1">
1
</div>
<div class="crayon-num crayon-striped-num" data-line="crayon-5b8f19d520d69092077898-2">
2
</div>
<div class="crayon-num" data-line="crayon-5b8f19d520d69092077898-3">
3
</div>
<div class="crayon-num crayon-striped-num" data-line="crayon-5b8f19d520d69092077898-4">
4
</div>
<div class="crayon-num" data-line="crayon-5b8f19d520d69092077898-5">
5
</div>
<div class="crayon-num crayon-striped-num" data-line="crayon-5b8f19d520d69092077898-6">
6
</div>
<div class="crayon-num" data-line="crayon-5b8f19d520d69092077898-7">
7
</div>
</div></td>
<td><div class="crayon-pre" style="font-size: 13px !important; line-height: 15px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;">
<div id="crayon-5b8f19d520d69092077898-1" class="crayon-line">
[dpr=1] {
</div>
<div id="crayon-5b8f19d520d69092077898-2" class="crayon-line crayon-striped-line">
       font-size=16px; 
</div>
<div id="crayon-5b8f19d520d69092077898-3" class="crayon-line">
}
</div>
<div id="crayon-5b8f19d520d69092077898-4" class="crayon-line crayon-striped-line">
 
</div>
<div id="crayon-5b8f19d520d69092077898-5" class="crayon-line">
[dpr=2] {
</div>
<div id="crayon-5b8f19d520d69092077898-6" class="crayon-line crayon-striped-line">
       font-size=32px; 
</div>
<div id="crayon-5b8f19d520d69092077898-7" class="crayon-line">
}
</div>
</div></td>
</tr>
</tbody>
</table>

布局

权衡之下,我觉得flex真的灵活方便太多,因此这里给出一个布局demo。大致如下图。(画的比较粗糙..)

(上稿下还原)

图片 18图片 19

基本涵盖:

  • 固定头部
  • 固定底部
  • 多列自适应
  • 高度自定义
  • 内容滚动

为什么flex能够做到百分比做不到的自适应。

比如我们也去学天猫,笃定认为宽度就是375(iPhone6尺寸),那么两个元素flex分别为200和175。

无需计算百分比,在不同的界面上就会自动计算,而且以该浏览器可以识别的最小单位实现,比自己计算的百分比要精准。

图片 20

demo传送门

结论

  1. 写死initial-scale=1.0 对于实现1px问题, 问题比较大。与设计师沟通协商才是最好的解决问题的方法。
  2. 写死initial-scale=1.0 对于不同图片的显示, 采用不同倍图的话,会有一定压缩,但在可接受范围内。(当然,动态生成scale能够完美呈现…)
  3. 布局

    如果采用动态生成viewport方案,就用到rem来还原设计稿(还有rem-px的计算)。成本在效率上。

    如果采用写死initial-scale=1.0方案,就用flex布局,主要成本在flex兼容性上,但是实现非常灵活简单。

后记

viewport的scale的重要性远比我想象的要低很多,我原本以为这就是自适应。

但是后来发现,其实自适应还是回到了远古时代的百分比%,只是现在有更聪明更灵活的方式flex,未来应该有两个方向去自适应。

  • 一个是拥抱vw,vh。(手淘的ml.js十等分宽度,1rem=10vw
  • 一个是更好的使用flex

现在使用后者已经有很多的库可以解决兼容性了,如参考资源最后的一个flex库。

调研的网站并不多,但是百分比仍然是很多人的首选。

参考资源

手淘ml库

手机淘宝

天猫首页

移动端高清、多平适配方案

rem对webapp带来的影响

flex方案 适配到IE10+

 

 

2 赞 10 收藏 评论

图片 21

压缩后的效果

接下来我将使用访问本博客的抓包记录来说明 HTTP/2 头部压缩带来的变化。如何使用 Wireshark 对 HTTPS 网站进行抓包并解密,请看我的这篇文章。

首先直接上图。下图选中的 Stream 是首次访问本站,浏览器发出的请求头:

图片 22

从图片中可以看到这个 HEADERS 流的长度是 206 个字节,而解码后的头部长度有 451 个字节。由此可见,压缩后的头部大小减少了一半多。

然而这就是全部吗?再上一张图。下图选中的 Stream 是点击本站链接后,浏览器发出的请求头:

图片 23

可以看到这一次,HEADERS 流的长度只有 49 个字节,但是解码后的头部长度却有 470 个字节。这一次,压缩后的头部大小几乎只有原始大小的 1/10。

为什么前后两次差距这么大呢?我们把两次的头部信息展开,查看同一个字段两次传输所占用的字节数:

图片 24

图片 25

对比后可以发现,第二次的请求头部之所以非常小,是因为大部分键值对只占用了一个字节。尤其是 UserAgent、Cookie 这样的头部,首次请求中需要占用很多字节,后续请求中都只需要一个字节。

本文由澳门新葡亰平台官网发布于web前端,转载请注明出处:头部压缩技术介绍,移动端自适应方案

TAG标签:
Ctrl+D 将本页面保存为书签,全面了解最新资讯,方便快捷。