参考:
SVG 教程 | 菜鸟教程
https://www.runoob.com/svg/svg-tutorial.html
本地环境:win10, visual studio 2022 community
注意:本文只给出思路和框架,对于具体的计算细节,考虑到日后会写入软件著作权和专利文书,因此不会展示。(虽然大概率要经过修改后才会放进文书,但是考虑到隐私保护,还是不写了。不过基本都是三角函数)
目录
- 效果
- 实现
- 思路
- 前端
- 下载svg图像的js代码
- 报错
- Error: Microsoft.JSInterop.JSException: Failed to execute 'btoa' on 'Window': The string to be encoded contains characters outside of the Latin1 range.
- 下载svg图像时,只显示一部分
- 改进
效果
导出效果:
实现
思路
在<svg>
标签内,首先绘制两个圆,形成圆环;然后再使用循环,每次绘制一组<path>
、<line>
和<text>
。需要着重考虑的是:
<path>
的数据属性(d)、<line>
中起终点坐标和<text>
坐标的计算(这部分是三角函数,不展开)- 导出按钮的
onclick
函数编写 - 实际的导出函数(js)
前端
<div>
<svg id="mySvg" width="100%" xmlns="http://www.w3.org/2000/svg" style="display:none">
<rect width="100%" height="100%" fill="white" />
<circle cx="@cx" cy="@cy" r="@outerRadius" fill="#ccc" stroke="#ccc" stroke-width="1" />
<circle cx="@cx" cy="@cy" r="@innerRadius" fill="#fff" stroke="#ccc" stroke-width="1" />
@foreach (DisplayPart p in v.Parts)
{
<g>
<path d="@p.PathData" opacity="0.5" fill="@p.Color" stroke="black" stroke-width="2" data-toggle="tooltip" title="@p.Name" />
<line x1="@p.Lcx" y1="@p.Lcy" x2="@p.Tcx" y2="@p.Tcy" stroke="black" stroke-width="1" />
<text x="@p.Tcx" y="@p.Tcy" fill="black">@p.Name</text>
</g>
}
</svg>
</div>
<div id="exportBtnDiv" style="display:none">
<button value="" class="btn btn-primary" id="ExportSvgBtn" @onclick="ExportSvg">
导出图谱
</button>
</div>
注意:
<rect width="100%" height="100%" fill="white" />
是为svg增加白色背景- 其中@开头的变量都是在code中定义的
v.Parts
是List<DisplayPart>
,DisplayPart
是自定义的一个类型
随后,在@code{}
块中,实现ExportSvgBtn
:
private async Task ExportSvg()
{
var svgElement = await JsRuntime.InvokeAsync<IJSObjectReference>("document.getElementById", "mySvg");
await module.InvokeVoidAsync("exportSvgToImage", svgElement, "png");
}
其中JsRuntime
是:
@inject IJSRuntime JsRuntime
而module在前面定义过,这里用它可以调用在别处定义的js函数。
参考:
Blazor入门-调用js+例子-CSDN博客
https://blog.csdn.net/pxy7896/article/details/138670348
跟这篇文章里一样设置即可。
下载svg图像的js代码
export function exportSvgToImage(svgElement, format) {
if (format == null)
return;
var svgXml = new XMLSerializer().serializeToString(svgElement);
var imageUrl = "data:image/svg+xml;base64," + btoa(svgXml);
var canvas = document.createElement("canvas");
var ctx = canvas.getContext("2d");
var img = new Image();
img.onload = function () {
canvas.width = img.width;
canvas.height = img.height;
ctx.drawImage(img, 0, 0);
var a = document.createElement("a");
a.download = "exported_image." + format;
a.href = canvas.toDataURL("image/" + format);
a.click();
};
img.src = imageUrl;
}
注意:函数前面写export
的原因已在上一节的参考文章里解释。
报错
Error: Microsoft.JSInterop.JSException: Failed to execute ‘btoa’ on ‘Window’: The string to be encoded contains characters outside of the Latin1 range.
这个错误通常发生是因为要编码的字符串中包含了Latin1范围之外的字符。解决方案是修改 exportSvgToImage 函数的这句:
var imageUrl = "data:image/svg+xml;base64," + btoa(svgXml);
改为:
var utf8 = unescape(encodeURIComponent(svgXml));
var imageUrl = "data:image/svg+xml;base64," + btoa(utf8);
下载svg图像时,只显示一部分
使用上面下载图像的js代码时,输出的图像可能只有一部分,如下图所示:
解决方案是将canvas的宽高设置为跟svg元素一致:
canvas.width = svgElement.clientWidth;
canvas.height = svgElement.clientHeight;
改进
未来会通过算法调整label的位置和line的线形,以避免label重叠。如果可以的话,会再写:)