前端实现文本描边

上篇文章讲述了一些关于不同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 通过 strokestroke-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 相框 → 完美清晰

参考文章

CSS和SVG实现文字渐变、描边、投影

相关推荐
ywf12151 小时前
前端的dist包放到后端springboot项目下一起打包
前端·spring boot·后端
恋猫de小郭1 小时前
2026,Android Compose 终于支持 Hot Reload 了,但是收费
android·前端·flutter
hpoenixf7 小时前
2026 年前端面试问什么
前端·面试
还是大剑师兰特7 小时前
Vue3 中的 defineExpose 完全指南
前端·javascript·vue.js
泯泷7 小时前
阶段一:从 0 看懂 JSVMP 架构,先在脑子里搭出一台最小 JSVM
前端·javascript·架构
mengchanmian8 小时前
前端node常用配置
前端
华洛8 小时前
利好打工人,openclaw不是企业提效工具,而是个人助理
前端·javascript·产品经理
xkxnq9 小时前
第六阶段:Vue生态高级整合与优化(第93天)Element Plus进阶:自定义主题(变量覆盖)+ 全局配置与组件按需加载优化
前端·javascript·vue.js
A黄俊辉A9 小时前
vue css中 :global的使用
前端·javascript·vue.js
小码哥_常10 小时前
被EdgeToEdge适配折磨疯了,谁懂!
前端