上篇文章讲述了一些关于不同PPI设备逻辑像素和DPR的一些关系,这篇文章有了上篇知识基础讲一下前端实现文本描边有哪些方案。
文本描边分三种:外描,居中描边,内描边。
居中描边和内描边会占据字体内部空间,最后的效果会显的字体变细
描边路径的转角处的形状又分尖角、圆角、斜角
而我需要实现的是,粗体描边 + 外描边 + 描边圆角 + 可被html2canvas兼容转成图片
text-shadow
模拟描边
这是通过设置四个方位阴影来模拟描边的效果
css
.text {
text-shadow:
1px 0 0 #000, /* 右 */
-1px 0 0 #000, /* 左 */
0 1px 0 #000, /* 下 */
0 -1px 0 #000; /* 上 */
color: white;
}
1px
3px
7px
优点:兼容性好(IE9+)610。
缺点:锯齿明显(有点像马赛克风),但是描边设置的超过3px就会偏移明显,无法控制描边圆角,文字不能透明
-webkit-text-stroke
描边属性
text-stroke属性曾短暂在W3C的规范草案中出现过,但后来被移除了,不过,为了兼容现有的网页内容,一些浏览器仍然支持这个属性(主要作为 非标准属性 存在)
css
.text {
-webkit-text-stroke: 2px #000; /* Chrome/Safari */
}

从这里可以看出,-webkit-text-stroke
其实是 居中描边,并且是覆盖在文本上的,也无法更改描边方式,那么如何实现外描边效果呢?
也是可以的!用两层文本,一层文本带描边,一层原文本覆盖在带描边上就可以了,为了节省标签,可以用伪元素来生成
css
.text::before{
content: "为你定制 发现精彩";
position: absolute;
background-image: linear-gradient(#FFCF02, #FF7352);
}
.text{
-webkit-text-stroke: 6px #333;
}
示意图如下:
改变描边大小也不会出现文字"变瘦"的情况
优点:效果平滑。
缺点:无法设置描边圆角和描边方式,无法兼容html2canvas,兼容性一般。
SVG实现
SVG 通过 stroke
和 stroke-width
可以来控制描边颜色和大小,比如
css
.text{
/*其他*/
stroke-width: 4px;
stroke: #333;
}

和 CSS 表现一样,都是居中描边,也无法改变。
不一样的是,SVG 控制更为灵活,默认是先填充、然后再描边 ,所以看着是描边在填充之上,但是,我们可以改变这种规则,设置先描边,再填充,那么填充的颜色就会覆盖在描边之上了。SVG 中改变这种规则的可以通过 paint-order 来设置。
css
.text{
/*其他*/
stroke-width: 4px;
stroke: #333;
paint-order: stroke; /*先描边*/
}
这样就实现了外描边效果,是不是比 CSS 方便许多?
除此之外,SVG 还可以通过 stroke-linejoin 属性设置描边路径的转角处的形状,这里是圆角,可以做如下设置
css
.text{
/*其他*/
stroke-width: 4px;
stroke: #333;
paint-order: stroke; /*先描边*/
stroke-linejoin: round; /*路径转角为圆角*/
}
各种属性效果如下
优点:啥都好。
缺点:比较麻烦,不灵活。
Canvas实现
canvas可以直接设置外描边以及圆角
jsx
<canvas id="autoCanvas" />
const canvas = document.getElementById('autoCanvas')
const ctx = canvas.getContext('2d')
// 设置显示尺寸(CSS像素)
canvas.style.width = "500px"
canvas.style.height = "500px"
// 设置画布尺寸
canvas.width = 500
canvas.height = 500
ctx.font = `30px ${myFontFamily}`
ctx.textAlign = 'center'
ctx.textBaseline = 'middle'
// 描边设置
if (textShadowColor) {
ctx.strokeStyle = "#xxxx"
ctx.lineWidth = 7 // 描边厚度
ctx.lineJoin = 'round' // 线条连接处圆角
ctx.lineJoin = 'round' // 线条连接处圆角
ctx.strokeText("点击编辑文案", , 500 / 2, 500 / 2)
}
// 字体颜色
ctx.fillStyle = "#fff"
// 设置文字以及其位置
ctx.fillText("点击编辑文案", 500 / 2, 500 / 2)
在h5项目中,画完会发现移动端看效果会有点模糊,这是因为canvas.width
本质上设置的是物理像素大小,又因为canvas 不是矢量图,而是像图片一样是位图模式的,所以在比自己大的容器里会被拉伸。
高 PPI 等高清屏幕 物理像素 和 逻辑像素(CSS 像素) 会有差异。当设备DPR=2时,500x500px的大小需要占据1000x1000的物理像素。
把画布想象成一幅画
用 500×500 像素的图片放在 1000×1000 相框里 → 强制拉伸变模糊
解决方案 :通过window.devicePixelRatio
获取DPR,根据 DPR 放大画布 + 缩放上下文
jsx
const canvas = document.getElementById('autoCanvas')
if (!canvas) return
const ctx = canvas.getContext('2d')
const dpr = window.devicePixelRatio || 1
// 控制画布大小
const w = 500
const h = 500
// 设置显示尺寸(CSS像素)
canvas.style.width = `${w}px`
canvas.style.height = `${h}px`
// 设置实际像素尺寸(考虑DPR)
canvas.width = w * dpr
canvas.height = h * dpr
// 坐标系放大 - 必须在所有绘制操作之前
ctx.scale(dpr, dpr)
// 清空上一次画布 - 使用逻辑坐标
ctx.clearRect(0, 0, w, h)
ctx.font = `30px ${fontFamily}`
ctx.textAlign = 'center'
ctx.textBaseline = 'middle'
// 描边设置
if (textShadowColor) {
ctx.strokeStyle = "#描边颜色"
ctx.lineWidth = Math.ceil(9 / dpr) // 描边缩放
ctx.lineJoin = 'round' // 线条连接处圆角
ctx.lineJoin = 'round' // 线条连接处圆角
ctx.strokeText("点击编辑文案", w / 2, h / 2)
}
// 字体颜色
ctx.fillStyle = "#fff"
// 设置文字以及其位置
ctx.fillText("点击编辑文案", w / 2, h / 2)
适配后:直接画一幅 1000×1000 像素的画 → 放入 1000×1000 相框 → 完美清晰