在 HTML5画布上绘制 SVG 文件

在 HTML5画布上绘制 SVG 文件有默认的方法吗?Google Chrome 支持将 SVG 作为图像加载(并且只使用 drawImage) ,但是开发者控制台警告说 resource interpreted as image but transferred with MIME type image/svg+xml

我知道有可能将 SVG 转换为画布命令(如 这个问题中的命令) ,但我希望不需要这样做。我不关心旧的浏览器(所以如果 FireFox 4和 IE9支持某些东西,那就足够了)。

293352 次浏览

编辑: 2019年12月

现在 所有主要的浏览器支持 Path2D ()构造函数,“允许在2D 画布表面上声明路径对象”。


编辑: 2014年11月

你现在可以使用 ctx.drawImage来绘制具有 .svg源代码 在 < a href = “ https://caniuse.com/mdn-api _ gallasrenderingcontext2d _ draimage _ svgimageelement _ source _ image”rel = “ nofollow noReferrer”> 部分但不是所有的浏览器 的 HTMLImageElements (75% 的覆盖率: Chrome、 IE11和 Safari,Firefox 可以处理一些 bug,但是每晚都会修复它们)。

var img = new Image();
img.onload = function() {
ctx.drawImage(img, 0, 0);
}
img.src = "http://upload.wikimedia.org/wikipedia/commons/d/d2/Svg_example_square.svg";

实例在这里 。你应该在画布上看到一个绿色的正方形。页面上的第二个绿色方块与插入 DOM 中以供参考的 <svg>元素相同。

您还可以使用新的 Path2D 对象来绘制 SVG (字符串)路径:

var path = new Path2D('M 100,100 h 50 v 50 h 50');
ctx.stroke(path);

这就是一个活生生的例子。


2010年原始答案:

没有什么原生的东西可以让您在画布中原生地使用 SVG 路径。您必须转换自己或使用库来为您转换。

我建议查看 调查: (检查 网页演示)

画布 获取 SVG 文件的 URL 或 SVG 文件的文本,用 JavaScript 解析它并在画布上呈现结果。

正如 Simon 上面所说,使用 draImage 应该不会起作用,但是,使用 调查库和:

var c = document.getElementById('canvas');
var ctx = c.getContext('2d');
ctx.drawSvg(SVG_XML_OR_PATH_TO_SVG, dx, dy, dw, dh);

这来自于 Simon 上面提供的链接,该链接还提供了许多其他建议,并指出您需要链接到或下载画布.js 和 rgbcolor. js。它们允许您在 JavaScript 函数中通过 URL 或使用 SVG 标记之间的内联 SVG 代码来操作和加载 SVG。

Mozilla 有一种在画布上绘制 SVG 的简单方法,称为“ 将 DOM 对象绘制到画布中

您可以通过以下方法轻松地在画布上绘制简单的 svg:

  1. 将 svg 的源分配给 base64格式的图像
  2. 将图像绘制到画布上

注意: 该方法的唯一缺点是不能绘制嵌入在 svg中的图像

示范:

(注意,嵌入的图像只能在 svg中看到)

var svg = document.querySelector('svg');
var img = document.querySelector('img');
var canvas = document.querySelector('canvas');


// get svg data
var xml = new XMLSerializer().serializeToString(svg);


// make it base64
var svg64 = btoa(xml);
var b64Start = 'data:image/svg+xml;base64,';


// prepend a "header"
var image64 = b64Start + svg64;


// set it as the source of the img element
img.src = image64;


// draw the image onto the canvas
canvas.getContext('2d').drawImage(img, 0, 0);
svg, img, canvas {
display: block;
}
SVG


<svg height="40">
<rect width="40" height="40" style="fill:rgb(255,0,255);" />
<image xlink:href="https://en.gravatar.com/userimage/16084558/1a38852cf33713b48da096c8dc72c338.png?size=20" height="20px" width="20px" x="10" y="10"></image>
</svg>
<hr/><br/>


IMAGE
<img/>
<hr/><br/>
   

CANVAS
<canvas></canvas>
<hr/><br/>

对于@Matyas 的回答: 如果 svg 的映像也在 base64中,它将被绘制到输出中。

演示:

var svg = document.querySelector('svg');
var img = document.querySelector('img');
var canvas = document.querySelector('canvas');


// get svg data
var xml = new XMLSerializer().serializeToString(svg);


// make it base64
var svg64 = btoa(xml);
var b64Start = 'data:image/svg+xml;base64,';


// prepend a "header"
var image64 = b64Start + svg64;


// set it as the source of the img element
img.onload = function() {
// draw the image onto the canvas
canvas.getContext('2d').drawImage(img, 0, 0);
}
img.src = image64;
svg, img, canvas {
display: block;
}
SVG
<svg height="40" width="40">
<rect width="40" height="40" style="fill:rgb(255,0,255);" />
<image xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAEX0lEQVQ4jUWUyW6cVRCFv7r3/kO3u912nNgZgESAAgGBCJgFgxhW7FkgxAbxMLwBEmIRITbsQAgxCEUiSIBAYIY4g1EmYjuDp457+Lv7n+4tFjbwAHVOnVPnlLz75ht67OhhZg/M0p6d5tD9C8SNBBs5XBJhI4uNLC4SREA0UI9yJr2c4e6QO+v3WF27w+rmNrv9Pm7hxDyHFg5yYGEOYxytuRY2SYiSCIwgRgBQIxgjEAKuZWg6R9S0SCS4qKLZElY3HC5tp7QPtmlMN7HOETUTXBJjrEGsAfgPFECsQbBIbDGJZUYgGE8ugQyPm+o0STtTuGZMnKZEjRjjLIgAirEOEQEBDQFBEFFEBWLFtVJmpENRl6hUuFanTRAlbTeZarcx0R6YNZagAdD/t5N9+QgCYAw2jrAhpjM3zaSY4OJGTDrVwEYOYw2qioigoviq5MqF31m9fg1V5fCx+zn11CLNVnufRhBrsVFE1Ihpthu4KDYYwz5YQIxFBG7duMZnH31IqHL6wwnGCLFd4pez3/DaG2/x4GNPgBhEZG/GGlxkMVFkiNMYay3Inqxed4eP33uf7Y0uu90xWkGolFAru7sZn5w5w921m3u+su8vinEO02hEWLN/ANnL2rkvv2an2yd4SCKLM0JVBsCgAYZZzrnPP0eDRzXgfaCuPHXwuEYjRgmIBlQVVLl8/hKI4fRzz3L6uWe5+PMvnHz6aa4uX+D4yYe5vXaLH86eoyoLjLF476l9oKo9pi5HWONRX8E+YznOef7Vl1h86QWurlwjbc+QpikPPfoIcZLS39pmMikp8pzae6q6oqgriqrGqS+xeLScoMYSVJlfOMTl5RXW1+5w5fJVnFGWf1/mxEMnWPppiclkTLM5RdJoUBYFZVlQ5DnZMMMV167gixKLoXXsKGqnOHnqOJ/+/CfZ+XUiZ0jTmFv5mAvf/YjEliQ2vPD8Ir6qqEcZkzt38cMRo5WruFvfL9FqpyRxQhj0qLOax5I2S08+Tu/lFiGUGOPormxwuyfMnjrGrJa88uIixeYWl776lmrzNjmw8vcG8sU7ixpHMXFsCUVg9tABjEvRgzP82j7AhbyiX5Qcv2+Bvy7dYGZ1k7efeQB/Y4PBqGBtdYvb3SFzLcfqToZc/OB1zYeBSpUwLBlvjZidmWaSB1yaYOfn6LqI/r0hyU6P+cRSlhXjbEI2zvnt7y79oqQ3qeg4g6vKjCIXehtDmi6m0UnxVnCRkPUHVNt9qkLJxgXOCYNOg34v48raPaamU2o89/KKsQ9sTSpc0JK7NwdcX8s43Ek5cnSOLC/Z2R6Rj0ra0w2W1/t0xyWn51uk2Ri1QtSO6OU5d7OSi72cQeWxKG7p/Dp//JXTy6C1Pcbc6DMpPRtjTxChEznWhwVZUCKrjCrPoPDczHLmnLBdBgZlRRWUEBR3ZKrme5TlrTGlV440Y1IrXM9qQGi6mkG5V6uza7tUIeCDElTZ1L26elX+fcH/ACJBPYTJ4X8tAAAAAElFTkSuQmCC" height="20px" width="20px" x="10" y="10"></image></svg><br/>


IMAGE
<img/><br/>
   

CANVAS
<canvas></canvas><br/>

需要添加的是,为了在画布元素中正确显示 svg,需要将属性 身高宽度添加到 svg 根元素中,例如:

<svg height="256" width="421">...</svg>

或者

// Use this if to add the attributes programmatically
const svg = document.querySelector("#your-svg");


svg.setAttribute("width", `${width}`);
svg.setAttribute("height", `${height}`);

有关详细信息,请参阅 这个

由于矢量图形是潜在的 比例,我将提供一个方法,我已经尽可能类似于 SVG。此方法支持:

  • 一块可调整大小的画布

  • 透明度

  • 高分辨率图形(自动,但还没有捏支持)

  • 向两个方向扩展 SVG!

    (为了使用像素,您必须将新的长度除以旧的长度)

这是通过将 SVG 转换为画布函数 给你,然后将其添加到 svgRed()(在将 ctx的名称更改为 ctx2之后)来完成的。svgRed()函数在启动和像素比率变化(例如,增加缩放)时使用,但在画布缩放之前不使用(为了增加图像的大小)。它将结果转换成 Image,并且可以在任何时候被 ctx.drawImage(redBalloon, Math.round(Math.random() * w), Math.round(Math.random() * h))调用。要清除屏幕,请使用 ctx.clearRect(0, 0, w, h)

使用 SVG 对此进行测试后,我发现只要不将缩放设置为较大的值(我发现 window.devicePixelRatio为5时的速度是 SVG 的两倍多,而 window.devicePixelRatio为1时的速度大约是 SVG 的60倍) ,速度就会快很多倍。

这还有一个额外的好处,即允许许多“假 SVG”项同时存在,而不会干扰 HTML (如下面的代码所示)。如果屏幕调整了大小或缩放,您将需要再次呈现它(在我的示例中完全忽略)。

显示结果的画布按 devicePixelRatio缩放(以像素为单位) ,因此在绘制项目时要小心!缩放(与 ctx.scale()这个画布将导致一个潜在的模糊图像,所以一定要帐户的像素差异!

注意: 在 devicePixelRatio改变后,浏览器似乎需要一段时间来优化图像(有时大约一秒钟) ,因此,如示例所示,立即向画布发送图像可能不是一个好主意。

<!DOCTYPE html>
<html>


<head lang="en">
<title>Balloons</title>


<style>
* {
user-select: none;
-webkit-user-select: none;
}


body {
background-color: #303030;
}
</style>
</head>


<body>
<canvas id="canvas2" style="display: none" width="0" height="0"></canvas>
<canvas id="canvas"
style="position: absolute; top: 20px; left: 20px; background-color: #606060; border-radius: 25px;" width="0"
height="0"></canvas>
<script>
// disable pinches: hard to implement resizing
document.addEventListener("touchstart", function (e) {
if (e.touches.length > 1) {
e.preventDefault()
}
}, { passive: false })
document.addEventListener("touchmove", function (e) {
if (e.touches.length > 1) {
e.preventDefault()
}
}, { passive: false })
// disable trackpad zooming
document.addEventListener("wheel", e => {
if (e.ctrlKey) {
e.preventDefault()
}
}, {
passive: false
})


// This is the canvas that shows the result
const canvas = document.getElementById("canvas")
// This canvas is hidden and renders the balloon in the background
const canvas2 = document.getElementById("canvas2")


// Get contexts
const ctx = canvas.getContext("2d")
const ctx2 = canvas2.getContext("2d")
// Scale the graphic, if you want
const scaleX = 1
const scaleY = 1


// Set up parameters
var prevRatio, w, h, trueW, trueH, ratio, redBalloon


function draw() {
for (var i = 0; i < 1000; i++) {
ctx.drawImage(redBalloon, Math.round(Math.random() * w), Math.round(Math.random() * h))
}
requestAnimationFrame(draw)
}


// Updates graphics and canvas.
function updateSvg() {
var pW = trueW
var pH = trueH
trueW = window.innerWidth - 40
trueH = Math.max(window.innerHeight - 40, 0)
ratio = window.devicePixelRatio
w = trueW * ratio
h = trueH * ratio
if (trueW === 0 || trueH === 0) {
canvas.width = 0
canvas.height = 0
canvas.style.width = "0px"
canvas.style.height = "0px"
return
}
if (trueW !== pW || trueH !== pH || ratio !== prevRatio) {
canvas.width = w
canvas.height = h
canvas.style.width = trueW + "px"
canvas.style.height = trueH + "px"
if (prevRatio !== ratio) {
// Update graphic
redBalloon = svgRed()
// Set new ratio
prevRatio = ratio
}
}
}
window.onresize = updateSvg
updateSvg()
draw()


// The vector graphic (you may want to manually tweak the coordinates if they are slightly off (such as changing 25.240999999999997 to 25.241)
function svgRed() {
// Scale the hidden canvas
canvas2.width = Math.round(44 * ratio * scaleX)
canvas2.height = Math.round(65 * ratio * scaleY)
ctx2.scale(ratio * scaleX, ratio * scaleY)


// Draw the graphic
ctx2.save()
ctx2.beginPath()
ctx2.moveTo(0, 0)
ctx2.lineTo(44, 0)
ctx2.lineTo(44, 65)
ctx2.lineTo(0, 65)
ctx2.closePath()
ctx2.clip()
ctx2.strokeStyle = '#0000'
ctx2.lineCap = 'butt'
ctx2.lineJoin = 'miter'
ctx2.miterLimit = 4
ctx2.save()
ctx2.beginPath()
ctx2.moveTo(0, 0)
ctx2.lineTo(44, 0)
ctx2.lineTo(44, 65)
ctx2.lineTo(0, 65)
ctx2.closePath()
ctx2.clip()
ctx2.save()
ctx2.fillStyle = "#e02f2f"
ctx2.beginPath()
ctx2.moveTo(27, 65)
ctx2.lineTo(22.9, 61.9)
ctx2.lineTo(21.9, 61)
ctx2.lineTo(21.1, 61.6)
ctx2.lineTo(17, 65)
ctx2.lineTo(27, 65)
ctx2.closePath()
ctx2.moveTo(21.8, 61)
ctx2.lineTo(21.1, 60.5)
ctx2.bezierCurveTo(13.4, 54.2, 0, 41.5, 0, 28)
ctx2.bezierCurveTo(0, 9.3, 12.1, 0.4, 21.9, 0)
ctx2.bezierCurveTo(33.8, -0.5, 45.1, 10.6, 43.9, 28)
ctx2.bezierCurveTo(43, 40.8, 30.3, 53.6, 22.8, 60.2)
ctx2.lineTo(21.8, 61)
ctx2.fill()
ctx2.stroke()
ctx2.restore()
ctx2.save()
ctx2.fillStyle = "#f59595"
ctx2.beginPath()
ctx2.moveTo(18.5, 7)
ctx2.bezierCurveTo(15.3, 7, 5, 11.5, 5, 26.3)
ctx2.bezierCurveTo(5, 38, 16.9, 50.4, 19, 54)
ctx2.bezierCurveTo(19, 54, 9, 38, 9, 28)
ctx2.bezierCurveTo(9, 17.3, 15.3, 9.2, 18.5, 7)
ctx2.fill()
ctx2.stroke()
ctx2.restore()
ctx2.restore()
ctx2.restore()


// Save the results
var image = new Image()
image.src = canvas2.toDataURL()
return image
}
</script>
</body>


</html>

试试这个:

let svg = `<svg xmlns="http://www.w3.org/2000/svg" ...`;
let blob = new Blob([svg], {type: 'image/svg+xml'});
let url = URL.createObjectURL(blob);


const ctx = canvas.getContext('2d');
canvas.width = 900;
canvas.height = 1400;


const appLogo = new Image();
appLogo.onload = () => ctx.drawImage(appLogo, 54, 387, 792, 960);
appLogo.src = url;


// let image = document.createElement('img');
// image.src = url;
// image.addEventListener('load', () => URL.revokeObjectURL(url), {once: true});

注意: Blob 不是在 Node.js 文件中定义的,这是为在浏览器中运行而设计的代码,而不是在 Node 中。

更多信息 给你