我需要将 SVG 的可见区域转换为静态图像以用于打印和类似目的。我从阅读中得知,该方法应该是首先使用 将 SVG 转换为画布canvg
,然后使用 将画布转换为图像canvas.toDataURL
。我能够完成这些基本要求,但是我的 viewbox 比我的 SVG 小得多,因此裁剪和缩放成为问题,这就是我目前遇到的问题。 我的想法是在转换为用于将未缩放的画布裁剪到 SVG 视图框定义的区域的图像之前插入额外的两个步骤。canvasContext.drawImage(...)
最后一步对我不起作用:我无法裁剪图像并保持当前比例。 然后我打算使用pica.resizeCanvas
以实现图像的高质量缩放(使其适合可打印页面)。
在进入代码之前,问题是:
- 总体方法是否合理?
canvasContext.drawImage(...)
图像最终缩放或裁剪不正确,我做错了什么?目标是不应用缩放,而是裁剪一些多余的空白区域。
步骤总结
- 复制
svg
到canvas
使用canvg
。这行得通。- 不应用缩放
- 偏移量用于将图像移动到左上角,为将来裁剪剩余的空白区域(右下区域)做准备。
- 获取新创建的
canvas
并将裁剪区域绘制到辅助canvas
使用canvasContext.drawImage
. 这失败了。画布尺寸错误但缩放正确(即没有)或画布尺寸正确但缩放错误(放大)。 - 用于
pica.resizeCanvas
以最小的质量损失应用缩放。 还没有真正尝试过。 - 用于
canvasContext.toDataURL(...)
将画布转换为png
. 这行得通。
代码
function MakeImage() {
//min-x, min-y, width and height
var viewBox = parseViewBox(); //defined below
var vbMinX = viewBox[0];
var vbMinY = viewBox[1];
var vbWidth = viewBox[2];
var vbHeight = viewBox[3];
var svgUnitRatio = getSvgUnitRatio(vbHeight); //defined below
//xMin,yMin,xMax,yMax
var boundingBox = getBounds(); //defined below
var bbXmin = boundingBox[0];
var bbYmin = boundingBox[1];
var bbXmax = boundingBox[2];
var bbYmax = boundingBox[3];
var offsetX = (vbMinX - bbXmin) * svgUnitRatio;
var offsetY = (vbMinY - bbYmin) * svgUnitRatio;
var adjustedWidth = (bbXmax - bbXmin) * svgUnitRatio;
var adjustedHeight = (bbYmax - bbYmin) * svgUnitRatio;
var options = {
ignoreDimensions: false, //allow it to resize the canvas based on the svg size
offsetX: offsetX,
offsetY: offsetY
};
//first we copy the svg to a canvas w/o applying any scaling
window.canvg("workspaceCanvas", $("#mysvg").parent().html(), options);
//now we crop according the svg viewbox
var canvas = document.getElementById("canvas");
var workspaceCanvas = document.getElementById("workspaceCanvas");
var context = canvas.getContext('2d');
context.drawImage(workspaceCanvas, 0, 0, adjustedWidth, adjustedHeight, 0, 0, adjustedWidth, adjustedHeight); //something is wrong here i guess???
//next we do a high quality scaling of the canvas
var pOptions = { //maybe this has problems but i won't kow until i get the previous step right
quality: 3,
alpha: true,
unsharpAmount: 50,
unsharpRadius: 0.5,
unsharpThreshold: 0
};
//holding off on trying this for now
window.pica.resizeCanvas(workspaceCanvas, canvas, pOptions, function(err) { /*this is a mandatory argument*/ });
var img = canvas.toDataURL("image/png,1");
//do stuff with image data
}
function getSvgUnitRatio(viewboxHeight) {
//shouldnt need to worry about width since the aspect ratio should be locked
var height = parseFloat(d3.select("#mysvg").attr("height"));
return height / viewboxHeight;
}
function getBounds() {
//xMin,yMin,xMax,yMax
var boundingBox = [];
var xMin = Number.MAX_SAFE_INTEGER;
var yMin = Number.MAX_SAFE_INTEGER;
var xMax = Number.MIN_SAFE_INTEGER;
var yMax = Number.MIN_SAFE_INTEGER;
window.svg.selectAll(".elementsICareAbout").nodes().forEach(function(d) {
var dx = parseFloat(d.getAttribute("x"));
var dy = parseFloat(d.getAttribute("y"));
var width = parseFloat(d.getAttribute("width"));
var height = parseFloat(d.getAttribute("height"));
if (dx + width > xMax) {
xMax = dx + width;
}
if (dx < xMin) {
xMin = dx;
}
if (dy + height > yMax) {
yMax = dy + height;
}
if (dy < yMin) {
yMin = dy;
}
});
var padding = 25; //add some fluff
//xMin,yMin,xMax,yMax
boundingBox = [
xMin - padding, yMin - padding, xMax + padding, yMax + padding
];
return boundingBox;
}
function parseViewBox() {
var str = d3.select("#mysvg").attr("viewBox");
var parts = str.split(" ");
var parsed = [];
parts.forEach(function(p) {
parsed.push(parseFloat(p));
});
return parsed;
}