前言
chrome 浏览器有 12 px 的字体的限制,因此对于 vw 自适应布局,非常的不友好。
我之前曾经写过一篇文章,用 svg 辅助页面文字自适应,它可以解决常见的 html 元素里面的文字不能小于 12 px 的问题。
很不幸的是,最近我们的项目中加了 input 标签,于是采用之前的方案的问题就浮出水面了——它无法让 input 标签里面的字体在 chrome 中突破 12 px。
但是对于我们实际的项目,又必须要做到这种效果,那么该怎么办呢?
经过大胆的猜想和细心的论证,得出了几种方案:
- 利用 html 标签的 contenteditable 属性,再配合 svg 的特性,进行缩放
- 利用 scale 对 input 中的文字进行缩放
- 改用 canvas 绘制文字
思路
1. 利用 html 标签的 contenteditable 属性
这个属性,说来惭愧,以前并没接触过,在此只能暗自感慨自个儿学艺不精了。
这次也是为了解决这个问题,在找解决方案的时候,无意中发现的。
当时就感觉很惊叹了,没想到 html 标签居然还有这个属性。这不刚好就是我理想中的解决方案么?
首先按照惯例,避免误导大家,还是先奉上 mdn 文档:https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/contenteditable
不过说实话,其实在默认情况下,这个属性其实并没有这么好用。
div 上加了这个属性以后,它的默认行为有点类似于 textarea,你可以换行输入,可以换行。但是与 textarea 不同的是(其实你打开控制台就会发现),它实际上是采用不同的 div 来辅助排版的,比如你换了一行,就给你新起了一行,创建了一个新的 div,然后你输入的内容就会出现在新创建的 div 内部。
这个行为与我们期待的还是有点不一样的,但是我们明白了它的工作原理以后,是否能想到,我们把之前的 svg 辅助排版的功能,塞到这个地方来,也能实现我们想要的效果呢?
果不其然,经过我的测试了以后发现,使用 svg 元素放在里面,也是可以编辑的,这样就太好办了。
我用如下代码,构建了三个不同字体大小的 div 输入框,div 里面放置的是 svg,用来辅助 chrome 字体缩放的。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>Document</title>
<style>
* {
padding: 0;
margin: 0;
}
[contenteditable]:focus {
outline: none;
}
div {
width: 75px;
height: 20px;
margin-top: 5px;
border: 1px solid red;
}
.first {
font-size: 5px;
}
.second {
font-size: 8px;
}
.third {
font-size: 12px;
}
</style>
</head>
<body>
<div class="first" contenteditable="true">
<svg width="100%" height="100%">
<text dy=".3em" x="5%" y="50%" fill="#000" text-anchor="left">
5px;
</text>
</svg>
</div>
<div class="second" contenteditable="true">
<svg width="100%" height="100%">
<text dy=".3em" x="5%" y="50%" fill="#000" text-anchor="left">
10px;
</text>
</svg>
</div>
<div class="third" contenteditable="true">
<svg width="100%" height="100%">
<text dy=".3em" x="5%" y="50%" fill="#000" text-anchor="left">
12px;
</text>
</svg>
</div>
</body>
</html>
运行代码以后,chrome 浏览器里面的效果是这样的:
<br />可以看到,可以成功实现输入的效果,并且字体大小的问题也解决了。
这个方案是否能在别的浏览器上使用,我暂时并未详尽的测试过,不过别的浏览器里面就没有字体的限制,所以压根就不需要用到这种 hacker 方法。
虽然看起来效果还挺像 input 输入框的,但是这里还是有一些需要考虑到的问题:
- 里面的文字不能像 input 输入框一样,输入内容超过父容器的范围时,会自动隐藏前面的内容,显示出最后的内容
- 里面的文字能够按 delete 或者 backspace 键删除掉,但是也会将内部的元素删除掉,这样会导致 svg 内容被删除掉
目前,我能想到的解决方案是,朝 div 上添加监听事件,监听键盘事件,当碰到对应的情况,给出对应的解决方案。
不过,这个解决方案还是挺复杂的,所以推荐文字数目一定的情况下使用。
2. 利用 transform 的 scale 属性对 input 中的文字进行缩放
我之前就提到过,可以采用 scale 属性,解决 chrome 12 px 字体限制的文字,但是问题就是麻烦。
因为 transform 是对整个 input 框的内容进行缩放,所以在缩放完以后,input 框本身的大小也变化了,位置也变化了,所以为了达到理想的突破 12px 限制的效果,还是需要用 js,动态的扩大 input 宽高,然后在用 transform 去缩小,用起来着实太麻烦。
而且这个方法,只适合按照设计稿里面的尺寸来写页面的情况,不能有效的适应 vw 动态缩放的布局。关键麻烦的地方,还是在计算,必须要动态的计算出来 input 需要扩大多少,然后再缩小多少,因为 font 撑开 input 框,因此得在外面套一个 div 放原本的 input 位置来占位,然后将 input 动态的算进占位符上面才行。
3. 利用 canvas 进行绘制
这个方法对 canvas 功底要求比较高,这就相当于用 canvas 自己构建一套输入框了,说实话,难度还是比较大。
这里暂且跳过具体实现方式吧,我后面有计划写一个类似的 demo,到时候我再回来补上具体细节。
总结
有些时候,一些看似很小的功能,但是由于各种历史原因和实现的时候的抉择,真正实现起来并不好弄。
就像以前前端工程师对 ie 浏览器的颇多怨言一样,我相信有些 hacker 技巧,会随着技术的进步和成熟,慢慢会变得不是问题。
但是对于一名合格的前端工程师来说,目前问题就摆在我们面前了,抱怨是没有任何有意思的,想出解决方案,才是我们的第一要务。