我需要将SVG的可见区域转换为静态图像以进行打印和类似用途。我从阅读中得知,该方法应该是先使用canvg
将SVG转换为画布,然后使用canvas.toDataURL
将画布转换为图像。我能够完成这些基本要求,但是我的viewbox比我的SVG小得多,因此裁剪和缩放成为问题,而这正是我目前磕磕绊绊的地方。 我的想法是在转换为使用canvasContext.drawImage(...)
的图像之前插入另外两个步骤,将未缩放的画布裁剪到由SVG视图框定义的区域。这最后一步对我不起作用:我无法设法裁剪图像并保持当前的比例。然后,我打算使用pica.resizeCanvas
来实现图像的高质量缩放(使其适合可打印页面)。将SVG转换为画布,同时排除SVG视图框外的区域
之前进入代码的问题是:
- 将是对所有进场声?
- 我在做什么错误
canvasContext.drawImage(...)
该图像结束了缩放或裁剪不正确?目标是不应用缩放,而是裁剪一些多余的空白区域。步骤- 复印
svg
的
概述以便
canvas
使用canvg
。 这工作。- 无缩放施加
- 偏移被用于将图像移动到左上角,准备剩余的白色空间(右下区域)的未来裁剪。
- 复印
- 获取新创建
canvas
保持和使用canvasContext.drawImage
绘制裁剪区域的二次canvas
。 这失败了。画布尺寸不正确,但缩放比例正确(即无)或画布尺寸正确,但缩放比例不正确(缩放)。 - 使用
pica.resizeCanvas
可应用缩放,质量损失最小。 还没有真正尝试过。 - 使用
canvasContext.toDataURL(...)
转换canvas到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;
}
你总是可以应用剪裁或clipPath到视框。 –
如果您想要提供全尺寸内联svg进行打印,那么您可以使用事件window.onbeforeprint,window.onafterprint和window.matchmedia(Chrome)。这提供了操纵svg以填充窗口的能力,然后在打印后重置它。 (这不需要画布)。我可以举一个例子,你想看到这种方法。 –
@FrancisHemsher如果你有一个方便的例子,我很乐意看到它。 –