前端实现文本描边

上篇文章讲述了一些关于不同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实现文字渐变、描边、投影

相关推荐
网络研究院5 小时前
苹果 Safari 地址栏可能被超大光标视觉欺骗
前端·safari·苹果
slongzhang_6 小时前
html添加水印
前端·html
Small black human7 小时前
前端-什么是Vue
前端·javascript·vue.js
IT 前端 张7 小时前
Axios与Ajax:现代Web请求大比拼
前端·javascript·ajax
ikun778g8 小时前
uniapp使用uview UI,自定义级联选择组件
前端·前端框架·uni-app
java水泥工8 小时前
基于Echarts+HTML5可视化数据大屏展示-惠民服务平台
前端·echarts·html5
万少9 小时前
可可图片编辑 HarmonyOS(3)应用间分享图片
前端·harmonyos·客户端
Hy行者勇哥9 小时前
现代软件系统架构:前端、后端、数据库、部署、算法与AI学习的结构与交互分析
前端·数据库·学习
前端开发爱好者10 小时前
90% 前端都不知道的 20 个「零依赖」浏览器原生能力!
前端·javascript·vue.js
讨厌吃蛋黄酥10 小时前
React语法全景指南:面试官问我用了哪些语法时,我这样回答拿到了offer
前端·react.js·面试