解决 canvas 在高清屏中绘制模糊的问题

本文阐述了在高清屏上使用Canvas绘制时出现模糊的原因,关键在于设备像素比与渲染倍率的不匹配。通过获取并利用devicePixelRatio,调整Canvas尺寸及绘制参数,实现了在Retina等高分辨率屏幕上的清晰呈现。

解决 canvas 在高清屏中绘制模糊的问题

1、问题

使用 canvas 绘制图片或者是文字在MAC系统中 Retina 屏中显示会非常模糊。如图:

在这里插入图片描述
因为 canvas 不像svg这样,canvas不是矢量图,而是像我们像常见图片一样是位图模式的。高 dpi 显示设备意味着每平方英寸有更多的像素。也就是说二倍屏,浏览器就会以2个像素点的宽度来渲染一个像素,该 canvas 在 Retina 屏幕下相当于占据了2倍的空间,相当于图片被放大了一倍,因此绘制出来的图片文字等会变模糊。

因此,要做 Retina 屏适配,关键是知道当前屏幕的设备像素比,然后将 canvas 放大到该设备像素比来绘制,然后将 canvas 压缩到一倍来展示。

2、解决思路

在浏览器的 window 对象中有一个 devicePixelRatio 的属性,该属性表示了屏幕的设备像素比,即用几个(通常是2个)像素点宽度来渲染1个像素。在控制台打印,就能够看到自己屏幕的 devicePixelRatio 值为多少.

在这里插入图片描述

我们继续, 举例 🌰 来说,假设 devicePixelRatio 的值为 2 ,一张 100×100 像素大小的图片,在 Retina 屏幕下,会用 2 个像素点的宽度去渲染图片的 1 个像素点,因此该图片在 Retina 屏幕上实际会占据 200×200 像素的空间,但是我们总的像素点只有100×100,这就相当于图片被放大了一倍,因此图片会变得模糊。
类似的,在 canvas context 中也存在一个 backingStorePixelRatio 的属性,该属性的值决定了浏览器在渲染canvas之前会用几个像素来来存储画布信息。 backingStorePixelRatio 属性在各浏览器厂商的获取方式不一样,所以需要加上浏览器前缀来实现兼容。

3、解决问题
3.1 首先获取 Canvas 对象:
var myCanvas = document.getElementById("my_canvas");
var context = myCanvas.getContext("2d");
3.2 获取像素比,计算比例

获取像素比,将 Canvas 宽高进行放大,放大比例为:devicePixelRatio / webkitBackingStorePixelRatio , 我们写了一个兼容的方法。注意,这个方法有部分浏览器已经废弃使用了,可以直接默认使用1这个值.

   var getPixelRatio = function (context) {
    var backingStore = context.backingStorePixelRatio ||
        context.webkitBackingStorePixelRatio ||
        context.mozBackingStorePixelRatio ||
        context.msBackingStorePixelRatio ||
        context.oBackingStorePixelRatio ||
        context.backingStorePixelRatio || 1;
    return (window.devicePixelRatio || 1) / backingStore;
    };
   var ratio = getPixelRatio(context);
3.3 按实际渲染倍率来缩放canvas。

注意基础知识点:

  • 要设置canvas的画布大小,使用的是 canvas.width 和 canvas.height;
  • 要设置画布的实际渲染大小,使用的 style 属性或CSS设置的 width 和height,只是简单的对画布进行缩放。
    2倍屏幕下示例代码:
 <canvas width="640" height="800" style="width:320px; height:400px"></canvas>

canvas的实际大小的640px × 800px,但是实际渲染到页面的大小是320px × 400px,相当于缩小一倍来显示。

那么在3倍屏幕下就是:

<canvas width="960" height="1200" style="width:320px; height:400px"></canvas>

因此,要使canvas适配高倍屏,就是要将canvas放大到设备像素比来绘制,最后将canvas压缩成一倍的物理大小来展示。如下:

myCanvas.style.width = myCanvas.width + 'px';
myCanvas.style.height = myCanvas.height + 'px';
 
myCanvas.width = myCanvas.width * ratio;
myCanvas.height = myCanvas.height * ratio;
3.4 绘制

由于 Canvas 放大后,相应的绘制图片时也要放大,我们可以采用下面的方法进行处理

context.font = "36px Georgia"; //一倍屏下18px字体
context.fillStyle = "#999";
context.fillText("我是清晰的文字", 50*ratio, 50*ratio);// 坐标位置乘以像素比

这样就可以解决 canvas 在高清屏中绘制模糊的问题.


[参考链接]
https://www.jianshu.com/p/2cd5143cf9aa
https://github.com/jondavidjohn/hidpi-canvas-polyfill

<think>好的,我现在需要帮助用户在Vue2中使用Canvas绘制印章。首先,我需要理清楚整个步骤,确保覆盖所有必要的部分。用户可能对Vue和Canvas都有一定的了解,但可能需要具体的实现指导。 首先,我应该考虑在Vue组件中如何集成Canvas。Vue的生命周期钩子在这里很重要,尤其是mounted阶段,因为这时候DOM元素已经准备好,可以安全地操作。所以,我应该建议用户在mounted钩子中调用绘制函数。 接下来,绘制印章的基本结构。Canvas的基本操作包括获取上下文,设置填充和描边样式,绘制形状和文本。印章通常是一个圆形,带有内外边框,中间有文字,可能还有五角星或者其他图案。所以步骤可能包括绘制圆形、添加内外边框、绘制中间的图标(比如五角星),然后添加环形文字和底部文字。 然后,需要考虑文字环绕效果。Canvas本身不支持环形文字,所以需要使用路径和旋转来模拟。这可能涉及到保存和恢复上下文状态,计算每个字符的位置和角度,这需要一些三角函数的知识。可能需要将文字拆分成字符,逐个绘制,并调整每个字符的位置和旋转角度。 另外,五角星的绘制可能需要使用路径,计算各个点的坐标。五角星的顶点坐标可以用极坐标公式计算,涉及到角度和半径的转换。这里可能需要使用Math.cos和Math.sin函数,并根据五角星的结构确定各个点的位置。 颜色方面,通常印章是红色的,所以填充样式和描边样式应该设置为红色。可能需要调整线条的宽度,使边框看起来合适。 关于代码结构,应该在Vue组件的模板中添加一个canvas元素,并设置ref属性以便在Vue中访问。然后,在methods中定义绘制印章的方法,在mounted钩子中调用这个方法。 测试部分可能需要提醒用户调整canvas的宽高,确保清晰度,以及处理不同尺寸时的缩放问题。此外,文字内容过长可能需要调整字体大小或间距。 可能遇到的问题包括文字位置不准确、旋转角度计算错误、五角星绘制不正确等。需要确保所有的坐标和角度计算正确,可能需要逐步调试。 最后,应该提供一个完整的示例代码,供用户参考和修改。确保代码中的注释清晰,解释每个部分的作用,帮助用户理解实现细节。</think>在Vue2中使用Canvas绘制印章的实现步骤如下(分步骤说明): 一、组件基础结构 ```html <template> <div class="seal-container"> <canvas ref="sealCanvas" :width="size" :height="size"></canvas> </div> </template> <script> export default { props: { size: { type: Number, default: 300 }, // 画布尺寸 company: { type: String, default: "某某科技有限公司" }, // 环形文字 text: { type: String, default: "电子签章专用章" } // 底部文字 }, mounted() { this.drawSeal(); }, methods: { drawSeal() { // 初始化Canvas const canvas = this.$refs.sealCanvas; const ctx = canvas.getContext('2d'); // 清空画布 ctx.clearRect(0, 0, this.size, this.size); // 后续绘制步骤... } } } </script> ``` 二、核心绘制步骤(在drawSeal方法中实现) 1. 绘制基础圆形 ```javascript // 设置绘制基准点 const center = this.size / 2; const radius = this.size * 0.4; // 绘制外圆 ctx.beginPath(); ctx.arc(center, center, radius, 0, Math.PI * 2); ctx.strokeStyle = '#f00'; ctx.lineWidth = 4; ctx.stroke(); // 绘制内圆 ctx.beginPath(); ctx.arc(center, center, radius * 0.9, 0, Math.PI * 2); ctx.stroke(); ``` 2. 绘制五角星 ```javascript function drawStar(ctx, cx, cy, r) { ctx.save(); ctx.beginPath(); const angle = Math.PI / 2.5; // 72度弧度值 for(let i = 0; i < 5; i++) { const x = cx + Math.cos(angle * i - Math.PI/2) * r; const y = cy + Math.sin(angle * i - Math.PI/2) * r; ctx.lineTo(x, y); } ctx.closePath(); ctx.fillStyle = '#f00'; ctx.fill(); ctx.restore(); } // 调用绘制 drawStar(ctx, center, center, radius * 0.15); ``` 3. 环形文字绘制 ```javascript ctx.textAlign = 'center'; ctx.textBaseline = 'middle'; ctx.fillStyle = '#f00'; ctx.font = `${radius * 0.12}px Microsoft YaHei`; // 文字环绕算法 const angle = Math.PI * 2 / this.company.length; ctx.save(); ctx.translate(center, center); ctx.rotate(-Math.PI/2); for(let i = 0; i < this.company.length; i++) { ctx.save(); ctx.rotate(angle * i); ctx.fillText( this.company[i], radius * 0.75, radius * 0.02 ); ctx.restore(); } ctx.restore(); ``` 4. 底部文字绘制 ```javascript ctx.fillStyle = '#f00'; ctx.font = `${radius * 0.15}px Microsoft YaHei`; ctx.fillText( this.text, center, center + radius * 0.5 ); ``` 三、使用注意事项 1. 高清显示处理:添加CSS样式避免模糊 ```css .seal-container canvas { width: 100%; height: 100%; } ``` 2. 自适应处理建议: - 监听容器尺寸变化 - 使用window.devicePixelRatio处理高清屏 - 添加redraw方法响应props变化 ```javascript watch: { size() { this.$nextTick(this.drawSeal); }, company() { this.drawSeal(); }, text() { this.drawSeal(); } } ``` 四、完整组件示例 ```html <template> <div class="seal-container"> <canvas ref="sealCanvas" :width="size" :height="size"></canvas> </div> </template> <script> export default { props: { /* 同上 */ }, mounted() { this.drawSeal(); }, methods: { drawSeal() { const canvas = this.$refs.sealCanvas; const ctx = canvas.getContext('2d'); ctx.clearRect(0, 0, this.size, this.size); // 绘制逻辑... }, drawStar() { /* 同上 */ } }, watch: { /* 同上 */ } } </script> ``` 五、常见问题处理 1. 文字重叠:调整radius * 0.75参数 2. 旋转偏移:检查translate和rotate的顺序 3. 字体模糊:确保canvas宽高与CSS尺寸一致 4. 移动端适配:添加viewport meta标签 该实现方案支持响应式配置,通过props可调整印章尺寸、文字内容等参数,适用于电子签名、合同系统等场景。
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

suwu150

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值