您的位置:奥门新浦京网址 > Wed前段 > 缓存原理解析,精晓SVG坐标系和转换

缓存原理解析,精晓SVG坐标系和转换

发布时间:2019-10-20 03:41编辑:Wed前段浏览(183)

    减少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>

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

    图片 1

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

    图片 2

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

    图片 3

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

    图片 4

     

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

    图片 5

     

    图片 6

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

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

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

    本次博文就写到这!!!

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

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

      互相学习,共同进步!

    2 赞 3 收藏 评论

    图片 7

    理解SVG坐标系和变换:视窗,viewBox和preserveAspectRatio

    2015/09/23 · HTML5 · SVG

    原文出处: SaraSoueidan   译文出处:Blueed(@Ivan_z3)   

    SVG元素不像HTML元素一样由CSS盒模型管理。这使得我们可以更加灵活定位和变换这些元素-也许一眼看上去不太直观。然而,一旦你理解了SVG坐标系和变换,操纵SVG会非常简单并且很有意义。本篇文章中我们将讨论控制SVG坐标系的最重要的三个属性:viewport, viewBox, 和 preserveAspectRatio

    这是本系列三篇文章中的第一篇,这篇文章讨论SVG中的坐标系和变换。

    • 理解SVG坐标系和变换(第一部分)-viewport,viewBox,和preserveAspectRatio
    • 理解SVG坐标系和变换(第二部分)-transform属性
    • 理解SVG坐标系和变换(第三部分)-建立新视窗

    为了使文中的内容和解释更形象化,我创建了一个互动演示,你可以任意改变viewBox 和 preserveAspectRatio的值。

    在线案例

    这个例子只是主要内容的一小部分,所以看完请回来继续阅读这篇文章

    浏览器 HTTP 缓存原理分析

    2015/10/27 · HTML5 · 1 评论 · HTTP

    原文出处: 桃子夭夭   

    以前项目中遇到了很多浏览器缓存相关的问题,也在网上查过资料,搞过服务器的配置,来确保客户端加载服务器资源的速度和资源有效性。最近仔细看了下http协议中和缓存相关的一些属性,总结一下。

    SVG画布

    canvas是绘制SVG内容的一块空间或区域。理论上,画布在所有维度上都是无限的。所以SVG可以是任意尺寸。然而,SVG通过有限区域展现在屏幕上,这个区域叫做viewport。SVG中超出视窗边界的区域会被裁切并且隐藏。

    浏览器缓存原理

    视窗

    视窗是一块SVG可见的区域。你可以把视窗当做一个窗户,透过这个窗户可以看到特定的景象,景象也许完整,也许只有一部分。

    SVG的视窗类似访问当前页面的浏览器视窗。网页可以是任何尺寸;它可以大于视窗宽度,并且在大多数情况下都比视窗高度要高。然而,每个时刻只有一部分网页内容是透过视窗可见的。

    整个SVG画布可见还是部分可见取决于这个canvas的尺寸以及preserveAspectRatio属性值。你现在不需要担心这些;我们之后会讨论更多的细节。

    你可以在最外层<svg>元素上使用widthheight属性声明视窗尺寸。

    <!-- the viewport will be 800px by 600px --> <svg width="800" height="600"> <!-- SVG content drawn onto the SVG canvas --> </svg>

    1
    2
    3
    4
    <!-- the viewport will be 800px by 600px -->
    <svg width="800" height="600">
        <!-- SVG content drawn onto the SVG canvas -->
    </svg>

    在SVG中,值可以带单位也不可以不带。一个不带单位的值可以在用户空间中通过用户单位声明。如果值通过用户单位声明,那么这个值的数值被认为和px单位的数值一样。这意味着上述例子将被渲染为800px*600px的视窗。

    你也可以使用单位来声明值。SVG支持的长度单位有:emexpxptpccmmmin和百分比。

    一旦你设定最外层SVG元素的宽高,浏览器会建立初始视窗坐标系和初始用户坐标系。

    文字版描述

    ①浏览器第一次访问服务器资源 /index.html

    在浏览器中没有缓存文件,直接向服务器发送请求。

    服务器返回  200 OK,实体中返回 index.html文件内容,并设置一个缓存过期时间,一个文件修改时间,一个根据index.html内容计算出来的实体标记Entity Tag,简称Etag。

    浏览器将/index.html路径的请求缓存到本地。

    ②浏览器第二次访问服务器资源 /index.html

    由于本地已经有了此路径下的缓存文件,所以这一次就不直接向服务器发送请求了。

    首先进行缓存过期判断。浏览器根据①中设置缓存过期时间判断缓存文件是否过期。

    情景一:若没有过期,则不向服务器发送请求,直接使用缓存中的结果,此时我们在浏览器控制台中可以看到  200 OK(from cache) ,此时的情况就是完全使用缓存,浏览器和服务器没有任何交互的。

    情景二:若已过期,则向服务器发送请求,此时请求中会带上①中设置的文件修改时间,和Etag

    然后进行资源更新判断。服务器根据浏览器传过来的文件修改时间,判断自浏览器上一次请求之后,文件是不是没有被修改过;根据Etag,判断文件内容自上一次请求之后,有没有发生变化

    情形一:若两种判断的结论都是文件没有被修改过,则服务器就不给浏览器发index.html的内容了,直接告诉它,文件没有被修改过,你用你那边的缓存吧—— 304 Not Modified,此时浏览器就会从本地缓存中获取index.html的内容。此时的情况叫协议缓存,浏览器和服务器之间有一次请求交互。

    情形二:若修改时间和文件内容判断有任意一个没有通过,则服务器会受理此次请求,之后的操作同

    ①我的文字表达能力可能有限,为了尽量把这个流程描述清楚一点,下面

    初始坐标系

    初始视窗坐标系是一个建立在视窗上的坐标系。原点(0,0)在视窗的左上角,X轴正向指向右,Y轴正向指向下,初始坐标系中的一个单位等于视窗中的一个”像素”。这个坐标系统类似于通过CSS盒模型在HTML元素上建立的坐标系。

    初始用户坐标系是建立在SVG画布上的坐标系。这个坐标系一开始和视窗坐标系完全一样-它自己的原点位于视窗左上角,x轴正向指向右,y轴正向指向下。使用viewBox属性,初始用户坐标系统-也称当前坐标系,或使用中的用户空间-可以变成与视窗坐标系不一样的坐标系。我们在一下节中讨论如何改变坐标系。

    到现在为止,我们还没有声明viewBox属性值。SVG画布的用户坐标系统和视窗坐标系统完全一样。

    下图中,视窗坐标系的”标尺”是灰色的,用户坐标系(viewBox)的是蓝色的。由于它们在这个时候完全相同,所以两个坐标系统重合了。图片 8

    上面SVG中的鹦鹉的外框边界是200个单位(这个例子中是200个像素)宽和300个单位高。鹦鹉基于初始坐标系在画布中绘制。

    新用户空间(即,新当前坐标系)也可以通过在容器元素或图形元素上使用transform属性来声明变换。我们将在这篇文章的第二部分讨论关于变换的内容,更多细节在第三部分和最后部分中讨论。

    一图以蔽之

    图片 9

    图片 10

    viewBox

    我喜欢把viewBox理解为“真实”坐标系。首先,它是用来把SVG图形绘制到画布上的坐标系。这个坐标系可以大于视窗也可以小于视窗,在视窗中可以整体可见或部分可见。

    在之前的章节里,这个坐标系-用户坐标系-和视窗坐标系完全一样。因为我们没有把它声明成其他坐标系。这就是为什么所有的定位和绘制看起来是基于视窗坐标系的。因为我们一旦创建视窗坐标系(使用widthheight),浏览器默认创建一个完全相同的用户坐标系。

    你可以使用viewBox属性声明自己的用户坐标系。如果你选择的用户坐标系统和视窗坐标系统宽高比(高比宽)相同,它会延伸来适应整个视窗区域(一分钟内我们就来讲个例子)。然而,如果你的用户坐标系宽高比不同,你可以用preserveAspectRatio属性来声明整个系统在视窗内是否可见,你也可以用它来声明在视窗中如何定位。我们会在下个章节里讨论这一情况的细节和例子。在这一章里,我们只讨论viewBox的宽高比符合视窗的情况-在这些例子中,preserveAspectRatio不产生影响。

    在我们讨论这些例子前,我们回顾一下viewBox的语法。

    缓存相关首部字段

    viewBox语法

    viewBox属性接收四个参数值,包括:<min-x>, <min-y>, width 和 height

    CSS

    viewBox = <min-x> <min-y> <width> <height>

    1
    viewBox = <min-x> <min-y> <width> <height>

    <min-x> 和 <min-y> 值决定viewBox的左上角,widthheight决定视窗的宽高。这里要注意视窗的宽高不一定和父<svg>元素的宽高一样。<width><height>值为负数是不合法的。值为0的话会禁止元素的渲染。

    注意视窗的宽度也可以在CSS中设置为任何值。例如:设置width:100%会让SVG视窗在文档中自适应。无论viewBox的值是多少,它会映射为外层SVG元素计算出的宽度值。

    设置viewBox的例子如下:

    <!-- The viewBox in this example is equal to the viewport, but it can be different --> <svg width="800" height="600" viewBox="0 0 800 600"> <!-- SVG content drawn onto the SVG canvas --> </svg>

    1
    2
    3
    4
    <!-- The viewBox in this example is equal to the viewport, but it can be different -->
    <svg width="800" height="600" viewBox="0 0 800 600">
        <!-- SVG content drawn onto the SVG canvas -->
    </svg>

    如果你之前在其他地方看到过viewBox,你也许会看到一些解释说你可以用viewBox属性通过缩放或者变化使SVG图形变换。这是真的。我将深入探究并且告诉你甚至可以使用viewBox来切割SVG图形。

    理解viewBox和视窗之间差异最好的方法是亲身观察。所以让我们看一些例子。我们将从viewBox和viewport的宽高比相同的例子开始,所以我们还不需要深入了解preserveAspectRatio

    request缓存相关首部字段

    图片 11

    ① cache-control  用来做缓存过期判断

    常用指令:

    no-cache  不直接使用缓存,始终向服务器发起请求

    max-age  缓存过期时间,是一个时间数值,比如3600秒,设置为0的时候效果等同于no-cache

    s-maxage  给缓存代理用的指令,对直接返回资源的server无效,当s-maxage生效时,会忽略max-age的值

    only-if-cached 若有缓存,则只使用缓存,若缓存文件出问题了,请求也会出问题

    ② Pragma  用来做缓存过期判断

       它可以取值no-cache

       这是一个http1.0遗留的字段,当它和cache-control同时存在的时候,会被cache-control覆盖

    ③ if-match / if-none-match  用来做资源更新判断

       这个指令会把缓存中的Etag传给服务器,服务器用它来和服务器端的资源Etag进行对比,若不一致则证明资源被修改了,需要响应请求为 200 OK

    ④ if-modified-since  用来做资源更新判断

        这个指令会把文件的上一次缓存中的文件的更新时间传给服务器,服务器判断文件在这个时间点后是否被修改,如果被修改过则需要响应请求为200 OK

    与viewport宽高比相同的viewBox

    我们从一个简单的例子开始。这个例子中的viewBox的尺寸是视窗尺寸的一半。在这个例子中我们不改变viewBox的原点,所以<min-x><min-y>都设置成0。viewBox的宽高是viewport宽高的一半。这意味着我们保持宽高比。

    <svg width="800" height="600" viewBox="0 0 400 300"> <!-- SVG content drawn onto the SVG canvas --> </svg>

    1
    2
    3
    <svg width="800" height="600" viewBox="0 0 400 300">
        <!-- SVG content drawn onto the SVG canvas -->
    </svg>

    所以,viewBox="0 0 400 300"到底有什么用呢?

    • 它声明了一个特定的区域,canvas横跨左上角的点(0,0)到点(400,300)
    • SVG图像被这个区域裁切
    • 区域被拉伸(类似缩放效果)来充满整个视窗。
    • 用户坐标系被映射到视窗坐标系-在这种情况下-一个用户单位等于两个视窗单位。

    下面的图片展示了在我们例子中把上面的viewBox应用到<svg> 画布中的效果。灰色单位代表视窗坐标系,蓝色坐标系代表viewBox建立的用户坐标系。

    图片 12

    任何在SVG画布中画的内容都会被对应到新的用户坐标系中。

    我喜欢像Google地图一样通过viewBox把SVG画布形象化。在Google地图中你可以在特定区域缩放;这个区域是唯一可见的,并且在浏览器视窗中按比例增加。然而,你知道地图的剩余部分还在那里,但是不可见因为它超出视窗的边界-被裁切了。

    现在让我们试着改变<min-x><min-y>的值。都设置为100。你可以设置成任何你想要的值。宽高比还是和视窗的宽高比一样。

    <svg width="800" height="600" viewBox="100 100 200 150"> <!-- SVG content drawn onto the SVG canvas --> </svg>

    1
    2
    3
    <svg width="800" height="600" viewBox="100 100 200 150">
        <!-- SVG content drawn onto the SVG canvas -->
    </svg>

    添加viewBox="100 100 200 150"的效果和之前例子中一样都是裁切的效果。图形被裁切然后拉伸来充满整个视窗区域。

    图片 13

    再一次,用户坐标系被映射到视窗坐标系-200用户单位映射为800视窗单位因此每个用户单位等于四个视窗单位。结果像你看到的那样是放大的效果。

    另外注意,在这个时候,为<min-x><min-y>声明非0的值对图形有变换的效果;更加特别的是,SVG 画布看起来向上拉伸100个单位,向左拉伸100个单位(transform="translate(-100 -100)")。

    的确,作为规范说明,viewBox属性的影响在于用户代理自动添加适当的变换矩阵来把用户空间中具体的矩形映射到指定区域的边界(通常是视窗)”

    这是一个很棒的说明我们之前已经提到的内容的方法:图形被裁切然后被缩放以适应视窗。这个说明随后增加了一个注释:“在一些情况下用户代理在缩放变换之外需要增加一个移动变换。例如,在最外层的svg元素上,如果viewBox属性对<min-x><min-y>声明非0值得那么就需要移动变换。”

    为了更好演示移动变换,让我们试着给<min-x><min-y>添加-100。移动效果类似transform="translate(100 100)";这意味着图形会在切割和缩放后移动到右下方。回顾倒数第二个裁切尺寸为400*300的例子,添加新的无效<min-x><min-y>值,新的代码如下:

    <svg width="800" height="600" viewBox="-100 -100 300 200"> <!-- SVG content drawn onto the SVG canvas --> </svg>

    1
    2
    3
    <svg width="800" height="600" viewBox="-100 -100 300 200">
        <!-- SVG content drawn onto the SVG canvas -->
    </svg>

    给图形添加上述viewBox transformation的结果如下图所示:图片 14

    注意,与transform属性不同,因为viewBox自动添加的tranfomation不会影响有vewBox属性的元素的x,y,宽和高等属性。因此,在上述例子中展示的带有width,heightviewBox属性的svg元素,widthheight属性代表添加viewBox 变换之前的坐标系中的值。在上述例子中你可以看到初始(灰色)viewport坐标系甚至在<svg>上使用了viewBox属性后仍然没有影响。

    另一方面,像tranform属性一样,它给所有其他属性和后代元素建立了一个新的坐标系。你还可以看到在上述例子中,用户坐标系是新建立的-它不是保持像初始用户坐标系和使用viewBox前的视窗坐标系一样。任何<svg>后代会在这个的用户坐标系中定位和确定尺寸,而不是初始坐标系。

    最后一个viewBox的例子和前一个类似,但是它不是切割画布,我们将在viewport里扩展它并看它如何影响图形。我们将声明一个宽高比视窗大的viewBox,并依然保持viewport的宽高比。我们在下一章里讨论不同的宽高比。

    在这个例子中,我们将viewBox的尺寸设为viewport的1.5倍。

    <svg width="800" height="600" viewBox="0 0 1200 900"> <!-- SVG content drawn onto the SVG canvas --> </svg>

    1
    2
    3
    <svg width="800" height="600" viewBox="0 0 1200 900">
        <!-- SVG content drawn onto the SVG canvas -->
    </svg>

    现在用户坐标系会被放大到1200*900。它会被映射到视窗坐标系,用户坐标系中的每一个单位水平方向上等于视窗坐标系中的viewport-width / viewBox-width,竖直方向上等于viewport-height / viewBox-height。这意味着,在这种情况下,每一个用户坐标系中的x-units等于viewport坐标系中的0.66x-units,每个用户y-unit映射成0.66的viewport y-units。

    当然,理解这些最好的方法是把结果视觉化。viewBox被缩放到适应下图所示的viewport。因为图形在画布里基于新的用户坐标系绘制的,而不是视窗坐标系,它看起来比视窗小。图片 15

    到目前为止,我们所有的例子的宽高比都和视窗一致。但是如果viewBox中声明的宽高比和视窗中的不一样会发生什么呢?例如,试想我们把视窗的尺寸设为1000*500。宽高比不再和视窗的一样。在例子中使用viewBox="0 0 1000 500"的结果如下图:图片 16

    用户坐标系。因此图形在视窗中定位:

    • 整个viewBox适应视窗。
    • 保持viewBox的宽高比。viewBox没有被拉伸来覆盖视窗区域。
    • viewBox在视窗中水平垂直居中。

    这是默认表现。那用什么控制表现呢?如果我们想改变视窗中viewBox的位置呢?这就需要用到preserveAspectRatio属性了。

    response缓存相关首部字段

    图片 17

    ① cache-control  用来设置缓存过期时间

    常用指令:

    no-cache  让客户端不直接使用缓存,始终向服务器发起请求,不设置默认是这个,上边截图中的请求就是省略了,所以客户端不会直接使用缓存。

    max-age  缓存过期时间,是一个时间数值,比如3600秒,设置为0的时候效果等同于no-cache

    s-maxage  给缓存代理用的指令,对直接返回资源的server无效,当s-maxage生效时,会忽略max-age的值

    private/public  默认是private,只在一个浏览器中缓存,设置为public时缓存可被多个用户共享

    ② Etag 用来设置根据资源内容生成的实体标签

        这个值有强tag和弱tag,区别是计算方式不同,只有强tag才会在资源被更新的时候立即发生变化,请求首部中的if-match/if-none-match字段就会传回这个值给服务端

    ③ age

       这个字段用来告诉客户端,这个response是在多久前被创建的,单位为秒,缓存服务器返回资源的时候必须创建此字段

    preserveAspectRatio属性

    preserveAspectRatio属性强制统一缩放比来保持图形的宽高比。

    如果你用不同于视窗的宽高比定义用户坐标系,如果像我们在之前的例子中看到的那样浏览器拉伸viewBox来适应视窗,宽高比的不同会导致图形在某些方向上扭曲。所以如果上一个例子中的viewBox被拉伸以在所有方向上适应视窗,图形看起来如下:图片 18

    当给viewBox设置0 0 200 300的值时扭曲显而易见(显然这很不理想),这个值小于视窗尺寸。我故意选择这个尺寸从而让viewBox匹配鹦鹉边界盒子的尺寸。如果浏览器拉伸图像来适应整个视窗,看起来会像下面这样:图片 19

    preserveAspectRatio属性让你可以在保持宽高比的情况下强制统一viewBox的缩放比,并且如果不想用默认居中你可以声明viewBox在视窗中的位置。

    实体首部缓存相关字段

    response的head里边可能还包括实体首部,实体首部是紧跟在response首部后边的。

    ①last-modified-time ——用来设置资源最后修改时间

    ②Exprire —— 设置文件过期时间

    这个字段的作用和cache-control相同,不同的是它直接指定一个缓存过期时间点,容易受客户端时间的影响。

    这也是一个遗留的字段,和cache-control同时存在的时候会被后者覆盖

    本文由奥门新浦京网址发布于Wed前段,转载请注明出处:缓存原理解析,精晓SVG坐标系和转换

    关键词: