本人翻译者系奇舞团前端工程师
译文地址:https://mp.weixin.qq.com/s/dfmQUPS7_FJZ1sDK8_HS8w
原文标题:Yes, I can connect to a DB in CSS
原文作者:Memeloper
原文地址:https://www.leemeichin.com/posts/yes-i-can-connect-to-a-db-in-css.html
某公司招聘需求如下:
我们正在寻求可以 使用CSS连接数据库 的前端伙伴~
自从我上次开始一个高质量的“发帖”以来,已经有很长一段时间了,事实上,它已经很长一段时间了,那时候我的词汇表中可能还没有“水帖”这个词。
为此,我受到了一个早期项目的启发,该项目基于区块链初创公司将投资者的脸投影到3D立方体上让我想起了以前的互联网,那时一切都很奇怪。
好汉不提当年勇。所以今天,我将讨论如何管理我自己的新项目: sqlcss.xyz[1]
顾名思义,这就是使用CSS连接数据库的方式。不幸的是,它 只能在Chrome中工作 ,但你可以提供任何你喜欢的SQLite数据库,并通过CSS查询它。
它是如何工作的?
首先我们需要用到一组被亲切地称为 Houdini[2] 的api,它让你的浏览器能够通过Javascript对象模型来控制CSS。换言之,这意味着您可以定制CSS样式、添加定制属性,等等。
可能这个作品最大的特性是 CSS Paint Worklet[3] ,它允许你在一个元素上“绘制”,就像你知道和喜欢的画布一样,并让浏览器把它当作CSS中的图像。这里有一些例子可以用来 演示Houdini[4] 。
然而,这个工作集只提供了Worker API的一个子集,而且画布上下文本身也被大量剥离。这样做的实际结果是,您的自定义CSS绘制代码提供了一个比您预期的更小的沙盒。
这意味着什么? 没有网络访问权限,因此可以和fetch和XmlHttpRequest说再见了。在绘制上下文上没有drawText功能。其他各种JS api也消失了,以防你希望解决这些问题。
不过,不用担心。并非一切都完了。让我们把它分解成几个步骤。
这必须是第一步,以便理解概念证明是否可行。
首先我们会借助于 sql.js[5] 。它实际上是一个通过emscripten编译成WebAssembly和老式ASM.js的SQLite版本。不幸的是,我们不能使用WASM版本,因为它必须通过网络获取二进制文件。ASM版本没有这个限制,因为所有的代码都可以在一个模块中使用。
虽然PaintWorklet限制了worker内部的网络访问,但你仍然可以导入代码,只要它是一个ES6模块。这意味着文件中必须有一个导出语句。不幸的是,sql.js没有ES6的版本,所以我自己修改了sql.js,使其能够顺利的被import进入项目。
现在到了关键时刻:我可以在我的工作包中建立一个数据库吗?
**成功了!**但没有任何数据,所以我们来解决这个问题。
一开始最简单的方法就是设置一些假数据,sql.js有两个函数可以做到这一点。
我有了测试表,里面有一些值。我应该能够查询这个并获得这些值,尽管我不确定得到什么样的结构化查询结果。
正如预期的那样,结果已经出来了。不过,渲染展示通过CSS查询数据库的结果会更好。
我认为这就像在画布上写文本一样。这有多难,对吧?
不,那样就太简单了。这里的上下文与画布元素的上下文不同,它只提供了功能的一个子集。
当然,它仍然可以绘制路径和曲线,所以缺乏方便的API是一个障碍,但这一切都不是问题。
幸运的是,我们可以借助于 opentype.js[6] 所提供的解决方案。它可以解析一个字体文件,然后,给定一个文本字符串,生成每个字符的字母形式。这个操作的实际结果是一个表示字符串的路径对象,然后可以将其呈现到上下文中。
这次我不必修改opentype库来导入它,因为它已经可以从 JSPM[7] 中获得。所以,如果你给JSPM一个npm包,它会自动生成一个ES6模块,你可以直接导入到你的浏览器中。这是非常棒的,因为我真的不想为了一个有趣的项目而使用打包工具。
但这里有一个问题——它想通过网络加载字体,而我不能这样做!嗨,挫败了!
……而且?它还有一个接受数组缓冲区的解析方法。我将用base64编码字体,然后在我的模块中解码它。
我告诉过你worklet也没有处理base64字符串的api吗?atob和btoa都没有!我也不得不为此找到一个普通的JS实现。
我把这段代码放在它自己的文件中,因为它不太符合人体工程学……必须在剩下的代码旁边使用大约200kb的编码字体字符串。
这就是我为何要滥用ES模块来加载我的字体的原因。
从现在起,所有繁重的工作都由opentype库来完成,所以我所需要做的就是用一点数学知识来对齐。
最好再来一些HTML和CSS看看发生了什么。
成功了!但这里没有足够的CSS,而且查询是硬编码的。
如果必须使用CSS来查询数据库,那就更好了。事实上,这是我们可以在Paint Worker的上下文之外与其通信的唯一方式,因为没有与Web worker一样的消息传递API。
这些CSS属性被称为类型化属性,但它们本质上被封装在一个特殊的CSSProperty类中,而这个类本身并不是很有用。因此,你必须手动将其转换为字符串或数字或其他类似的使用它,如上所述。
现在对CSS做一个快速调整。
引号在这里被故意省略了,因为否则在将字符串传递给数据库之前,我必须将它们从字符串中删除。也就是说,这很有效!
任务完成!
如果你玩过sqlcss。你会注意到我并没有满足于此。在进行了一些重构之后,又进行了一些更改。
硬编码数据库模式和实际数据,有点糟糕。它证明了这个概念,但我们肯定可以做得更好。
如果您可以查询任何您喜欢的数据库,只要您手边有数据库文件,那就太棒了。我只需要读取这个文件并对其进行base64编码,就像我对字体文件所做的那样。
我为此做了一个额外的CSS属性,在这个属性中,您可以将SQLite数据库作为base64编码的数据URI提供。data URI只是为了显示并确保它对DOM是有效的,我将在Worker层面解析这些东西。
最后一步是使其更易于查询,因为否则您必须进入调试器来操作元素的CSS。
这可能是项目中最简单的部分。自定义属性对于分号有一点问题,而SQLite并不关心末尾的分号是否被省略,所以最简单的做法是,如果在输入中找到它,就删除它。
从现在开始,您可以使用CSS导入和浏览您自己的数据库了!
我遗漏了一件事,就是所有这些查询结果特别多的时候,如何更好的渲染展示的问题。如果查询结果有很多,他们需要分开到单独的行。这与本文的主题-- 使用CSS连接到数据库 并没有太大关系,所以我认为在这里谈论这个问题并不合适,但如果你想进一步了解这个"荒谬"的概念, git上的代码都是可用的[8] 。
[1]sqlcss.xyz: https://www.sqlcss.xyz
[2]CSS Houdini: https://developer.mozilla.org/en-US/docs/Web/Guide/Houdini
[3]CSS Paint Worklet: https://developer.mozilla.org/en-US/docs/Web/API/PaintWorklet
[4]houdini how: https://houdini.how/
[5]sql.js: https://sql.js.org/#/
[6]opentype.js: https://opentype.js.org/
[7]jspm: https://jspm.org/
[8]sqlcss git: https://git.sr.ht/~mrlee/sqlcss
1、交错动画
有时候,我们需要给多个元素添加同一个动画,播放后,不难发现它们会一起运动,一起结束,这样就会显得很平淡无奇。那么如何将动画变得稍微有趣一点呢?很简单,既然它们都是同一时刻开始运动的,那么让它们不在同一时刻运动不就可以了吗。如何让它们不在同一时刻运动呢?注意到CSS动画有延迟( delay )这一属性。举个栗子,比如有十个元素播放十个动画,将第二个元素的动画播放时间设定为比第一个元素晚0.5秒(也就是将延迟设为0.5秒),其他元素以此类推,这样它们就会错开来,形成一种独特的视觉效果。
这就是所谓的交错动画:通过设置不同的延迟时间,达到动画交错播放的效果。
本demo地址: https://codepen.io/alphardex/pen/XWWWBmQ
本demo地址: https://codepen.io/alphardex/full/KKwvKGY
一般我们都是从第一个元素开始交错的。但如果要从中间元素开始交错的话,就要给当前元素的延时各加上一个值,这个值就是中间元素的下标到当前元素的下标的距离(也就是下标之差的绝对值)与步长的乘积,即: delay + Math.abs(i - middle) * step ,其中中间元素的下标 middle = letters.filter(e =>e !== "").length / 2
本demo地址: https://codepen.io/alphardex/full/eYYMYXJ
本demo地址: https://codepen.io/alphardex/full/dyPorwJ
HTML元素的状态是可以动态变化的。举个栗子,当你的鼠标悬浮到一个按钮上时,按钮就会变成“悬浮”状态,这时我们就可以利用伪类 :hover 来选中这一状态的按钮,并对其样式进行改变。 :hover 是笔者最最常用的一个伪类。还有一个很常用的伪类是 :nth-child ,用于选中元素的某一个子元素。其他的类似 :focus 、 :focus-within 等也有一定的使用。
本demo地址: https://codepen.io/alphardex/pen/pooYKVa
谁规定按钮只能有一套边框的?利用绝对定位和 padding ,我们可以给按钮做出3套大小不一的边框来,这样效果更炫了。
本demo地址: https://codepen.io/alphardex/full/ZEYXomW
简而言之,伪元素就是在原先的元素基础上插入额外的元素,而且这个元素不充当HTML的标签,这样就能保持HTML结构的整洁。我们知道每个元素都有 ::before 和 ::after 这两个伪元素,也就是说每个元素都提供了3个矩形(元素本身1个,伪元素2个)来供我们进行形状的绘制。现在又有了 clip-path 这个属性,几乎任意的形状都可以被绘制出来,全凭你的想象力上面的动图是条子划过文本的动画,条子就是每个文本所对应的伪元素,对每个文本和其伪元素应用动画,就能达到上图的效果了。
本demo地址: https://codepen.io/alphardex/pen/jOEOEzZ
元素可以有自定义的属性值,它的命名格式通常为 data-*``attr() 用于获取元素的这种自定义属性值,并赋值给其伪元素的 content 作为其生成的内容利用这个函数,我们可以用伪元素在原先文本的基础上“复制”出另一个文本,如下图所示。
本demo地址: https://codepen.io/alphardex/full/wvBeXjd
之前有做过闪光按钮的效果:鼠标悬浮按钮上时一道光从左到右划过去。笔者就用渐变来模拟那道光,通过 transform: translateX() 将其平移至右边。
但这样明显不对啊,这光为啥能被看见呢?不应该把它给“挡”起来吗?于是乎,给按钮加上 overflow: hidden ,光在按钮外的位置时就被隐藏起来了。
这就是障眼法的力量:)
本demo地址: https://codepen.io/alphardex/pen/eYYzXBZ
更多障眼法可以看看这个作品,一次性看个够XD
提示:这里最好将 input 作为 label 的子元素,这样用户点击 label 时就能传到 input 上默认的 input 太丑怎么办?那就把它先抹掉,用 appearance: none 或 opacity: 0 都可以然后,利用兄弟选择符 ~ 来定制和 input 相邻的所有元素( + 号也行,只不过只能选中最近的元素),例如可以用伪元素生成一个新的方框代替原先的 input ,利用伪类 :checked 和动画来表示它被勾选后的状态,本质上还是障眼法哦~
善用某些CSS特性,也可以为你的作品增色不少哦
此处包括 transition 和 transform CSS动画可以说是利用CSS设计炫酷特效的最强法器,它几乎贯穿了我的所有作品 有人问我为什么我能想出这么多的动画?笔者阅番百部,对常用的动画技巧了如指掌,同样那些酷炫的网站只要细心观察,也会给笔者带来很多设计上的灵感。一言以蔽之:只有多欣赏动画,才能写出好的动画。
为盒子添加圆角,经常用来美化按钮等组件如果设定为 50% 则是圆形,也很常用
调整多个顶点的 border-radius 可以做出不规则的曲边形状
本demo地址: https://codepen.io/alphardex/full/abbWOPR
为盒子添加阴影,增加盒子的立体感,可以多层叠加,并且会使阴影更加丝滑
如果给 box-shadow 的扩张半径设定足够大的值,可以用它来遮住背景,而无需额外的div元素
注意到 box-shadow 还有个 inset ,用于盒子内部发光利用这个特性我们可以在盒子内部的某个范围内设定颜色,做出一个新月形
本demo地址: https://codepen.io/alphardex/full/eYmGEGp
文本阴影,本质上和 box-shadow 相同,只不过是相对于文本而言,常用于文本发光,也可通过多层叠加来制作霓虹文本和伪3D文本等效果
本demo地址: https://codepen.io/alphardex/full/Exxodoq
本demo地址: https://codepen.io/alphardex/full/rNNwmZz
本demo地址 : https://codepen.io/alphardex/full/QWWavvx
能将背景裁剪成文字的前景色,常用来和 color: transparent 配合生成渐变文本
本demo地址: https://codepen.io/alphardex/full/QWwveZG
渐变可以作为背景图片的一种,具有很强的色彩效果,甚至可以用来模拟光
这个作品用到了HTML的 dialog 标签,渐变背景,动画以及 overflow 障眼法,细心的你看出来了吗:)
本demo地址: https://codepen.io/alphardex/full/eYYxzBm
径向渐变常用于生成圆形背景,上面例子中Snow的背景就是一个椭圆形的径向渐变此外,由于背景可以叠加,我们可以叠加多个不同位置大小的径向渐变来生成圆点群,再加上动画就产生了一种微粒效果,无需多余的 div 元素。
本demo地址: https://codepen.io/alphardex/full/OJPvMGx
用一个伪元素叠在饼图上面,并将 content 设为某个值(这个值通过CSS变量计算出来),就能制作出度量计的效果,障眼法又一次完成了它的使命。
本demo地址: https://codepen.io/alphardex/full/BaydVvQ
PS里的滤镜,玩过的都懂, blur 最常用
本demo地址: https://codepen.io/alphardex/full/pooQMVp
PS里的混合模式,常用于文本在背景下的特殊效果以下利用滤色模式( screen )实现文本视频蒙版效果
本demo地址: https://codepen.io/alphardex/full/wvvLYpV
本demo地址: https://codepen.io/alphardex/full/ZEEBRrq
本demo地址: https://codepen.io/alphardex/full/ExaZgxp
虽然这并不是一个CSS特性,但是它经常用于完成那些CSS所做不到的事情那么何时用它呢?当CSS动画中有属性无法从CSS中获取时,自然就会使用到它了
目前CSS还尚未有获取鼠标位置的API,因此考虑用JS来进行通过查阅相关的DOM API,发现在监听鼠标事件的API中,可通过 e.clientX 和 e.clientY 来获得鼠标当前的位置既然能够获取鼠标的位置,那么跟踪鼠标的位置也就不是什么难事了:通过监听 mouseenter 和 mouseleave 事件,来获取鼠标出入一个元素时的位置,并用此坐标来当作鼠标的位移距离,监听 mousemove 事件,来获取鼠标在元素上移动时的位置,同样地用此坐标来当作鼠标的位移距离,这样一个跟踪鼠标的效果就实现了。
本demo地址: https://codepen.io/alphardex/full/OJPmQGz
CSS Houdini是CSS的底层API,它使我们能够通过这套接口来扩展CSS的功能
目前来说,我们无法直接给渐变添加动画,因为浏览器不理解要改变的值是什么类型这时,我们就可以利用 CSS.registerProperty() 来注册我们的自定义变量,并声明其语法类型( syntax )为颜色类型 <color>,这样浏览器就能理解并对颜色应用插值方法来进行动画还记得上文提到的圆锥渐变 conic-gradient() 吗?既然它可以用来制作饼图,那么我们能不能让饼图动起来呢?答案是肯定的,定义三个变量: --color1 、 --color2 和 --pos ,其中 --pos 的语法类型为长度百分比 <length-percentage>,将其从 0 变为 100% ,饼图就会顺时针旋转出现。
本demo地址: https://codepen.io/alphardex/full/RwNxpXQ
将交错动画和伪类伪元素结合起来写出来的慎重勇者风格的菜单。
本demo地址: https://codepen.io/alphardex/full/ExavZdV