我正在做一些D3.JS操作,要求我使用SVG路径而不是基元/形状(折线,recs等).
这个问题很通用,但我想知道是否可以将任何SVG原语转换为路径,或者使用D3或其他脚本/库.
作为参考,这里有一个链接,用于折线:https://gist.github.com/andytlr/9283541
我想为每一个原始人做这个.有任何想法吗?这可能吗?
您还可以使用Jarek Foksa 的路径数据 polyfill转换所有基元:
\n它的主要目的是将路径的d
属性解析为命令数组。
getPathData()
还可以从任何原语检索路径数据,SVGGeometryElement
例如<rect>
, <circle>
, <ellipse>
, <polygon>
。
const svgWrp = document.querySelector(\'.svgWrp\');\nconst svg = document.querySelector(\'svg\');\nconst primitives = svg.querySelectorAll(\'path, line, polyline, polygon, circle, rect\');\nconst svgMarkup = document.querySelector(\'#svgMarkup\');\nsvgMarkup.value = new XMLSerializer().serializeToString(svg);\n\nfunction convertPrimitives(svg, primitives) {\n primitives.forEach(function(primitive, i) {\n /**\n * get normalized path data: \n * all coordinates are absolute; \n * reduced set of commands: M, L, C, Z\n */\n let pathData = primitive.getPathData();\n\n //get all attributes\n let attributes = [...primitive.attributes];\n let path = document.createElementNS(\'http://www.w3.org/2000/svg\', \'path\');\n //exclude attributes not needed for paths\n let exclude = [\'x\', \'y\', \'x1\', \'y1\', \'x2\', \'y2\', \'cx\', \'cy\', \'r\', \'rx\', \'ry\', \'points\', \'height\',\n \'width\'\n ];\n setAttributes(path, attributes, exclude);\n // set d attribute from rounded pathData\n path.setPathData(roundPathData(pathData, 1));\n primitive.replaceWith(path);\n })\n // optional: output new svg markup\n let newSvgMarkup = new XMLSerializer().serializeToString(svg);\n svgMarkup.value = newSvgMarkup;\n}\n\nfunction roundPathData(pathData, decimals = 3) {\n pathData.forEach(function(com, c) {\n let values = com[\'values\'];\n values.forEach(function(val, v) {\n pathData[c][\'values\'][v] = +val.toFixed(decimals);\n })\n })\n return pathData;\n}\n\nfunction setAttributes(el, attributes, exclude = []) {\n attributes.forEach(function(att, a) {\n if (exclude.indexOf(att.nodeName) === -1) {\n el.setAttribute(att.nodeName, att.nodeValue);\n }\n })\n}
Run Code Online (Sandbox Code Playgroud)\r\n<script src="https://cdn.jsdelivr.net/npm/path-data-polyfill@1.0.3/path-data-polyfill.min.js"></script>\n\n<p><button type="button" onclick="convertPrimitives(svg, primitives)">Convert Primitives</button></p>\n<div class="svgWrp">\n <svg id="svg" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 150 30">\n <polygon id="polygon" fill="#ccc" stroke="green" points="9,22.4 4.1,14 9,5.5 18.8,5.5 23.7,14 18.8,22.4 " />\n <polyline id="polyline" fill="none" stroke="red" points="43,22.4 33.3,22.4 28.4,14 33.3,5.5 43,5.5 47.9,14 " />\n <rect id="rect" x="57.3" y="5.5" rx="2" ry="2" fill="none" stroke="orange" width="16.9" height="16.9" />\n <line id="line" fill="none" stroke="purple" x1="52.6" y1="22.4" x2="52.6" y2="5.5" />\n <circle class="circle" data-att="circle" id="circle" fill="none" stroke="magenta" cx="87.4" cy="14" r="8.5" />\n <path transform="scale(0.9) translate(110,5)" d="M 10 0 A 10 10 0 1 1 1.34 15 L 10 10 z" fill="red" class="segment segment-1 segment-class" id="segment-01" />\n </svg>\n</div>\n<h3>Svg markup</h3>\n<textarea name="svgMarkup" id="svgMarkup" style="width:100%; height:20em;"></textarea>
Run Code Online (Sandbox Code Playgroud)\r\ngetPathData()
还提供了标准化方法,将任何元素的几何形状转换为一组精简的绝对命令 - 仅使用:
\n M
, L
, C
, Z
。
二次Q
或T
命令以及
\n弧线A
和简写(如V
or H
)都被转换
element.getPathData({normalize: true});\n
Run Code Online (Sandbox Code Playgroud)\nelement.getPathData({normalize: true});\n
Run Code Online (Sandbox Code Playgroud)\r\nconst svgWrp = document.querySelector(\'.svgWrp\');\nconst svg = document.querySelector(\'svg\');\nconst primitives = svg.querySelectorAll(\'path, line, polyline, polygon, circle, rect\');\nconst svgMarkup = document.querySelector(\'#svgMarkup\');\nsvgMarkup.value = new XMLSerializer().serializeToString(svg);\n\nfunction convertPrimitives(svg, primitives) {\n primitives.forEach(function(primitive, i) {\n /**\n * get path data: \n */\n let pathData = primitive.getPathData({normalize:true});\n\n //get all attributes\n let attributes = [...primitive.attributes];\n let path = document.createElementNS(\'http://www.w3.org/2000/svg\', \'path\');\n //exclude attributes not needed for paths\n let exclude = [\'x\', \'y\', \'x1\', \'y1\', \'x2\', \'y2\', \'cx\', \'cy\', \'r\', \'rx\', \'ry\', \'points\', \'height\',\n \'width\'\n ];\n setAttributes(path, attributes, exclude);\n // set d attribute from rounded pathData\n path.setPathData(roundPathData(pathData, 1));\n primitive.replaceWith(path);\n })\n // optional: output new svg markup\n let newSvgMarkup = new XMLSerializer().serializeToString(svg);\n svgMarkup.value = newSvgMarkup;\n}\n\nfunction roundPathData(pathData, decimals = 3) {\n pathData.forEach(function(com, c) {\n let values = com[\'values\'];\n values.forEach(function(val, v) {\n pathData[c][\'values\'][v] = +val.toFixed(decimals);\n })\n })\n return pathData;\n}\n\nfunction setAttributes(el, attributes, exclude = []) {\n attributes.forEach(function(att, a) {\n if (exclude.indexOf(att.nodeName) === -1) {\n el.setAttribute(att.nodeName, att.nodeValue);\n }\n })\n}
Run Code Online (Sandbox Code Playgroud)\r\n上面的示例脚本还将保留所有属性class
,例如id
、fill
等。
但它会剥离特定于基元的属性,例如r
, cx
, 。rx
不幸的是,getPathData()
和setPathData()
方法仍然是svg 2 草案/提案\xe2\x80\x93 ,旨在替换已弃用的pathSegList()
方法。
\n希望我们能在不久的将来获得本机浏览器支持。
\n由于与更高级的 svg 库(如(snap.svg、d3 等))相比,这个 polyfill 仍然相当轻量(~12.5 KB 未压缩),因此它不会显着增加加载时间。
这是一个概念证明 \xe2\x80\x93 您可以根据非常基本的值计算 \xe2\x80\x93 转换 svg 原语 \xe2\x80\x93 ,而不需要高级框架/库 \xe2\x80\x93 受到这篇文章的启发:转换所有形状/基元到 SVG 的路径元素中。
\n但当我摆弄自己笨重的转换脚本时,我很快意识到存在一些挑战(Jarek Foksa 的规范化实现完美地解决了),例如:
\n相对即基于百分比的单位
\n<circle cx="50%" cy="50%" r="25%" /> \n
Run Code Online (Sandbox Code Playgroud)\n好的...我想我们需要根据 viewBox
属性定义的父 svg 边界来计算这些绝对坐标的相对值...也许根本没有可用的 viewBox ...或宽度/高度值。
或者类似rx
,的属性,将圆角边框ry
应用于元素\xe2\x80\x93 以便进行适当的转换,我们需要添加一些曲线命令,例如,或。<rect>
a
c
s
路径与基元
\n确实,元素可以通过三次或二次样条命令<path>
\xe2\x80\x93 甚至以更有效的方式绘制基元可以提供的任何形状,因为它的连接能力(结合多个形状)及其相关命令或简写命令。\n但它不支持相对单位\xe2\x80\x93,但是您需要转换的形状可能在很大程度上取决于相对尺寸(例如圆形仪表饼图等)
结论
编写自定义转换脚本并不太困难,但要注意一些棘手的细节。
<script src="https://cdn.jsdelivr.net/npm/path-data-polyfill@1.0.3/path-data-polyfill.min.js"></script>\n\n<p><button type="button" onclick="convertPrimitives(svg, primitives)">Convert Primitives</button></p>\n<div class="svgWrp">\n <svg id="svg" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 150 30">\n <polygon id="polygon" fill="#ccc" stroke="green" points="9,22.4 4.1,14 9,5.5 18.8,5.5 23.7,14 18.8,22.4 " />\n <polyline id="polyline" fill="none" stroke="red" points="43,22.4 33.3,22.4 28.4,14 33.3,5.5 43,5.5 47.9,14 " />\n <rect id="rect" x="57.3" y="5.5" rx="2" ry="2" fill="none" stroke="orange" width="16.9" height="16.9" />\n <line id="line" fill="none" stroke="purple" x1="52.6" y1="22.4" x2="52.6" y2="5.5" />\n <circle class="circle" data-att="circle" id="circle" fill="none" stroke="magenta" cx="87.4" cy="14" r="8.5" />\n <path transform="scale(0.9) translate(110,5)" d="M 10 0 A 10 10 0 1 1 1.34 15 L 10 10 z" fill="red" class="segment segment-1 segment-class" id="segment-01" />\n </svg>\n</div>\n<h3>Svg markup</h3>\n<textarea name="svgMarkup" id="svgMarkup" style="width:100%; height:20em;"></textarea>
Run Code Online (Sandbox Code Playgroud)\r\n<p><button type="button" onclick="getConvertedMarkup(svg, svgMarkup, 2)">Convert Primitives</button></p>\n<svg id="svg" xmlns="http://www.w3.org/2000/svg" data-width="150px" data-height="30px" viewBox="0 0 150 30">\n<polygon id="polygon" fill="#CCCCCC" stroke="#E3000F" points="7.9,22.8 3,14.3 7.9,5.8 17.6,5.8 22.5,14.3 17.6,22.8 " />\n<p