巧借CSS var变量实现任意的CSS自定义语法

这篇文章发布于 2020年10月11日,星期日,17:31,归类于 CSS相关, JS实例。 阅读 1720 次, 今日 5 次 一条评论

 

CSS变量的自定义语法技术

CSS自定义属性支持任意类型的属性值,我们可以借助这一特性自定义CSS语法,或者polyfill全新的CSS语法。

本文通过3个案例展示这种用法。

一、CSS变量自定义全新的CSS语法

CSS新世界中的新特性虽然很多,但是仍然有部分需求是无法实现的,例如虽然目前CSS Color Level 4支持超级多的颜色写法(详见我写的这篇文章),但是唯独不支持颜色关键字的半透明效果。

所以,我就会思考这样一个问题,有没有可能自创一个CSS函数语法,实现颜色关键字的半透明设置,例如自创一个keyword()函数:

color: keyword(red, 50%)
color: keyword(red, 0.5)
color: keyword(red / 50%)
color: keyword(red / 0.5)

显然,上面的语法浏览器肯定是无法识别的,图8-7就是Chrome浏览器下语法无效的截图示意。

图8-7 自定义的keyword()函数无效示意

那有没有办法让浏览器认为keyword()函数也是合法的呢?

还真有,那就是使用CSS自定义属性,让CSS自定义属性做为信使让整个语法合法化,例如:

body {
    --keyword: keyword(red, 50%);  /* 合法 */
    color: var(--keyword);
}

此时浏览器会认为上面的color属性语法就是合法的,下图就是Chrome浏览器下语法合法的截图示意。

keyword()函数语法有效示意

接下来要做的事情就简单了,既然语法合法,那我们就可以使用JavaScript获取到哪些元素使用了keyword()函数,此时再转换成浏览器可识别的颜色函数就可以实现我们想要的效果了。

具体实现原理如下所示:

  1. 获取⻚⾯所有的包含keyword()的⾃定义属性,就是把代码中的CSS变成JS中的数组数据,类似这样:
    body {
     --keyword: keyword(red, 50%);
     color: var(--keyword);
    }

    然后变成:

    [
     ['--keyword', 'keyword(red, 50%)]
    ]
  2. 遍历并观察所有DOM,如果设置了对应的⾃定义属性,将keyword()语法转换成浏览器识别的rgba()语法。例如:
    自定义属性的遍历与处理

基于上述原理,我写了个JavaScript代码片段,大家只需要在页面中引入下面这段HTML代码,就可以畅快自如地使用自定义的keyword()函数了。

<script src="./keyword-color.js"></script>

keyword-color.js可以点击或右键这里进行下载:keyword-color.js

下面简单测试下,假设页面中有如下所示的HTML和CSS代码:

<body>
    <p>文字颜色是?</p>
</body>
body {
    --keyword: keyword(blue, 50%);
    color: var(--keyword);
    /* 支持自定义属性嵌套 */
    --aaa: keyword(blue, 0.1);
    --bbb: var(--aaa);
    background-color: var(--bbb);

    font-size: 3rem;
}

结果就有如下图所示的效果,无论是背景色还是文字颜色都表现出了符合预期的半透明效果。

自定义的keyword()函数语法生效示意

眼见为实,您可以狠狠地点击这里:自定义keyword()语法与效果demo

二、Polyfill案例之offset-path

上面的自定义keyword()函数语法的案例其实使用预处理也可以解决的,类似Sass,Less这种,直接写个编译函数,使用Node.js转换成rgba()函数,也是很香的。

例如:

body {
 --keyword: keyword(red, 50%);
 color: var(--keyword);
}

使用Node.js工具直接转换成:

body {
 color: rgba(255,0,0,.5);
}

但是,有一类CSS语法的自定义预处理工具是搞不定的,那就是一些还没有支持的CSS新语法的Polyfill实现,这是CSS变量作为中间件自定义其他CSS语法技术的王牌应用。

这里先举offset-path属性的例子。

大家应该知道,offset-path⽀持path()函数可以实现元素任意不规则路径的运动效果。

.horse-run {
 offset-path: path("M10,80 q100,120 120,20 q140,-50 160,0");
 animation: move 3s linear infinite;
}
@keyframes move {
 100% { offset-distance: 100%;}
}

就会有一个马儿跑起来的效果。

马儿不停跑gif截图

关于offset-path属性更多内容可以参见这篇文章:“使用CSS offset-path让元素沿着不规则路径运动”。

实际上,offset-path的规范还支持很多其他的CSS函数,例如url()函数:

offset-path: url(#somePathId)

或者基本图形运动函数:

offset-path: circle(50% at 25% 25%);
offset-path: ellipse(50% 40px at top);
offset-path: inset(50% 50% 50% 50%);
offset-path: polygon(30% 0%, 70% 0%, 30% 100%, 0% 70%, 0% 30%);

可惜目前所有浏览器都不支持,如下图所示:

offset-path其他函数都不支持

没关系,我们可以借助CSS变量进行Polyfill。

首先所有函数使用CSS自定义属性表示,如下所示:

--offset-path: url(#somePathId);
--offset-path: circle();
--offset-path: ellipse();
--offset-path: inset();
--offset-path: polygon(30% 0%, 70% 0%, 0% 30%);
offset-path: var(--offset-path);

然后使用JavaScript识别哪些元素应用了这些CSS自定义属性,然后进行语法分析,转换成浏览器支持的path()函数即可。

眼见为实,您可以狠狠地点击这里:offset-path polyfill方法测试demo

各个函数polyfill之后的效果如下GIF所示(点击播放 278K):

路径运动

如何使用?

1. 引入JS,例如:

<script src="./offset-path.js"></script>

offset-path.js可以点击或右键这里进行下载:keyword-color.js

2. 需要使用offset-path其他函数语法的元素新增一个自定义属性is-offset-path,例如:

<img src="horse.png" class="horse-run" is-offset-path title="by zhangxinxu(.com)">

3. 使用CSS自定义属性设置相关的运动路径函数,例如这里使用外部的SVG元素路径:

.horse-run {
 --offset-path: url(#road);
 offset-path: var(--offset-path);
 animation: motion 3s linear infinite;
}

页面中的SVG元素代码如下(其他函数不需要SVG支持):

<svg width="280" height="150" viewBox="0 0 280 150">
 <path id="road" d="M10,80 q100,120 120,20 q140,-50 160,0"/>
</svg>

4. 效果完成。此时,offset-path.js会把url(#road)转换成浏览器可以识别的path()函数,于是就有了对应的运动效果。

源代码截图如下所示:

转换成path()函数示意

三、attr()函数新语法的支持

这个案例上一篇文章专门讲过。

attr()函数新语法可以让HTML属性变成任意的CSS值,例如可以让HTML属性中的任意属性的颜色值转变成CSS中真实的值进行使用,例如:



button {
    background-color: attr(bgcolor color);
    border-radius: attr(radius px, 4px);
}

理论上会有下图所示的效果。

理论上的按钮效果示意

可以看到attr()函数新语法让HTML和CSS之间的联系更加紧密,这个特性我认为是会带来很多变革与颠覆的,但是没有任何浏览器支持,且估计很长一段时间也不会有浏览器支持。

不要急,借助CSS变量,我们同样可以浏览器能识别我们书写的CSS attr()函数新语法。

例如:

button {
    --attr-bg: attr(bgcolor color);
    background-color: var(--attr-bg);
    --attr-radius: attr(radius px, 4px);
    border-radius: var(--attr-radius);
}

前提就是引入一段代码量很少的JS文件即可。

详细内容请访问这篇文章:“Polyfill吊炸天的CSS attr()新语法”。

这里不再赘述。

四、写在最后的结语

本文介绍的这个技术的核心就是通过让原来浏览器不认识的函数或者语法通过CSS变量的形式调用,从而让浏览器可识别。如果浏览器原本就识别这些语法,功能也完全正常,不受影响。

于是,我们就可以获取到用户对当前元素具体设置的CSS变量值,也就是获取到用户设置的这个不认识的函数或者语法究竟是什么,然后再通过JavaScript把这些新语法转换成浏览器可识别的语法。

上面3个方法的内核都是一样的。

大家遇到类似的需求也可以仿照相关的JS逻辑进行修改。

另外,相关技术是我最近才研究折腾出来的,JS文件可能会有瑕疵,或者bug,欢迎反馈,当然,我也会积极在生成环境使用,看看会不会有什么问题,我也会及时更新。

如果你有其他更好的实现方法,也欢迎交流,不吝赐教。

最后,感谢您的阅读,希望本文的内容可以对您的学习有所帮助。

(本篇完)

分享到:


发表评论(目前一条评论)

  1. 前端小白说道:

    张老师,你好, 请教一个问题, 项目中需要在chrome中打开其他客户端(自定义协议),如果协议存在则直接打开,如果不存在则提示下载。 之前使用protocolCheck.js 可以实现,但是chrome升级到86之后 不会触发blur 事件 所以无法实现。有什么别的方法可以实现这种功能吗?