Edge(含IE11)中Canvas绘制SVG出现偏移问题求助
解决SVG序列化后在Edge/IE11 Canvas绘制的偏移截断问题
嗨,我之前也踩过微软浏览器SVG转Canvas的兼容坑,看了你的代码和对比图,这几个关键点应该是导致Edge/IE里出现偏移、截断的原因,咱们来一步步修复:
问题根源拆解
从你的代码和现象来看,核心问题出在三个地方:
- Edge对SVG Data URI的解析兼容问题:
encodeURIComponent处理后的SVG字符串,在Edge里可能出现解析异常,导致图片尺寸计算错误 - Image元素宽高获取的浏览器差异:在Edge/IE中,
new Image()加载SVG后,width/height属性不一定能正确返回SVG的实际尺寸,原代码里的分支逻辑依赖这个不可靠的数值,自然会算错比例 - SVG尺寸源数据的准确性:原代码只用了jQuery获取的SVG宽高,没有考虑SVG本身的
viewBox属性,而很多SVG是靠viewBox来定义比例的,这也会导致缩放偏差
具体修复方案
1. 替换Data URI为Blob URL
Edge对Blob URL的支持比复杂编码的Data URI稳定得多,能避免解析错误:
// 替换原有的svgString生成逻辑 var svgBlob = new Blob([svgURL], {type: 'image/svg+xml;charset=utf-8'}); var svgBlobURL = URL.createObjectURL(svgBlob); // 记得在图片加载完成后释放Blob URL,避免内存泄漏 this._img.onload = function() { URL.revokeObjectURL(svgBlobURL); // 后续绘制逻辑... }
2. 从SVG本身获取精准尺寸
不要依赖Image元素的宽高,直接从SVG的viewBox或原生属性读取真实尺寸:
var svgElement = this._$svgElement[0]; // 优先取viewBox的尺寸(SVG的实际比例基准),没有的话再用width/height属性 var viewBox = svgElement.viewBox.baseVal; var svgWidth, svgHeight; if (viewBox.width !== 0 && viewBox.height !== 0) { svgWidth = viewBox.width; svgHeight = viewBox.height; } else { svgWidth = parseFloat(svgElement.getAttribute('width')) || this._initSVGWidth; svgHeight = parseFloat(svgElement.getAttribute('height')) || this._initSVGHeight; }
3. 统一缩放计算逻辑,去掉浏览器分支
原代码里的IE分支逻辑容易出错,换成统一的比例计算,确保所有浏览器行为一致:
this._img.onload = function () { URL.revokeObjectURL(svgBlobURL); // 计算不超出Canvas的最大缩放比例 var scaleX = this._canvas.width / svgWidth; var scaleY = this._canvas.height / svgHeight; var scale = Math.min(scaleX, scaleY); // 计算最终绘制的宽高 newImageWidth = svgWidth * scale; newImageHeight = svgHeight * scale; // 计算居中位置 this._imgData = { x: (this._canvas.width - newImageWidth) / 2, y: (this._canvas.height - newImageHeight) / 2, w: newImageWidth, h: newImageHeight }; // 清空Canvas并绘制 this._ctx.clearRect(0, 0, this._canvas.width, this._canvas.height); this._ctx.drawImage(this._img, this._imgData.x, this._imgData.y, newImageWidth, newImageHeight); }.bind(this);
完整修复后的代码
this._$svgElement = this._props._svgContainer.find('svg'); this._initSVGWidth = this._$svgElement.width(); this._initSVGHeight = this._$svgElement.height(); var svgElement = this._$svgElement[0]; // 获取SVG真实尺寸 var viewBox = svgElement.viewBox.baseVal; var svgWidth, svgHeight; if (viewBox.width !== 0 && viewBox.height !== 0) { svgWidth = viewBox.width; svgHeight = viewBox.height; } else { svgWidth = parseFloat(svgElement.getAttribute('width')) || this._initSVGWidth; svgHeight = parseFloat(svgElement.getAttribute('height')) || this._initSVGHeight; } var svgURL = new XMLSerializer().serializeToString(svgElement); // 改用Blob URL加载SVG var svgBlob = new Blob([svgURL], {type: 'image/svg+xml;charset=utf-8'}); var svgBlobURL = URL.createObjectURL(svgBlob); var newImageWidth, newImageHeight; this._ctx.clearRect(0, 0, this._canvas.width, this._canvas.height); this._img = new Image(); this._img.onload = function () { URL.revokeObjectURL(svgBlobURL); // 统一计算缩放比例和位置 var scaleX = this._canvas.width / svgWidth; var scaleY = this._canvas.height / svgHeight; var scale = Math.min(scaleX, scaleY); newImageWidth = svgWidth * scale; newImageHeight = svgHeight * scale; this._imgData = { x: (this._canvas.width - newImageWidth) / 2, y: (this._canvas.height - newImageHeight) / 2, w: newImageWidth, h: newImageHeight }; this._ctx.drawImage(this._img, this._imgData.x, this._imgData.y, newImageWidth, newImageHeight); }.bind(this); this._img.src = svgBlobURL;
额外适配IE11的注意事项
如果需要兼容IE11,得引入两个polyfill:
blob-polyfill:让IE11支持Blob对象url-polyfill:让IE11支持URL.createObjectURL和URL.revokeObjectURL
效果对比
- Edge浏览器(修复后效果):

- Chrome浏览器(预期效果):

内容的提问来源于stack exchange,提问作者breezertwo
相关产品推荐
相关产品推荐

