画布 toDataUrl 没有在最新的 safari 上返回正确的图像
Posted
技术标签:
【中文标题】画布 toDataUrl 没有在最新的 safari 上返回正确的图像【英文标题】:Canvas toDataUrl not returning proper image on latest safari 【发布时间】:2021-12-11 02:45:37 【问题描述】:我从Convert SVG to image (JPEG, PNG, etc.) in the browser使用了这个sn-p
它在 chrome 中运行良好,但在最新的 safari update 15 之后,生成的图像不正确。
这是 chrome 图片 https://pastebin.com/hcLAS51W
这是 safari 生成的图像 https://pastebin.com/S7tEWQsQ
const base64Image = 'PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjkyLjk3NzUyODA4OTg4NzY0IDEyOSAzNzQuMDQ0OTQzODIwMjI0NyAxMTAiIGNsYXNzPSJQU1BERktpdC04czFzYmM1MTN0MzRxMzg4OTViNGJoM2p2biIgZm9jdXNhYmxlPSJmYWxzZSIgZGF0YS10ZXN0aWQ9Imluay1zdmciIHN0eWxlPSJsZWZ0OiAwcHg7IHRvcDogMHB4OyB3aWR0aDogMTAwJTsgaGVpZ2h0OiAxMDAlOyBvcGFjaXR5OiAxOyBvdmVyZmxvdzogaGlkZGVuOyI+PHJlY3QgeD0iOTcuOTc3NTI4MDg5ODg3NjQiIHk9IjEzNCIgd2lkdGg9IjM2NC4wNDQ5NDM4MjAyMjQ3IiBoZWlnaHQ9IjEwMCIgY2xhc3M9IlBTUERGS2l0LTc4NzFyZmh5eGI3cGRta3MxbjN4N2F2dnRhIiBzdHlsZT0iZmlsbDogdHJhbnNwYXJlbnQ7Ii8+PGcgc3R5bGU9ImZpbGw6IHRyYW5zcGFyZW50OyI+PGc+PHBhdGggY2xhc3M9IlBTUERGS2l0LTQ3cjV6eHB0eWtoanFwenFkdWU5YWNwaHR6IFBTUERGS2l0LTQ0OG5jcjVtdWhycGM4cDh5dmFkeTUxZW1jIFBTUERGS2l0LVNtb290aC1MaW5lcyIgZD0iTSAyMTIuNzYzODUwODAyMDQ4MiwxNjYuMDQwNTk0NDE4MjY3NDYgQyAxODMuNiwxNjAuNyAxNTQuNSwxNTUuNCAxMzUuNiwxNTIuMyBDIDExNi43LDE0OS4xIDEwNy45LDE0OC4yIDEwNC4wLDE0OS45IEMgMTAwLjIsMTUxLjUgMTAxLjEsMTU1LjkgMTAzLjMsMTYwLjcgQyAxMDUuNSwxNjUuNiAxMDguOSwxNzAuOSAxMTYuNCwxNzYuMiBDIDEyMy45LDE4MS41IDEzNS42LDE4Ni44IDE0Mi45LDE4Ny4xIEMgMTUwLjIsMTg3LjMgMTUzLjEsMTgyLjUgMTU1LjUsMTc3LjYgQyAxNTcuOSwxNzIuOCAxNTkuOSwxNjguMCAxNjEuMSwxNjUuMyBDIDE2Mi4zLDE2Mi43IDE2Mi44LDE2Mi4yIDE2NS43LDE2NS44IEMgMTY4LjYsMTY5LjQgMTczLjksMTc3LjIgMTgwLjcsMTg1LjQgQyAxODcuNSwxOTMuNiAxOTUuOCwyMDIuMyAyMDIuMSwyMDcuNiBDIDIwOC40LDIxMi45IDIxMi44LDIxNC45IDIxOC4zLDIxNS42IEMgMjIzLjksMjE2LjMgMjMwLjcsMjE1LjggMjM1LjYsMjEyLjkgQyAyNDAuNCwyMTAuMCAyNDMuMywyMDQuNyAyNDYuMywxOTcuMiBDIDI0OS4yLDE4OS43IDI1Mi4xLDE4MC4xIDI1My41LDE3NS4wIEMgMjU1LjAsMTY5LjkgMjU1LjAsMTY5LjQgMjU1LjIsMTY5LjIgQyAyNTUuNSwxNjguOSAyNTYuMCwxNjguOSAyNTYuOSwxNjkuNCBDIDI1Ny45LDE2OS45IDI1OS40LDE3MC45IDI2MS41LDE3MS44IEMgMjYzLjcsMTcyLjggMjY2LjYsMTczLjggMjY5LjgsMTc0LjMgQyAyNzIuOSwxNzQuNyAyNzYuMywxNzQuNyAyNzkuMywxNzQuMCBDIDI4Mi4yLDE3My4zIDI4NC42LDE3MS44IDI4Ni41LDE2OS45IEMgMjg4LjUsMTY4LjAgMjg5LjksMTY1LjYgMjkwLjksMTYxLjIgQyAyOTEuOSwxNTYuOSAyOTIuNCwxNTAuNiAyOTAuMiwxNDYuMCBDIDI4OC4wLDE0MS40IDI4My4xLDEzOC41IDI3Ny44LDEzNy4wIEMgMjcyLjUsMTM1LjYgMjY2LjYsMTM1LjYgMjYzLjUsMTM1LjYgQyAyNjAuMywxMzUuNiAyNTkuOCwxMzUuNiAyNTkuNCwxMzYuMSBDIDI1OC45LDEzNi42IDI1OC40LDEzNy41IDI2MS4zLDE0MC45IEMgMjY0LjIsMTQ0LjMgMjcwLjUsMTUwLjEgMjgzLjksMTU2LjEgQyAyOTcuMiwxNjIuMiAzMTcuNiwxNjguNSAzMjkuNSwxNzEuNiBDIDM0MS40LDE3NC43IDM0NC44LDE3NC43IDM1MC44LDE3NC4zIEMgMzU2LjksMTczLjggMzY1LjcsMTcyLjggMzcwLjMsMTcyLjEgQyAzNzQuOSwxNzEuNCAzNzUuNCwxNzAuOSAzNzUuNiwxNzIuMyBDIDM3NS44LDE3My44IDM3NS44LDE3Ny4yIDM3NS44LDE4Mi43IEMgMzc1LjgsMTg4LjMgMzc1LjgsMTk2LjAgMzc1LjYsMjAzLjUgQyAzNzUuNCwyMTEuMCAzNzQuOSwyMTguMiAzNzQuOSwyMjMuMSBDIDM3NC45LDIyNy45IDM3NS40LDIzMC4zIDM3OS43LDIzMS41IEMgMzg0LjEsMjMyLjcgMzkyLjMsMjMyLjcgNDAwLjYsMjMyLjIgQyA0MDguOCwyMzEuOCA0MTcuMSwyMzAuOCA0MjUuNiwyMjkuNiBDIDQzNC4xLDIyOC40IDQ0Mi44LDIyNi45IDQ0OC42LDIyNi4wIEMgNDU0LjUsMjI1LjAgNDU3LjQsMjI0LjUgNDYwLjMsMjI0LjAiIGRhdGEtdGVzdGlkPSJpbmstcGF0aCIgc3R5bGU9InN0cm9rZTogcmdiKDAsIDAsIDApOyBzdHJva2Utd2lkdGg6IDRweDsgcG9pbnRlci1ldmVudHM6IG5vbmU7Ii8+PHBhdGggY2xhc3M9IlBTUERGS2l0LTQ3cjV6eHB0eWtoanFwenFkdWU5YWNwaHR6IFBTUERGS2l0LTQ0OG5jcjVtdWhycGM4cDh5dmFkeTUxZW1jIFBTUERGS2l0LVNtb290aC1MaW5lcyIgc3Ryb2tlPSJ0cmFuc3BhcmVudCIgZD0iTSAyMTIuNzYzODUwODAyMDQ4MiwxNjYuMDQwNTk0NDE4MjY3NDYgQyAxODMuNiwxNjAuNyAxNTQuNSwxNTUuNCAxMzUuNiwxNTIuMyBDIDExNi43LDE0OS4xIDEwNy45LDE0OC4yIDEwNC4wLDE0OS45IEMgMTAwLjIsMTUxLjUgMTAxLjEsMTU1LjkgMTAzLjMsMTYwLjcgQyAxMDUuNSwxNjUuNiAxMDguOSwxNzAuOSAxMTYuNCwxNzYuMiBDIDEyMy45LDE4MS41IDEzNS42LDE4Ni44IDE0Mi45LDE4Ny4xIEMgMTUwLjIsMTg3LjMgMTUzLjEsMTgyLjUgMTU1LjUsMTc3LjYgQyAxNTcuOSwxNzIuOCAxNTkuOSwxNjguMCAxNjEuMSwxNjUuMyBDIDE2Mi4zLDE2Mi43IDE2Mi44LDE2Mi4yIDE2NS43LDE2NS44IEMgMTY4LjYsMTY5LjQgMTczLjksMTc3LjIgMTgwLjcsMTg1LjQgQyAxODcuNSwxOTMuNiAxOTUuOCwyMDIuMyAyMDIuMSwyMDcuNiBDIDIwOC40LDIxMi45IDIxMi44LDIxNC45IDIxOC4zLDIxNS42IEMgMjIzLjksMjE2LjMgMjMwLjcsMjE1LjggMjM1LjYsMjEyLjkgQyAyNDAuNCwyMTAuMCAyNDMuMywyMDQuNyAyNDYuMywxOTcuMiBDIDI0OS4yLDE4OS43IDI1Mi4xLDE4MC4xIDI1My41LDE3NS4wIEMgMjU1LjAsMTY5LjkgMjU1LjAsMTY5LjQgMjU1LjIsMTY5LjIgQyAyNTUuNSwxNjguOSAyNTYuMCwxNjguOSAyNTYuOSwxNjkuNCBDIDI1Ny45LDE2OS45IDI1OS40LDE3MC45IDI2MS41LDE3MS44IEMgMjYzLjcsMTcyLjggMjY2LjYsMTczLjggMjY5LjgsMTc0LjMgQyAyNzIuOSwxNzQuNyAyNzYuMywxNzQuNyAyNzkuMywxNzQuMCBDIDI4Mi4yLDE3My4zIDI4NC42LDE3MS44IDI4Ni41LDE2OS45IEMgMjg4LjUsMTY4LjAgMjg5LjksMTY1LjYgMjkwLjksMTYxLjIgQyAyOTEuOSwxNTYuOSAyOTIuNCwxNTAuNiAyOTAuMiwxNDYuMCBDIDI4OC4wLDE0MS40IDI4My4xLDEzOC41IDI3Ny44LDEzNy4wIEMgMjcyLjUsMTM1LjYgMjY2LjYsMTM1LjYgMjYzLjUsMTM1LjYgQyAyNjAuMywxMzUuNiAyNTkuOCwxMzUuNiAyNTkuNCwxMzYuMSBDIDI1OC45LDEzNi42IDI1OC40LDEzNy41IDI2MS4zLDE0MC45IEMgMjY0LjIsMTQ0LjMgMjcwLjUsMTUwLjEgMjgzLjksMTU2LjEgQyAyOTcuMiwxNjIuMiAzMTcuNiwxNjguNSAzMjkuNSwxNzEuNiBDIDM0MS40LDE3NC43IDM0NC44LDE3NC43IDM1MC44LDE3NC4zIEMgMzU2LjksMTczLjggMzY1LjcsMTcyLjggMzcwLjMsMTcyLjEgQyAzNzQuOSwxNzEuNCAzNzUuNCwxNzAuOSAzNzUuNiwxNzIuMyBDIDM3NS44LDE3My44IDM3NS44LDE3Ny4yIDM3NS44LDE4Mi43IEMgMzc1LjgsMTg4LjMgMzc1LjgsMTk2LjAgMzc1LjYsMjAzLjUgQyAzNzUuNCwyMTEuMCAzNzQuOSwyMTguMiAzNzQuOSwyMjMuMSBDIDM3NC45LDIyNy45IDM3NS40LDIzMC4zIDM3OS43LDIzMS41IEMgMzg0LjEsMjMyLjcgMzkyLjMsMjMyLjcgNDAwLjYsMjMyLjIgQyA0MDguOCwyMzEuOCA0MTcuMSwyMzAuOCA0MjUuNiwyMjkuNiBDIDQzNC4xLDIyOC40IDQ0Mi44LDIyNi45IDQ0OC42LDIyNi4wIEMgNDU0LjUsMjI1LjAgNDU3LjQsMjI0LjUgNDYwLjMsMjI0LjAiIGRhdGEtdGVzdGlkPSJjbGlja2FibGUtcGF0aCIgc3R5bGU9InN0cm9rZS13aWR0aDogNXB4OyIvPjwvZz48L2c+PC9zdmc+';
const svg = window.atob(base64Image);
svgToPng(svg,(imgData)=>
const pngImage = document.createElement('img');
document.body.appendChild(pngImage);
pngImage.src=imgData;
);
function svgToPng(svg, callback)
const url = getSvgUrl(svg);
svgUrlToPng(url, (imgData) =>
callback(imgData);
URL.revokeObjectURL(url);
);
function getSvgUrl(svg)
return URL.createObjectURL(new Blob([svg], type: 'image/svg+xml' ));
function svgUrlToPng(svgUrl, callback)
const svgImage = document.createElement('img');
// imgPreview.style.position = 'absolute';
// imgPreview.style.top = '-9999px';
document.body.appendChild(svgImage);
svgImage.onload = function ()
const canvas = document.createElement('canvas');
canvas.width = svgImage.clientWidth;
canvas.height = svgImage.clientHeight;
const canvasCtx = canvas.getContext('2d');
canvasCtx.drawImage(svgImage, 0, 0);
const imgData = canvas.toDataURL('image/png');
console.log(imgData, 'imgData') // This logged here gives different image in chrome and safari
callback(imgData);
// document.body.removeChild(imgPreview);
;
svgImage.src = svgUrl;
【问题讨论】:
您是否尝试在屏幕上显示画布以查看它是否正确?您有一堆步骤正在进行,最终结果不正确,但您无法确定哪一步正在中断。弄清楚这一点将大大有助于解决您的问题。 是的,画布在两个浏览器中都正确显示,即使您在 sn-p 上运行时也可以看到 哈,Safari,新的 Internet Explorer 您可以报告webkit错误here 【参考方案1】:drawImage()
使用没有固有宽度或高度的 SVG 图像是互操作的噩梦。
目前,规范文本确实遵循 Chrome 在此处的行为,但鉴于它们是唯一实现它的人,因此很难将其称为“标准”。
这里的一个问题是您正在根据<img>
元素的clientWidth
和clientHeight
属性设置画布大小。
这个<img>
元素嵌入了一个没有自己固有宽度和高度的SVG 图像。默认的width
因此被解释为100%
。
因此,您的 <img>
元素将占据容器 (<body>
) 的任何大小,并且您将画布大小设置为该主体的大小。
但是,这个大小应该只影响<img>
元素,当它的图像绘制在画布上时,这个<img>
元素应该(大部分)无关紧要。
在这种没有固有宽度或高度的图像的特殊情况下,重要的是输出画布的大小。在这种情况下,the current specs texts 要求使用CSS default sizing algorithm 来确定图像的大小,这基本上表示如果定义了使用固有宽度和高度,或者如果图像具有定义的固有纵横比(例如 viewBox),则乘以此比率的可用值,如果两者都不存在,则使用默认大小。 而这个默认大小是由输出画布大小决定的。
这里,<svg>
既没有 width
也没有 height
,但它确实有一个内在比率 (110/374.0449438202247),所以 width
将设置为画布宽度和高度将设置为此宽度 x 比率。
因此,drawImage
使用的正确计算的图像大小应该取决于画布大小,因此取决于用户的屏幕大小,我个人觉得这很令人不安。这就是 Chrome 所做的。
另一方面,Safari 使用代表width
和height
的viewBox
中的值。这种做法是有道理的,但在各种实践中存在问题(例如,使用 0 0 1 1
的 viewBox 来更轻松地在 SVG 中进行绝对定位是很常见的)。
在这种情况下,Firefox 根本不会绘制您的图像。
您可以通过不附加<img>
、根据<img>
、.naturalWidth
和.naturalHeight
设置画布宽度和高度以及设置根<svg>
元素的width
和height
为绝对值(即不是 %
)。
这将为您提供最好的跨浏览器体验和最可预测的体验(由于 Chrome 的行为,您的所有用户都将拥有不同大小的图像)。
const svg = `<svg
$/* We force width absolute width and height */""
xmlns="http://www.w3.org/2000/svg" viewBox="92.97752808988764 129 374.0449438202247 110" class="PSPDFKit-8s1sbc513t34q38895b4bh3jvn" focusable="false" data-testid="ink-svg" style="left: 0px; top: 0px; width: 100%; height: 100%; opacity: 1; overflow: hidden;">
<rect x="97.97752808988764" y="134" class="PSPDFKit-7871rfhyxb7pdmks1n3x7avvta" style="fill: transparent;"/>
<g style="fill: transparent;">
<g>
<path class="PSPDFKit-47r5zxptykhjqpzqdue9acphtz PSPDFKit-448ncr5muhrpc8p8yvady51emc PSPDFKit-Smooth-Lines"
d="M 212.7638508020482,166.04059441826746 C 183.6,160.7 154.5,155.4 135.6,152.3 C 116.7,149.1 107.9,148.2 104.0,149.9 C 100.2,151.5 101.1,155.9 103.3,160.7 C 105.5,165.6 108.9,170.9 116.4,176.2 C 123.9,181.5 135.6,186.8 142.9,187.1 C 150.2,187.3 153.1,182.5 155.5,177.6 C 157.9,172.8 159.9,168.0 161.1,165.3 C 162.3,162.7 162.8,162.2 165.7,165.8 C 168.6,169.4 173.9,177.2 180.7,185.4 C 187.5,193.6 195.8,202.3 202.1,207.6 C 208.4,212.9 212.8,214.9 218.3,215.6 C 223.9,216.3 230.7,215.8 235.6,212.9 C 240.4,210.0 243.3,204.7 246.3,197.2 C 249.2,189.7 252.1,180.1 253.5,175.0 C 255.0,169.9 255.0,169.4 255.2,169.2 C 255.5,168.9 256.0,168.9 256.9,169.4 C 257.9,169.9 259.4,170.9 261.5,171.8 C 263.7,172.8 266.6,173.8 269.8,174.3 C 272.9,174.7 276.3,174.7 279.3,174.0 C 282.2,173.3 284.6,171.8 286.5,169.9 C 288.5,168.0 289.9,165.6 290.9,161.2 C 291.9,156.9 292.4,150.6 290.2,146.0 C 288.0,141.4 283.1,138.5 277.8,137.0 C 272.5,135.6 266.6,135.6 263.5,135.6 C 260.3,135.6 259.8,135.6 259.4,136.1 C 258.9,136.6 258.4,137.5 261.3,140.9 C 264.2,144.3 270.5,150.1 283.9,156.1 C 297.2,162.2 317.6,168.5 329.5,171.6 C 341.4,174.7 344.8,174.7 350.8,174.3 C 356.9,173.8 365.7,172.8 370.3,172.1 C 374.9,171.4 375.4,170.9 375.6,172.3 C 375.8,173.8 375.8,177.2 375.8,182.7 C 375.8,188.3 375.8,196.0 375.6,203.5 C 375.4,211.0 374.9,218.2 374.9,223.1 C 374.9,227.9 375.4,230.3 379.7,231.5 C 384.1,232.7 392.3,232.7 400.6,232.2 C 408.8,231.8 417.1,230.8 425.6,229.6 C 434.1,228.4 442.8,226.9 448.6,226.0 C 454.5,225.0 457.4,224.5 460.3,224.0"
data-testid="ink-path" style="stroke: rgb(0, 0, 0); stroke-width: 4px; pointer-events: none;"/>
<path class="PSPDFKit-47r5zxptykhjqpzqdue9acphtz PSPDFKit-448ncr5muhrpc8p8yvady51emc PSPDFKit-Smooth-Lines" stroke="transparent"
d="M 212.7638508020482,166.04059441826746 C 183.6,160.7 154.5,155.4 135.6,152.3 C 116.7,149.1 107.9,148.2 104.0,149.9 C 100.2,151.5 101.1,155.9 103.3,160.7 C 105.5,165.6 108.9,170.9 116.4,176.2 C 123.9,181.5 135.6,186.8 142.9,187.1 C 150.2,187.3 153.1,182.5 155.5,177.6 C 157.9,172.8 159.9,168.0 161.1,165.3 C 162.3,162.7 162.8,162.2 165.7,165.8 C 168.6,169.4 173.9,177.2 180.7,185.4 C 187.5,193.6 195.8,202.3 202.1,207.6 C 208.4,212.9 212.8,214.9 218.3,215.6 C 223.9,216.3 230.7,215.8 235.6,212.9 C 240.4,210.0 243.3,204.7 246.3,197.2 C 249.2,189.7 252.1,180.1 253.5,175.0 C 255.0,169.9 255.0,169.4 255.2,169.2 C 255.5,168.9 256.0,168.9 256.9,169.4 C 257.9,169.9 259.4,170.9 261.5,171.8 C 263.7,172.8 266.6,173.8 269.8,174.3 C 272.9,174.7 276.3,174.7 279.3,174.0 C 282.2,173.3 284.6,171.8 286.5,169.9 C 288.5,168.0 289.9,165.6 290.9,161.2 C 291.9,156.9 292.4,150.6 290.2,146.0 C 288.0,141.4 283.1,138.5 277.8,137.0 C 272.5,135.6 266.6,135.6 263.5,135.6 C 260.3,135.6 259.8,135.6 259.4,136.1 C 258.9,136.6 258.4,137.5 261.3,140.9 C 264.2,144.3 270.5,150.1 283.9,156.1 C 297.2,162.2 317.6,168.5 329.5,171.6 C 341.4,174.7 344.8,174.7 350.8,174.3 C 356.9,173.8 365.7,172.8 370.3,172.1 C 374.9,171.4 375.4,170.9 375.6,172.3 C 375.8,173.8 375.8,177.2 375.8,182.7 C 375.8,188.3 375.8,196.0 375.6,203.5 C 375.4,211.0 374.9,218.2 374.9,223.1 C 374.9,227.9 375.4,230.3 379.7,231.5 C 384.1,232.7 392.3,232.7 400.6,232.2 C 408.8,231.8 417.1,230.8 425.6,229.6 C 434.1,228.4 442.8,226.9 448.6,226.0 C 454.5,225.0 457.4,224.5 460.3,224.0"
data-testid="clickable-path" style="stroke-width: 5px;"/>
</g>
</g>
</svg>`;
svgToPng(svg, (imgData) =>
const pngImage = document.createElement('img');
pngImage.src = imgData;
);
function svgToPng(svg, callback)
const url = getSvgUrl(svg);
svgUrlToPng(url, (imgData) =>
callback(imgData);
URL.revokeObjectURL(url);
);
function getSvgUrl(svg)
return URL.createObjectURL(new Blob([svg],
type: 'image/svg+xml'
));
function svgUrlToPng(svgUrl, callback)
const svgImage = document.createElement('img');
// do not append the <img>
svgImage.onload = function()
const canvas = document.createElement('canvas');
canvas.width = svgImage.naturalWidth;
canvas.height = svgImage.naturalHeight;
const canvasCtx = canvas.getContext('2d');
canvasCtx.drawImage(svgImage, 0, 0);
// show the <canvas> to debug
document.body.append(canvas);
const imgData = canvas.toDataURL('image/png');
callback(imgData);
;
svgImage.src = svgUrl;
canvas
background: chocolate;
【讨论】:
谢谢。这有帮助,但生成的图像质量低。我们如何放大图像?或图像尺寸 通过将宽度和高度属性设置为您想要的值。 同样的问题会发生,如果我增加画布的高度和宽度,在 chrome 中它可以工作,但在 safari 中图像出现在左上角,其余空间被空白占据 请再读一遍这个答案。您需要更改的不是画布的大小,而是您正在绘制的根<svg>
的width
和height
属性。以上是关于画布 toDataUrl 没有在最新的 safari 上返回正确的图像的主要内容,如果未能解决你的问题,请参考以下文章
从画布获取 img (toDataURL) --> 可能无法导出受污染的画布
toDataURL() 无法在 Blazor Webassembly 中使用 JS 互操作保存 HTML5 画布的内容?