这篇文章发布于 2022年02月25日,星期五,18:09,归类于 CSS相关。 阅读 17235 次, 今日 6 次 14 条评论
by zhangxinxu from https://www.zhangxinxu.com/wordpress/?p=10308 鑫空间-鑫生活
本文欢迎分享与聚合,全文转载就不必了,尊重版权,圈子就这么大,若急用可以联系授权。
本文不讲具体的 border-image
语法,因为语法庞杂,不是我针对谁,是在座的所有个位都消化不了,所以,直指要害,讲讲那些只有 border-image
能够实现的事情,读完后发现 border-image
属性还是挺有一套的,则可以买本 CSS新世界,里面有对 border-image
语法非常细致的介绍。
一、从鸡肋华丽转身的转折点
在很长一段时间内,border-image
属性就是个鸡肋属性,有着非常怪异的渲染机制,需要把图切成 9 块,分别填充到边框的 9 个区域。
几乎所有的 CSS 样式渲染属性,无论是渐变、圆角、盒阴影、滤镜还是混合模式,都可以从设计软件中找到对应的设置选项,唯一一个例外就是 border-image
。
为什么会出现这种现象呢?
现在的小年轻肯定不知道背后的原因,容我来给大家讲讲。
在 CSS3 还没有出现的年代,那个时候,所有的彩色效果、所有的圆角效果一定是PNG小图片实现的,例如,一些渐变按钮,还有一些渐变选项卡。
当时,为了让这些图像可以适应足够多的文字,会通过特殊的切图,通过两层 HTML 标签嵌套,配合 background-position
属性实现。
比方说下图就是当年实现1-7个字符宽度自适应选项卡使用的 PNG 小图片,中间有一段镂空:
HTML 结构是这样的:
<a href class="outer"> <span class="inner">选项卡</span> </a>
这个就是最终实现的效果:
是不是很麻烦?无论是切图还是写代码?
于是就催生了 border-image
属性,专门用来实现 PNG 小图片的内容自适应效果,只需要提供一个规整的(还是需要处理)的体积更小的图片,就可以实现任意字数的样式效果了。
下面就是一些典型的案例:
您可以狠狠地点击这里:border-image 设计初衷作用demo
看起来 border-image
属性还有点用,但是大家仔细观察上面的效果图,很容易就会发现,这些所谓的自适应效果,圆角效果,特么直接 Gradient 渐变、box-shadow
盒阴影以及 border-radius
圆角就可以实现,代码简单易上手,关键是兼容性绝赞(IE10+支持),比 border-image
属性的兼容性(IE11+)还要好,资源开销也小,这就导致没有任何需要使用 border-image
的理由。
border-image
属性从此沦为了鸡肋,只在某些花里花哨的边框装饰场景还有一点使用价值(因为不得不使用图片)。
然而,故事在这里并没有结束,随着时间的流逝,从 2012 年开始,在接下来1-2年的时间内,随着某一些新特性的支持,border-image
属性迎来了新生,开始在特定的场景下绽放出别样的竞争力。
这个特性就是:
- 2012年7月 Firefox 和 Safari 紧随 Chrome 浏览器的步伐对
border-image
新语法支持,包括:border-image:fill
和border-image-outset
属性; - 2014年4月,Firefox 浏览器终于支持了
border-image
的渐变图像功能。
这两个新变化让 border-image
属性迎来了新生,成为了一个被远远低估的 CSS 属性。
二、从简单的渐变边框说起
由于 border-image
支持了渐变图像,因此可以轻松实现其他 CSS 属性不太好实现的渐变边框效果。
<p class="border-linear-gradient">上下渐变边框</p> <p class="border-radial-gradient">径向渐变边框</p>
.border-linear-gradient { border-style: solid; border-image: linear-gradient(deepskyblue, deeppink) 20 / 10px; } .border-radial-gradient { border-style: solid; border-image: radial-gradient(deepskyblue, deeppink) 20 / 10px; }
效果如下图所示:
要想警示某一段内容存在风险,可以使用红色的条纹边框,代码如下:
<div class="border-stripe">我们可以使用红色条纹边框表示警示</div>
.border-stripe { border: 12px solid; border-image: repeating-linear-gradient(-45deg, red, red 5px, transparent 5px, transparent 10px) 12; }
MDN 一些废弃文档的警示边框就是使用 border-image
属性实现了,效果截图参见下方。
我们甚至可以用 border-image
属性重新定义元素的虚线边框,虚线的尺寸和虚实比例都可以随意控制,例如:
<div class="border-dashed">1:1的虚线</div>
.border-dashed { border: 1px dashed deepskyblue; border-image: repeating-linear-gradient(135deg, deepskyblue, deepskyblue 5px, transparent 5px, transparent 10px) 1; }
三、反馈态中的应用
移动端开发者,可交互元素需要有点击反馈态,如下所示:
通常做法是:
a[href]:active, button:active { background-image: linear-gradient(rgba(0,0,0,.05), rgba(0,0,0,.05)); }
如果元素本身有 background-image
图形,我们还可以使用 box-shadow
属性模拟:
button:active { box-shadow: inset 0 0 0 999px rgba(0,0,0,.05); }
如果盒阴影也被占用,则还有一招,那就是 border-image
:
button:active { border-image: linear-gradient(rgba(0,0,0,.05), rgba(0,0,0,.05)) 1 fill; }
特别适合无边框的场景,效果示意:
如果到这里就结束,那就太小儿科了,border-image
还有个厉害的子属性 border-image-outset
,可以扩展边框图像的范围,这个可是个大杀器。
先从我最近遇到的一个实际交互场景说起。
描述为点击输入框,整个列表项出现反馈态,如果是点击其他位置,则没有任何反馈。
如果是你,你会怎么实现?
有一种做法是这样的:
.list:focus-within:active { background-image: linear-gradient(rgba(0,0,0,.05), rgba(0,0,0,.05)); }
但是,如果列表中同时有按钮,就会有问题,因为点击按钮的时候,也会触发此选择器的执行,这并不是我们希望的效果,这个时候,就轮到 border-image
属性出马的时候了。
list { overflow: clip; } input:active { border-image: linear-gradient(rgba(0,0,0,.05), rgba(0,0,0,.05)) 1 fill / / 100vw; }
对内 fill 填充,对外 outset 扩展,填满整个区域。
对比其他实现
background-image
无法扩展;outline
则是层级不对,会覆盖输入框的文字内容。input:active { outline: 199vw solid rgba(0,0,0,.05); outline-offset: -999px; }
box-shadow
层级在文字之下,不过需要要求元素不能有边框宽度,否则就会有部分区域的颜色镂空:input:active { box-shadow: 0 0 0 199vw rgba(0,0,0,.05), inset 0 0 0 999px rgba(0,0,0,.05); }
于是,综合来看,在这个场景中,border-image
属性属于最佳实现。
四、绝杀:在元素之外构建图像
在元素之外构建纯色有 outline
和 box-shadow
这两个对手,那如果有需求,希望在元素之外构建图像(甚至是元素显示),那么 border-image
属性就是个王炸。
例如,希望在 <progress>
进度条上方同时显示进度数值,在不影响布局,不额外创建元素,兼容所有现代浏览器的方法一定只有 border-image
属性。
<progress value=".5"></progress>
progress { width: 300px; height: 6px; --img: url("data:image/svg+xml,%3Csvg width='300' height='30' xmlns='http://www.w3.org/2000/svg'%3E%3Ctext x='50%25' y='50%25' font-size='14' fill='%232a80eb' font-family='system-ui, sans-serif' text-anchor='middle' dominant-baseline='middle'%3E50%%3C/text%3E%3C/svg%3E"); border-image: var(--img) 0 fill / / 26px 0 0; }
效果如下截图所示:
立足于实际开发,这种构建需要和 JS 配合,走组件模式,图像跟随元素实时动态生成,因为 border-image
在元素的任意位置(包括元素外)显示图像是有一些局限的。
局限
有如下局限:
- 只能一个图像,极限是两个图像,使用
cross-fade()
图像函数; - 填充模式只能是拉伸,也就是
border-image
图像一定是拉伸填充元素区域,这使得图像尺寸和元素尺寸需要有匹配的对应关系,否则图像无法保持原始比例。通常做法是通过
border-image-width
属性约束最中心填充区域的大小,使填充图像保持原始比例(见最后那个邮箱输入框验证的案例)。 - 由于层级限制,无法在元素上方显示,会被内容覆盖。
- 会影响原本边框的显示,如果同时需要有边框效果,需要使用
box-shadow
模拟边框。
总结
由于元素显示区域外的图像显示可以通过伪元素绝对定位实现,因此,border-image
处理区域外图像就适合用在不支持伪元素的 HTML 元素上,例如:<input>
、<textarea>
、<img>
、<canvas>
、<progress>
、<video>
等元素。
以及可以使用伪元素,但是不方便使用绝对定位、或者容器设置了 overflow:hidden
的场景,要知道,border-image
图像效果不会因为元素设置了 overflow:hidden
而不可见,同时不会占据额外的尺寸,也就是无论如何显示,都不会影响布局。
再演示个效果
这里再演示一个验证效果实现,先看下效果。
此效果纯 CSS 实现,单 <input>
标签实现:
邮箱:<input type="email" class="use-border" placeholder="输入邮箱观察变化" required>
[type="email"] { width: 240px; padding: 10px; font-size: 100%; border: 1px solid transparent; background: #fff; box-shadow: inset 0 0 0 1px #ccc; outline: none; --valid: url("data:image/svg+xml,%3Csvg...'/%3E%3C/svg%3E"); --invalid: url("data:image/svg+xml,%3Csvg...%3E%3C/svg%3E"); } [type="email"]:focus { box-shadow: inset 0 0 0 1px #2a80eb; } .use-border { /* 20px 图标尺寸,272 是输入框长度 262 + 10px 偏移 */ border-image: 0 fill / 9 0 9 272 / 0 30px 0 0; } .use-border:valid { border-image-source: var(--valid); } .use-border:invalid:not(:placeholder-shown) { border-image-source: var(--invalid); }
最关键的实现就是这么一句:
border-image: 0 fill / 9 0 9 272 / 0 30px 0 0;
其中:
- 0 fill 表示不剪裁,直接填充到边框九宫格的中心位置;
- 9 0 9 272 表示
border-image-width
,用来确定一个 20 * 20 大小的中心区域,因为输入框高度 38px,宽度262px,图标偏移在右侧 30px 的地方,因此,才有右边框大小是 0,左边框大小是 272(262 – 20 + 30); - 0 30px 0 0 表示
border-image-outset
扩展大小,这里只往右扩展了 30px 大小。
这里例子有页面演示,眼见为实,您可以狠狠地点击这里:CSS border-image 实现输入框验证提示demo
多嘴一句,如果图像是要外挂在元素的四个边角,则理解上要相对简单些,此时,通过利用九宫格 4 个边角实现即可,难点在于一开始的 border-image-slice
,假设图像的原始尺寸是 200px * 200px,需要定位在右上角,以 20px 大小显示,则代码是这样的:
border-image: url(...) 200 200 0 0 / 20 20 0 0 / 0;
是不是有些不理解?那就对了,《CSS新世界》看起来,border-image
还是有些厉害的!
五、扯点其他的再结语
虽然不推荐,但是难免有人会遇到需要对 <img>
图像等替换元素进行点击反馈效果的实现,此时,本文提到的 3 种方法全部都是中国男足——凉凉!
img:active { /* 看不见 */ background-image: linear-gradient(rgba(0,0,0,.05), rgba(0,0,0,.05)); /* 看不见 + 1*/ box-shadow: inset 0 0 0 999px rgba(0,0,0,.05); /* 看不见 + 2 */ border-image: linear-gradient(rgba(0,0,0,.05), rgba(0,0,0,.05)) 1 fill; }
因为图像属于内容,层叠顺序比较高,此时可以使用滤镜实现:
img:active { filter: brightness(.95); }
本文最后那个邮箱输入后面自动显示对应验证图标的案例其实也可以使用 background-image
模拟。
.use-background { border-right-width: 30px; background: no-repeat right / 20px 20px; background-origin: border-box; } .use-background:valid { background-image: var(--valid); } .use-background:invalid:not(:placeholder-shown) { background-image: var(--invalid); }
不过这个实现虽然视觉上好像也 OK,但是,右侧的提示图标占据了布局空间,并且是输入框的一部分,点击是能够触发输入框的 focus 状态的,没有 border-image
实现的交互状态好。
好,以上就是本文的全部内容了。
对了,最近在自己独立小说网站“日紫烟”更新百万字长篇连载小说,轻小说带奇幻,欢迎品鉴,点击这里开始阅读。
感谢阅读,欢迎一键三连,哦,走错场了,不是视频,那欢迎分享,比心思密达!
本文为原创文章,欢迎分享,勿全文转载,如果实在喜欢,可收藏,永不过期,且会及时更新知识点及修正错误,阅读体验也更好。
本文地址:https://www.zhangxinxu.com/wordpress/?p=10308
(本篇完)
- 实现兼容性的CSS粗虚线边框(dashed)效果 (0.523)
- JS之我用单img元素实现了图像resize拉伸效果 (0.523)
- CSS3 border-image详解、应用及jQuery插件 (0.428)
- 10个demo示例学会CSS3 radial-gradient径向渐变 (0.428)
- 666,看hr标签实现分隔线如何玩出花 (0.428)
- jquery.guide.js新版上线操作向导镂空提示jQuery插件 (0.411)
- 解决文字和text-decoration:underline下划线重叠问题 (0.338)
- 小tip: CSS3如何实现圆角的outline效果? (0.316)
- 第五届CSS大会主题分享之CSS创意与视觉表现 (0.316)
- CSS3 linear-gradient线性渐变实现虚线等简单实用图形 (0.256)
- 纯CSS实现蜡烛、火焰以及熄灭后烟雾效果 (RANDOM - 0.083)
大佬怎样能让这个渐变转起来
css 变量
//lol.qq.com/act/a20231114heartsteelpass/index.html?e_code=500130
英雄联盟活动页,随便点击一个按钮触发弹框。这个弹框的边框实现就是用的border-image,但我看郁闷了。没看明白这个属性的规则;
求大佬解析
《CSS新世界》这本书对该属性有全网最详细的解释
大佬求助,iPhone11上border-image四周有一圈2px左右的透明有遇到过吗,别的iPhone上好像没问题,元素上只有这些css
element.style {
overflow-x: auto;
white-space: nowrap;
border-image: url(src) 0 fill / 1 / 0 stretch;
padding: 11.4667vw 5.33333vw 5.33333vw;
position: relative;
box-sizing: border-box;
}
开头(border-image 前面)写一句 border: 0
我想问问关于文章中 出现 border-image: 0 fill / 9 0 9 272 / 0 30px 0 0;
这个声明块中 // 是什么语法
原先以为是替代符号,用在其他简写形式的属性时是故障的。
而且这个vscode插件的检查还飘红
不太懂,求赐教
中间的简写属性省略了而已,检查标红是有问题的,这里。
不错
大佬,针对个位,是不是太简单了,建议换成 各位
> border-image 属性从此沦为了积累,
“积累”应为“鸡肋”
感谢反馈~
想咨询个问题。我们的系统有个场景:固定区域为了显示尽可能多的文字并且样式比较合适,会随着文字的多少动态的改变文字的字号,文字小到一定程度时再去省略。
目前我们的方式是根据字数进行分段,1-10,11-20,21-30每段一个字号
感觉最初的那个感觉有点像安卓上那个 9patch,可以做聊天气泡