# 前端点9图实现

什么是点9图? 我们以一个需求为例子说起, 如果你现在的需求是做一个聊天框的气泡, 设计师给到了这样一张图给你:

显然我们需要根据聊天内容来动态修改图片的尺寸, 但是如果我们只是简单的拉伸图片就会出现下面这种效果:

我们可以看到, 图片虽然被拉伸的, 但是四个角和箭头也被拉伸得变形了. 所以我们想要的效果是什么呢? 我们想要保持四个角以及箭头不变, 只拉伸图片的其余部分. 那么我脑子里首先想到的就是把这张图给拆分成几张不同的图, 这样我再通过拉伸+拼接的方式达到最终的效果.

可是设计师此时不乐意了: 客户端的兄弟只让我给了这一张图就能搞定, 你们前端是不是能力不行? 好家伙, 这能忍么, 我马上掏出了谷歌大法, 然后就找到了点9图这个东西. 通过点9图就能够去控制图片的拉伸范围, 达到我们保留某些部分拉伸某些部分的效果. 但是这是客户端的东西, 用css能搞出这种效果么, 其实也是可以的. 我们可以通过border-image-slice来达到这个效果.

# css 实现点9图

border-image-slice具体的用法我们可以参考MDN (opens new window), 如果你看不明白它在说什么, 不要着急, 让我来给你解释一番.

首先大家肯定都知道border-image-source这个属性, 所以我也就不过多介绍了. 而border-image-slice接受的四个值, 其实就是把border-image-source给分成了9个不同区域的图片. 例如我们给了一个这样的值:

border-image-slice: 1 2 3 4;

那么我们上面的那张聊天气泡将会被这样分割:

1 2 3 4分别代表了上 右 下 左四条线, 它们的值就是这条线离边界的距离. 这四个值也能接受百分比为参数, 同时如果你少传几个值, 这几个值会按照一定的规则来应用到四个位置上, 就像我们平时用的paddingmargin一样.

那么将图分成了9个部分之后又怎么样呢? 在图片被拉伸的时候1 3 7 9四个位置的图片将被保留, 也就是不会被拉伸. 2 4 6 8区域的图片才会被拉伸. 这样是不是就达到了我们想要的拉伸部分图片的效果, 那么我们就实际上手来实验一下:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <style>
    .wrapper {
      width: 200px;
    }

    .border {
      border:20px solid transparent;

      border-image-source: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAF8AAABSCAYAAAAy0ggrAAALxklEQVR4nOxde2wcV7n/zjx2du19eO14/UhuHCeu87pu2iR96t723kaRqOCPUqlVKxIQVaVKBQFFVSlUBSHUIvEHEuIvEFELLRKKhFCrClQKfdA2paHk5caKndixnbXj2Ou1970zs3MOmvHM+sx41mtnZ+PYM7/oZFb27Mye3/nO73zn+74dM+BhzcDV6brIenz2Z4+0du6IHOJ4/kGCIIYAuhBCoTrdvy7AmAwiIEVC0PvFQuGd7z326jkAIFQD6lgVqA6fEdHtpde/dldjSDgaCPgeaouFhfZYJCDwPOP38xzPc/W4f92QzRZLGGMyk8yK12bSxeRcdgiXlNe++/Bvjqljs9pBcLLztLUzh4/cKjz45btfiEYbn+zZEQt2xJoCK/pE6wRqJ/N5qXTx8lR2PJ78s1iQXn/+8Vf+pg8C1k9btrtOkW9cR11DmB8fO7I/GBGO9vZ2Ht3Z0x5CiEEYJCgqCSjga6AQCRSSB0xKDt3+xoBngoCABT/TAn62FXgU1jqeSKbFU+dG5/IZ6cVnHz32OwBQbORoCZwg30T8i7969LbYluibt+7ZGtnc3hyQSQ4ypRHIK1NA9I+x3q3f0FSOaYQwvx0CTDvkcsXSZ2dGkolE5ofPP/bqKysZANahz6IRf+DA9ob9h3uf2d/XfffmjmhDVhmHWeksiDizRBDXM4x+YCJpM1kmaQj7O5hYS0S4lkjdeccDvac+eLP/CnWqrZHX6moiinzu0efu+8Gu3o6vbu5sapiTz8O8PAiY4A1BuB00ZglAQZmBGekkCA2Eu62vq6kxJBxRVUo37ooc12L5JuK//4uH7+nsan7pwL7uaEq+gHLKpPbBNirxJpCFWSAqsxALb+ckSdnWd29X8r03+s8uN+GdsHyN/Ka28Fd27ugMFnAc5ZQJ9xBPzQB1fZuTzkNPd3sw3Bx8YevWiF/nx3HZKa87Dz91X0coHPhSa0wIpOQhUIVGbW76p/WXEM2xYAJJrrmp0ffQ0/cd1DeyDMVXzeTTHg67+/b2L3R2NPkzpWHA6sdwi8lbYHQ7I49AaywktLQF/99Cvgm1WD6jrxlcQ1C4p6UlIKij7mYY4q7KTzQK/kDA9786+awd+bXEdsp6z7JoG8unuBLBC9s7l1o+UAPABtIcL7A9W7Y0++PxpKRzhenFtxbZQYblIwZaMJ/yIqSwSK2IE8DzLOq9q7OlHrKDytJDgFFwfl0FyeoNGWfVA/KxnEDJjomj65UdZCafaPEa9Wi4XW4FQXr/keprYyjJirHRWmLoTriaiBC8QQIHzkI1RQVjxhJmL6PWBVezfs3eCYENEzlzCGTB+K2kI4MhRzJZWJcb2EDBs+uFKZtCCMiE2Fo9OLDJWrgocTvlFbB08XNMdsz38GRnCVTLJ7Jc0Qt0hnzsyY4BUwC/ChHObIzc7FsuhypbfYdkx/N27ECqEOEM+YA92dFBJ20X9j+V4ZDlYyh7PG5nn0I9yS+v4piAZ/k6zJbvrOyY/XsA5umfHHqcZ1FQi2W4mnYbOES+lXT2Wz89fDgYEY5s39Hxf7fvuyU8jz5EyorqtNyDBToqF4athHxTUdQ3Xj7UHQ4HntgUCz9x8MAt4ba2FkFdcGezGBQtj+lu7k2yg8ky1C9Pvqn2Um3P//KLzwRDvu/09fWEeno2N7Asg/LyNCTFAVCw4nrirdBcTbny7+3IXyIxz/388ONcwHdk586td9zatyPkD/gYGWdhJj8A+dKMRzqFWhZcOgLHfPvlQwcDId/Rzo7mRw7euTsSjYZ5TGRIFgYgLY1p/r2b87VVsULyTRLzyNdvi3bva/tmuKnhqYMHd4W3dbUH1MtkpXGYEwehRETP2leA1Vi+RvyTP/qf7e2dkQ/27ulu3Lu3O8jzHCqW5iFR/BxEJeWRXgXmBXflmyyN/Kao/+iB23tDu3Zva1RwEWby/ZCVJ8q5WY/4VUDb4a7M1dS0Pj6a+sP70pnQeHz0aNd/F8JCg8IZ4VFCHz3YghhJdIP7ZWBUKRtJXu7zf07mP31n9NPN3U35+Wm+j2CCQ83gY1hACOkj5BWJVITGkd4mLnG5of6p4xOX07Ow4HQq1FeGyuQjuvwPAHynP4oPn/9s8q1AqInPJYUtgkCYUATxyJqK96aBCTQ3KvkDp68dvzpWnXxE1ZeoA8AX8yXm3ImJoUxKupBL+UAq+jaFmhEv+IE1pYSRNwYGzJbP5gbPVicfbAag3KbG0+mzn0wO8D5fOjnN+xBigpEo+FhWp594s8AAbfnxYZX86RVbvt21yp7T8PnZ+L8/uHLS3xiCfDrQzvkAhZsQzzAL64EHG8s/tzLywWYAjO+UKnS7cGr60uR4+mwx61PEvNDaEAQu0Ig0z8nt5YK05V+5yOSG+mdWTD5Q4oFtyC8Zr+dnCtmBz64NyhJMEdLYtW0X26R6RG6P6tOWr5J/sf/q8atjWVvyjeoFemNm7AzUkyUAKABADgAyAJAGgHmqpU68PXqSlCtkF/PoXlt9MoU+G1ODgSnLl/VmzAJCMAFGU5319Y3yemM14QXT+6ij3QAo1DkMUJbv+iQuBS2wtow9LpdMIZbXhBoAQn0zha8WvXMrnEig01cwiJd14tU1QUKEEGLRfLfCmkxZJpG1quoFqxdkSFCpWmWWe+F8xZrVM8KYkGX2ae6F03U7pmubbuItuEvhVSmvJZbnxRHyq/mzbsWC7FRech0sEfdkx4pqmu/Qt8Y9tu1QbZPljOx43Nuint5OGRhjojqcQAB5srMA1ekWiyJOJ/PZSudcL/mm2I+ikLmCSHCAX5hJbube6HtJASKKErk6ni/Y/FqDI7IjF0rxbKpUVjdkXXxd1Iy+5zKyUiwoY5Kkqb6tHtRCfnmXm0qKpxMTsljO4rh0o4uoNNbctCIV8/J5G+LLr50gn/T/a+7j8ZFskVAPz1zrRMbaJE8W2+RlsTg1ljthecayI7JjCjGPDaZmpifyH14dLRboEoi1loAbKjdUdCszL5euxgviJ+/NnKZSh0t2orXGdsrRzbHB9F8GTgn3x/5L8CNmUXjc4IZaQ4oXTmUz8ZH0MTkvS1T+e4nl1/JQU4aqcuMnRnPp9q2BqL+B297a4RfK8rPBBwBRSXMGASQmi+LJdxMfvfnbsV8DQFbPgUt6+N30YKLrJd9aYKWVGcYvZ0d4H9/c0uHfFonyvOkdxGa5X+fQCIdF4nMZufTR24nkUH/qtYmR3CUAyANA0UJ+GbWQD9YSQ1kkiOVRjuOZe4Mhjg1ZBwAWx329DgTS/ytbPCwQn8/JpU/eSSSH+9O///Ctqbd1q1fJF/Xo2pLHcdX6LGWwlhbOTBbToqRcnJ2WQPAxnS1tfh9j1LMR6o2WRWo9NAYtWhvtVieniuKJv87MDfWnj//9j5N/0stscjaSY0KtCy7RLyxRFc7swMn5c5fOZK6UJFycHM8/0L3bH4x1CoGNov+GxRcLijI6VMgMnc6euzKce/fku4l/UBZvlZslC24t2yFa91XSBQAIAEAjAISMtueOaN+OPcH7WzcL+yMtxB9uVvwcD4xPAI7l1lfeUSpAqaQAyaWRmJ5lxLlpcm3qSvHj99+YfEu3droV9AEoVfJ2au08okpIjAHw6wOgtqB+bNjUIcR690X2xTqEvbyPDQl+dhPDga/G+99QiAVlVlFIYT4hX4wPZz+/cCY9pFt5Trf4nN5orVfs9B4cynjTi666wPr0AVBnQYPeAvrPBP0czvKgz5t9BhBL1YZRSlnULTyvt4LerMSDHflO/Z0s4wYyNb0Uaj0o6OT7LORbH21+sw0CoY7YUscq6+SL+tF4befT2650TnXW6vmUv15EzQYr8evN8gllyXQhsXE0SLdqfEUXw8lO2/r+1EBwFuJvdqsHi+USa0jF0hSbOM6yvl09OrzEPabIpp8rXMnqb5ZBsBJHbLQfWwgn1aSGRr06arVmK9lMhfNuVlSaAdb4Jqxm834jOo1sXtv97GaGXTLENkHiYZ3A+2sPawiP/DXEfwIAAP//8temndo6XsAAAAAASUVORK5CYII=");

      border-image-slice:65% 33% 33% 33%;

      border-image-repeat:repeat;
      

      width: 200px;
      height: 500px;

    }
  </style>
</head>
<body>
  <div class="wrapper">
    <div class="border">
    </div>
  </div>
</body>
</html>

我们通过border-image-slice将图片分割为9个部分, 因为我们想要保留四个角和箭头, 所以根据我们之前的介绍, 我们的第一个值要给的很大才行, 因为这样才能保证箭头被分割在区域1内, 不被拉伸. 我们最后得到的结果是这样的:

边缘倒是符合我们的预期了...但是为什么中间是空的? 其实我们之前介绍的时候漏掉了一个区域5, 同时border-image-slice还接受一个参数fill, 如果传了这个参数, 那么区域5将会铺满空余部分.

border-image-slice:65% 33% 33% 33% fill;

所以我们修改一下代码:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <style>
    .wrapper {
      width: 200px;
    }

    .border {
      border:20px solid transparent;

      border-image-source: url("data:image/png;base64,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");

      border-image-slice:65% 33% 33% 33% fill;

      border-image-repeat:repeat;
    }
  </style>
</head>
<body>
  <div class="wrapper">
    <div class="border">
      hello worldhello worldhello worldhello worldhello world
      hello worldhello worldhello worldhello worldhello world
      hello worldhello worldhello worldhello worldhello world
      hello worldhello worldhello worldhello worldhello world
      hello worldhello worldhello worldhello worldhello world
      hello worldhello worldhello worldhello worldhello world
      hello worldhello worldhello worldhello worldhello world
      hello worldhello worldhello worldhello worldhello world
    </div>
  </div>
</body>
</html>

最后得到的效果就是:

同时这个属性的兼容性也还不错:

这样, 我们就用一张图实现了我们想要的部分拉伸效果, 设计师再也不会说我们能力不行了!