html - 如何在动态 SVG 中嵌入多个静态 SVG?
问题描述
我有多个通过 Sketch 制作的 SVG。直到现在,我都亲自挑选了一个来签署我在 GitHub 上的项目的自述文件。但我刚刚找到了维基百科的动态 SVG 之一,制作这样的作品集会很有趣。所以我想要一个像这个一样的动态 SVG 默认显示一个静态 SVG 和其他,因为鼠标光标悬停在它上面。
通过对维基百科的逆向工程,我确实成功地让它展示了我的。但是,这会腐蚀它们;它们看起来一点也不好看,可能是因为它们的复杂性以及与文档的其余部分缺乏“隔离”。起初,我怀疑使用 iframe 或 shadow DOM,但我是一名软件工程师,而不是一名 Web 开发人员,而且我的前端并不出色。然而,很明显,维基百科确实使用了影子 DOM。不过,这有关系吗?
我确实在网上搜索,希望是正确的,但徒劳无功。我挖掘的最接近的帮助是这个 StackOverflow 页面。
那么你如何实现这一目标呢?
解决方案
SVG 的特点是它只是 XML。您可以将 SVG 加载到 XML 解析器中并获取所需的部分,然后将它们添加到画布或以其他方式独立操作它们。
我只是为我自己当前的项目做了这个。该图像是一个简单的国际象棋套装,但我可以将整个国际象棋套装放在一个文件中,包括白色和黑色棋子,然后单独解析它们,这样我就可以调整它们的大小甚至更改它们的颜色。这个 JavaScript 可以使用一些重构,但它应该让您对需要做什么有一个很好的了解。
loadPieces("images/SimpleStaunton.svg");
loadPieces(fileName) // the file name is a relative path
{
var request = new XMLHttpRequest();
request.open("GET", fileName);
request.setRequestHeader("Content-Type", "image/svg+xml");
var me = this;
request.addEventListener("load", function(event) {
var response = event.target.responseText;
var doc = new DOMParser();
var xml = doc.parseFromString(response, "image/svg+xml");
me.parseChessSets(xml.children[0]);
});
request.send();
}
parseChessSets(xml)
{
this.baseSet = new ChessSet(xml);
}
为了使下一部分工作,我进入 InkScape 并命名每个单独的对象,这样我就可以确切地知道我从 XML 中提取了 SVG 的哪一部分。(我包括了一个朝左的骑士和一个朝右的骑士,这就是为什么有两个骑士的原因。)因为它们是 SVG,所以你可以随心所欲地给它们上色,所以我只是拉出路径。您可能想做的还不止这些。一旦您将 XML 转换为 .xml 文件object
,您将能够更轻松地了解如何做到这一点。
棘手的部分是whitePiece.getAttribute("d")
,或者更确切地说xml.children[0].getAttribute("d")
,是 SVG 的路径,这一点都不明显。
class ChessSet
{
white = new ChessPieces();
black = new ChessPieces("#fff", "#000");
pieceNames = ["king", "queen", "bishop", "knightl", "knightr", "rook", "pawn"];
constructor (xml)
{
for (var i = 0; i < this.pieceNames.length; i++)
{
var whitePiece = xml.children["white" + this.pieceNames[i]];
if (!whitePiece)
{
continue;
}
var blackPiece = xml.children["black" + this.pieceNames[i]] || whitePiece;
this.white[this.pieceNames[i]] = new SvgImage(whitePiece.getAttribute("d"), whitePiece.style);
this.black[this.pieceNames[i]] = new SvgImage(blackPiece.getAttribute("d"), blackPiece.style);
}
}
class ChessPieces
{
scalePercent = 100; // percentage to shrink king to 90% height of a square
strokeColor;
fillColor;
king;
queen;
bishop;
knightr;
knightl;
rook;
pawn;
constructor(stroke = "#000", fill = "#fff")
{
this.strokeColor = stroke;
this.fillColor = fill;
}
}
class SvgImage
{
transform = {left: 0, down: 0};
pathElement = null;
name = null;
scaleFactor = 1;
strokeWidth = 1;
constructor(path, style)
{
this.path = path;
this.style = style;
}
}
然后我可以绘制单独的路径:
var newElement = this.drawPath(set[this.baseSet.pieceNames[i]].name, set[this.baseSet.pieceNames[i]].path, 1, set.strokeColor, set.fillColor);
this.context.appendChild(newElement);
drawPath(id, textPath, strokeWidth = 1, strokeColor = "#000", fill = "none", opacity = 1)
{
var newpath = document.createElementNS(this.svgns, "path");
newpath.setAttributeNS(null, "id", id);
newpath.setAttributeNS(null, "d", textPath);
newpath.setAttributeNS(null, "stroke", strokeColor);
newpath.setAttributeNS(null, "stroke-width", strokeWidth);
newpath.setAttributeNS(null, "opacity", opacity);
newpath.setAttributeNS(null, "fill", fill);
return newpath;
}
变量this.context
是我正在使用的画布 DOM。
现在我可以使用 CSS 变换和 object/variable 来调整片段的大小piece.scaleFactor
。我将piece
作为我们之前解析的单个 SVG 发送到SVGImage
课程中。我发送到的 X 和 Y 坐标Move
是像素。
transformPiece(piece, left, down)
{
if (piece)
{
piece.transform.left = left;
piece.transform.down = down;
piece.pathElement.style.transform = "scale(" + piece.scaleFactor + ") translate(" + piece.transform.left + "px, " + piece.transform.down + "px)";
}
}
movePiece(piece, x, y)
{
if (piece)
{
var left = x / piece.scaleFactor;
var down = y / piece.scaleFactor;
this.transformPiece(piece, left, down);
}
}
需要注意的是,您需要适应原始图像中 SVG 的偏移量。我最终做的是重写 SVG 路径本身的起始坐标。部分需要注意的是,您必须先将图像绘制到画布上,然后才能弄清楚偏移量是多少,因此您最终必须删除原始图像并将新图像添加到画布中,然后才能调用movePiece
ortransformPiece
和让它在你期望的地方结束。
fixPath(piece, strokeColor, fillColor)
{
if (piece)
{
var box = piece.pathElement.getBoundingClientRect()
var fragments = piece.path.split(" ");
var coords = fragments[1].split(",");
var x = (coords[0] * 1) - box.x - window.scrollX;
var y = (coords[1] * 1) - box.y - window.scrollY;
fragments[1] = x + "," + y;
piece.path = fragments.join(" ");
this.context.removeChild(piece.pathElement);
piece.pathElement = this.drawPath(piece.name, piece.path, 1, strokeColor, fillColor);
this.context.appendChild(piece.pathElement);
}
}
我只花了一两个星期就弄清楚了这一切,以及所有错误的方法。;-)
抱歉,这有点复杂。我把它简化了一点,但我也留下了一堆object
你不关心的符号和属性。我相信您可以根据您的需要大大简化它。大多数情况下,我只是从我的项目中复制并粘贴它,这样我就知道代码正在工作。
推荐阅读
- javascript - 整合particle.js和bootstrap
- azure-data-factory - 如何根据接收器中的参数设置文件名?
- c++ - 在排序算法中计算执行时间
- bash - BASH中的命令查找如何更改它
- java - 使用命令 java -cp 的 java 运行问题
- c - 当符号似乎存在时,为什么 ld、nm、objdump 在此共享对象中报告“无符号”?
- kubernetes - 部署到新实例后,certmanager 不再工作
- python - 在不访问光盘的情况下使用 webbrowser 生成和打开 HTML 字符串
- python - 使用分类值时如何在散点图中保留轴的顺序?
- python - 在熊猫(> 1.0)中,如何将具有缺失值的浮点系列的dtype转换为整数?