SVG - 将所有形状/基元转换为<path>

eka*_*atz 5 svg d3.js

我正在做一些D3.JS操作,要求我使用SVG路径而不是基元/形状(折线,recs等).

这个问题很通用,但我想知道是否可以将任何SVG原语转换为路径,或者使用D3或其他脚本/库.

作为参考,这里有一个链接,用于折线:https://gist.github.com/andytlr/9283541

我想为每一个原始人做这个.有任何想法吗?这可能吗?

her*_*zel 3

JavaScript解决方案

\n

您还可以使用Jarek Foksa 的路径数据 polyfill转换所有基元:

\n

它的主要目的是将路径的d属性解析为命令数组。

\n

getPathData()还可以从任何原语检索路径数据,SVGGeometryElement例如<rect>, <circle>, <ellipse>, <polygon>

\n

示例 1:转换所有基元

\n

\r\n
\r\n
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\n
\r\n
\r\n

\n

getPathData()还提供了标准化方法,将任何元素的几何形状转换为一组精简的绝对命令 - 仅使用:
\n M, L, C, Z

\n

二次QT命令以及
\n弧线A和简写(如Vor H)都被转换

\n
element.getPathData({normalize: true});\n
Run Code Online (Sandbox Code Playgroud)\n

示例 2:转换所有基元(标准化)

\n

\r\n
\r\n
element.getPathData({normalize: true});\n
Run Code Online (Sandbox Code Playgroud)\r\n
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 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
\r\n
\r\n

\n

上面的示例脚本还将保留所有属性class,例如idfill等。

\n

但它会剥离特定于基元的属性,例如r, cx, 。rx

\n

我们需要这个 Polyfill 吗?

\n

不幸的是,getPathData()setPathData()方法仍然是svg 2 草案/提案\xe2\x80\x93 ,旨在替换已弃用的pathSegList()方法。
\n希望我们能在不久的将来获得本机浏览器支持。
\n由于与更高级的 svg 库(如(snap.svg、d3 等))相比,这个 polyfill 仍然相当轻量(~12.5 KB 未压缩),因此它不会显着增加加载时间。

\n

更新:独立脚本(无polyfill依赖)

\n

这是一个概念证明 \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 ...或宽度/高度值。

\n

或者类似rx,的属性,将圆角边框ry应用于元素\xe2\x80\x93 以便进行适当的转换,我们需要添加一些曲线命令,例如,或。<rect>acs

\n

路径与基元
\n确实,元素可以通过三次或二次样条命令<path>\xe2\x80\x93 甚至以更有效的方式绘制基元可以提供的任何形状,因为它的连接能力(结合多个形状)及其相关命令或简写命令。\n但它不支持相对单位\xe2\x80\x93,但是您需要转换的形状可能在很大程度上取决于相对尺寸(例如圆形仪表饼图等)

\n

结论
编写自定义转换脚本并不太困难,但要注意一些棘手的细节。

\n

\r\n
\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\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


Copyright Info

© Copyright 2013-2021 admin@qa.1r1g.com

如未特别说明,本网站的内容使用如下协议:
Creative Commons Atution-NonCommercial-ShareAlike 4.0 International license
.

用以下方式浏览
回到顶部