hidpi-canvas-polyfill.js 多次调用canvas.getContext('2d') 导致画布放大问题

本文介绍hidpi-canvas-polyfill.js在多次调用canvas.getContext('2d')时导致canvas元素异常放大的问题及修复方法。通过添加判断条件避免重复放大。

相信做前端的 对hidpi-canvas-polyfill.js  都不陌生;

手机端的高清屏上 用canvas 画图,大部分 都用到了hidpi-canvas-polyfill.js ;

这个包在高清屏上很好用,但在使用时 可能也会遇到一些问题。

我在使用时就遇到一个,和大家分享一下。

(function(prototype) {
	prototype.getContext = (function(_super) {
		return function(type) {
			var backingStore, ratio,
				context = _super.call(this, type);

			if (type === '2d') {

				backingStore = context.backingStorePixelRatio ||
							context.webkitBackingStorePixelRatio ||
							context.mozBackingStorePixelRatio ||
							context.msBackingStorePixelRatio ||
							context.oBackingStorePixelRatio ||
							context.backingStorePixelRatio || 1;

				ratio = (window.devicePixelRatio || 1) / backingStore;

				if (ratio > 1) {
					this.style.height = this.height + 'px';
					this.style.width = this.width + 'px';
					this.width *= ratio;
					this.height *= ratio;
				}
			}

			return context;
		};
	})(prototype.getContext);
})(HTMLCanvasElement.prototype);

这是 hidpi-canvas.js 中的一段代码,

这段代码在我们调用canvas.getContext("2d")时 被执行。当调用一次时一切正常,但调用多次,就会有问题。它会把 canvas 元素放大到原来的 几倍。

解决方案:

(function(prototype) {
	prototype.getContext = (function(_super) {
		return function(type) {
			var backingStore, ratio,
				context = _super.call(this, type);

			if (type === '2d') {

				backingStore = context.backingStorePixelRatio ||
							//context.webkitBackingStorePixelRatio ||
							context.mozBackingStorePixelRatio ||
							context.msBackingStorePixelRatio ||
							context.oBackingStorePixelRatio ||
							context.backingStorePixelRatio || 1;

				ratio = (window.devicePixelRatio || 1) / backingStore;

				if (ratio > 1&&$(this).attr("val")!="true") {
					if((this.style.height == '' || Number(this.style.height.replace('px', '')) * ratio != this.height)) {
						this.style.height = this.height + 'px';
						this.style.width = this.width + 'px';
						this.width *= ratio;
						this.height *= ratio;
						$(this).attr("val",true)
					}
				}
			}

			return context;
		};
	})(prototype.getContext);
})(HTMLCanvasElement.prototype);
在 上面加一个判断,这样多次获取canvas.getContext("2d") 时就不会 吧canvas 元素放大很多倍了。 现在测试 只在 Android 6 原生的 WebView 上 才会出现这个问题。


### 解决 Canvas 图像绘制模糊的方法 Canvas 图像绘制模糊的主要原因是由于设备像素比 (devicePixelRatio) 和 CSS 像素之间的差异造成的。当在高分辨率屏幕上渲染时,如果没有适当地调整 Canvas 的实际尺寸和显示尺寸,就会导致图像失真或模糊。 #### 方法一:调整 Canvas 尺寸以匹配设备像素比 通过设置 `width` 和 `height` 属性来放大 Canvas 的实际大小,并保持其样式宽度不变,从而提高绘图清晰度[^1]。 ```javascript function scaleCanvas(canvas) { const dpr = window.devicePixelRatio || 1; const rect = canvas.getBoundingClientRect(); canvas.width = rect.width * dpr; // 实际像素数 canvas.height = rect.height * dpr; const ctx = canvas.getContext('2d'); ctx.scale(dpr, dpr); // 缩放上下文以便正常绘制 } ``` 调用此函数即可使 Canvas 自适应当前屏幕的 DPI 设置。 --- #### 方法二:使用整数值定位和缩放 Canvas 中的坐标系基于浮点数计算,但在某些情况下可能会引入亚像素误差,这也会造成模糊效果。因此,在绘制前应确保所有的位置参数均为整数[^3]。 例如: ```javascript const ctx = canvas.getContext('2d'); // 使用 Math.floor 或者其他方法取整 ctx.drawImage(image, Math.floor(x), Math.floor(y), width, height); ``` 这样可以有效减少因非精确值引起的边缘锯齿现象。 --- #### 方法三:应用高质量滤镜模式 对于 HTML5 Canvas,默认采用的是线性插值算法来进行图像缩放操作,这种做法虽然平滑过渡较好,但对于追求锐利边界的场景并不理想。可以通过修改全局属性 `imageSmoothingEnabled` 来关闭抗锯齿功能[^4]。 ```javascript const ctx = canvas.getContext('2d'); ctx.imageSmoothingEnabled = false; // 关闭图像平滑处理 ctx.mozImageSmoothingEnabled = false; // 针对 Firefox 浏览器兼容性补充 ctx.webkitImageSmoothingEnabled = false; // Safari 兼容性支持 ``` 上述代码片段能够显著改善加载后的图形质量,尤其适用于图标或者字体类素材展示需求较高的场合。 --- #### 方法四:利用第三方库辅助开发 尽管手动优化可行,但如果项目复杂度较高,则推荐借助成熟的工具包简化流程。比如前面提到过的 **hidpi-canvas-polyfill** ,它自动完成了大部分针对 Retina 显示器及其他 HiDPI 设备的最佳实践配置工作^。 安装方式如下: ```bash npm install hidpi-canvas-polyfill --save ``` 随后只需简单初始化一次即可生效: ```javascript import 'hidpi-canvas-polyfill'; document.querySelectorAll('canvas').forEach((el) => el.hidpi()); ``` 注意该方案并未涉及特定于位图资源本身的额外修正逻辑,所以仍需结合前述技巧共同运用才能达到最佳视觉体验。 --- ### 总结 以上四种途径各有侧重,开发者可以根据具体应用场景灵活选用一种或多组合策略应对不同类型的挑战。无论是基础原理层面的理解还是高级技术手段的应用都不可或缺。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值