前言
一些游戏中的窗口通常都有一些炫酷的效果
比如这样的

这样的

还有这样的

哎等等,你发现没有,后面两张的窗口长得很相似,只是第二张比较矮,第三张比较高,那么是不是要做两张素材来支持这种形式呢?
当然不用,在游戏开发中,通常会使用一种叫做 "9宫格缩放(9-slice scaling) " 的技术,这是一种非常常见且极其实用的图形技术,9-slice scaling 是一种对位图图像进行缩放的方法,它将图像划分成 9 个区域(3 行 × 3 列),像这样
css
╔════╦═══════╦════╗
║ TL ║ Top ║ TR ║
╠════╬═══════╬════╣
║ L ║Center ║ R ║
╠════╬═══════╬════╣
║ BL ║ Bottom║ BR ║
╚════╩═══════╩════╝
四个角(TL, TR, BL, BR) :固定尺寸,不缩放,用来保持 UI 的边角样式(圆角、高光、装饰等)。
四条边(Top, Bottom, Left, Right) :只在一个方向拉伸:
- Top/Bottom 水平拉伸
- Left/Right 垂直拉伸
中心区域(Center) :在 水平和垂直方向都可以拉伸,填满剩下的空间。
不得不说,发明九宫格缩放的人就是个天才,这大大减少了美术资源用量。
聪明的你肯定联想到了你那睿智的 UI 同事,他也总是给你一些让你抓耳挠腮的背景图,让你在使用时总是有图片拉伸变形的问题。
那么前端有没有类似的用法呢?
有的兄弟有的
哦?你以为我又要介绍某个 auto 系列插件是吗?

还真让你猜错了,今天我要说的是几个 CSS 属性。它们分别是:
- border-image-source
- border-image-slice
- border-image-repeat
通过这三个属性,就可以做到让图片做到不变形的拉伸效果。
现在拿一个图片举例

可以看到,我拿红线将在此图上花了一个井字形,这个井字可不是随便画的,它大有来头,它标记了我允许它形变的地方,和保持不变的地方。

这样四个角将会保持我们目前看到的样子(原始比例),当图片宽度或高度改变时,只会改变可拉伸区域的表现。
这张图片的原始尺寸是 1064*141,且上面的截图是以原始尺寸展示的。从此图来看(梯形),它大概率要承受比它更长或更短的内容,目测一下,可以看到右上角、左上角的不允许形变区域大概是 50px、而左右不允许形变区域大概是 200px、由于这张梯形图且不考虑上下拉伸,所以下面则不能设置固定部分,否则会使左右两边发生断层(有时斜率不一致)。
所以我们这么写
css
.frame-container {
/* 1. 先设定边框宽度(上 右 下 左)*/
border-width: 50px 200px 0px 200px;
border-style: solid;
border-color: transparent; /* 透明即可,边框颜色会被 image 覆盖 */
}
我们将正常的边框属性按照刚刚目测的数值写成这样,现在看起来无事发生,因为边框是透明的。
那么我们接着写:
css
.frame-container {
/* 1. 先设定边框宽度(上 右 下 左)*/
border-width: 50px 200px 0 200px;
border-style: solid;
border-color: transparent; /* 透明即可,边框颜色会被 image 覆盖 */
/* 2. 指定 border-image 源图片 */
border-image-source: url('./a.png');
/* 为了演示,就给它一个动态宽度和高度 */
width: 1064px;
height: 141px;
/* 3. 指定 slice,也就是按 30px / 10px / 20px / 15px 把图片分成 9 块 */
border-image-slice: 50 200 0 200 fill;
}
这里为了方便演示,我们将容器大小设置为图片的原始大小,目前的效果看起来有点奇怪,左右两边看起来被折断了。
css
.frame-container {
/* 1. 先设定边框宽度(上 右 下 左)*/
border-width: 50px 200px 0 200px;
border-style: solid;
border-color: transparent; /* 透明即可,边框颜色会被 image 覆盖 */
/* 2. 指定 border-image 源图片 */
border-image-source: url('./a.png');
/* 为了演示,就给它一个动态宽度和高度 */
width: 1064px;
height: 141px;
/* 3. 指定 slice,也就是按 30px / 10px / 20px / 15px 把图片分成 9 块 */
border-image-slice: 50 200 0 200 fill;
/* 4. 指定拉伸(stretch)------
上边/下边 拉伸水平;左边/右边 拉伸垂直;中间那块自由拉伸/填充 */
border-image-repeat: stretch;
transition: 1s;
/* 5. 你可以给中间区域再加个背景,比如纯白或者另一张图 */
background-color: white;
/* 如果想用内容图,也可以启用下面这行: */
/* background: url("content.png") no-repeat center/cover; */
box-sizing: border-box; /* 确保 width/height 包括 border */
}
加上 box-sizing: border-box; 图片看起来正常多了,我还加了其他必须的代码,现在这张图已经具备我们开头说的能力了。
加一个 hover 简单看一下:
css
/* 鼠标移进去改变尺寸,看看效果,模拟内容变少 */
.frame-container:hover {
width: 400px;
height: 141px;
}

总结
通过上面的演示,可以发现,通过这种方式可以实现横向纵向(取决于图片异形,有的只能横向,有的只能纵向,有的横纵都可)的不变形拉伸。
学会了这招,再也不用向 UI 要多张切图、拆分切图了,还不谢谢我。