CSS 实现九宫格缩放(9-slice scaling 不变形拉伸)

前言

一些游戏中的窗口通常都有一些炫酷的效果

比如这样的

这样的

还有这样的

哎等等,你发现没有,后面两张的窗口长得很相似,只是第二张比较矮,第三张比较高,那么是不是要做两张素材来支持这种形式呢?

当然不用,在游戏开发中,通常会使用一种叫做 "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 要多张切图、拆分切图了,还不谢谢我。

相关推荐
Dream耀11 分钟前
CSS选择器完全手册:精准控制网页样式的艺术
前端·css·html
wordbaby11 分钟前
React 19 亮点:让异步请求和数据变更也能用 Transition 管理!
前端·react.js
月亮慢慢圆11 分钟前
VUE3基础之Hooks
前端
我想说一句13 分钟前
CSS 基础知识小课堂:从“选择器”到“声明块”,带你玩转网页的时尚穿搭!
前端·javascript·面试
红衣信23 分钟前
深入浅出 CSS 基础:从概念到选择器实战
前端·css
饮茶三千24 分钟前
五分钟!带你开发一个 VS Code 插件,实现状态栏文案轮播效果
前端
GIS之路25 分钟前
OpenLayers 地图投影转换
前端
用户48183772080325 分钟前
css grid实现流体布局
前端
Hanbox27 分钟前
探探React-找一个好看的组件库HeroUI(原NextUI)
前端