【问题标题】:How to embed multiple static SVG in a dynamic SVG?如何在动态 SVG 中嵌入多个静态 SVG?
【发布时间】:2021-08-03 22:00:51
【问题描述】:

我有多个通过 Sketch 制作的 SVG。直到现在,我都亲自挑选了一个来签署我在 GitHub 上的项目的自述文件。但是我刚刚找到one of Wikipedia's dynamic SVG,像这样制作一个投资组合会很有趣。所以我想要一个像这个一样的动态 SVG,默认情况下显示一个静态 SVG 和其他,因为鼠标光标悬停在它上面。

通过对 Wikipedia 进行逆向工程,我确实成功地让它显示了我的。但是,这会腐蚀它们;它们看起来一点也不好看,可能是因为它们的复杂性以及与文档的其余部分缺乏“隔离”。起初,我怀疑使用 iframe 或 shadow DOM,但我是一名软件工程师,而不是一名 Web 开发人员,而且我在前端方面并不出色。然而,很明显,维基百科确实使用了影子 DOM。有关系吗?

我确实在网上搜索过,希望是正确的,但徒劳无功。我挖到的最接近的帮助是this StackOverflow page

那么你是如何做到这一点的呢?

【问题讨论】:

    标签: html svg dynamic static


    【解决方案1】:

    关于 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 放入 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 变换和对象/变量 piece.scaleFactor 来调整片段的大小。我将piece 作为我们之前解析到SVGImage 类中的单个SVG 发送。我发送给Move 的 X 和 Y 坐标是像素。

    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 路径本身的起始坐标。部分需要注意的是,您必须先将图像绘制到画布上,然后才能弄清楚偏移量是多少,因此您最终必须删除原始图像并将新图像添加到画布中,然后才能调用 @987654336 @ 或 transformPiece 并让它在你期望的地方结束。

    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 符号和你不关心的属性。我相信您可以根据您的需要大大简化它。大多数情况下,我只是从我的项目中复制并粘贴它,这样我就知道代码正在运行。

    【讨论】:

    • 或者在现代浏览器中使用现代 Web 组件 HTML:&lt;chessmeister-board fen="rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR"&gt;&lt;/chessmeister-board&gt; 并获得带有 one HTML 标签的完整交互式棋盘;见ChessMeister.github.io
    • @Danny'365CSI'Engelman,我的目标远远超出基本国际象棋。 chesssite.ericsgear.com/TestChessBoard.html 在页面底部查看我开发的 X2-FEN。您可以制作设计器板布局。 :-) 这就是我到目前为止所拥有的。我还没搞定。这只是一个早期的原型。
    猜你喜欢
    • 1970-01-01
    • 2016-08-31
    • 1970-01-01
    • 1970-01-01
    • 2017-04-12
    • 2021-06-03
    • 2011-12-23
    • 2011-07-23
    • 2016-04-02
    相关资源
    最近更新 更多