生成JavaScript中的正多边形,其中底边始终在底部。

huangapple go评论94阅读模式
英文:

Generate regular polygon in Javascript with base side always at the bottom

问题

这里有一种方法可以生成具有 X 边的正多边形,但基准(第一条边)始终应该水平位于底部。我找到了一些生成器,例如 https://codepen.io/winkerVSbecks/pen/wrZQQm,但它不会在底部生成基准边,并且还有一些“奇怪”的填充在视图框内,我无法去掉它(多边形不会使用 svg 视图框的全宽度和高度,而是在边缘留有空白)。

英文:

Is here a way how to generate regular polygon with X sides, but the base (first) side should be always horizontal at the bottom. I've found any of generators, e.g. https://codepen.io/winkerVSbecks/pen/wrZQQm, but it does not generate the base side at the bottom and more, it has some "wierd" padding inside the viewbox I could not get rid of (the polygon does not use the full width and height of svg viewbox, but it has spaces at edges).

答案1

得分: 2

根据代码,多边形生成器似乎在右侧边上生成“平坦”的一侧。因此,在绘制多边形时,只需将所有角度顺时针旋转90度(也就是 0.5 * Math.PI 弧度):

  1. function polygon([cx, cy], sideCount, radius) {
  2. return pts(sideCount, radius)
  3. .map(({ r, theta }) => {
  4. // 注意:将 theta 更新为加上顺时针90度的旋转
  5. return [
  6. cx + r * Math.cos(theta + Math.PI * 0.5),
  7. cy + r * Math.sin(theta + Math.PI * 0.5)
  8. ];
  9. })
  10. .join(" ");
  11. }

查看 forked CodePen 这里:https://codepen.io/terrymun/pen/LYJQMda

英文:

Based on the code, the polygon generator seems to be generating the "flat" side on the right edge. So it is just a matter of rotating all angles by 90deg clockwise (aka 0.5 * Math.PI radians) when drawing the polygon:

  1. function polygon([cx, cy], sideCount, radius) {
  2. return pts(sideCount, radius)
  3. .map(({ r, theta }) => {
  4. // NOTE: Update theta to add 90deg clockwise rotation
  5. return [
  6. cx + r * Math.cos(theta + Math.PI * 0.5),
  7. cy + r * Math.sin(theta + Math.PI * 0.5)
  8. ];
  9. })
  10. .join(" ");
  11. }

See forked CodePen here: https://codepen.io/terrymun/pen/LYJQMda

答案2

得分: 2

你可以将所有的代码转换为本机JavaScript Web组件(JSWC):

  1. <svg-polygon sides="8" fill="red"></svg-polygon>
  2. <svg-polygon sides="9" fill="yellow"></svg-polygon>
  3. <svg-polygon sides="10" fill="blue"></svg-polygon>

生成JavaScript中的正多边形,其中底边始终在底部。

  1. customElements.define("svg-polygon", class extends HTMLElement {
  2. connectedCallback() {
  3. var radians = (deg) => (Math.PI * deg) / 180;
  4. var sides = +this.getAttribute("sides") || 8;
  5. var radius = +this.getAttribute("radius") || 200;
  6. var vb = 2*radius + (this.getAttribute("padding") || 50);
  7. var points = Array(sides).fill(radians(90-((180-(360/sides))/2)))
  8. .map((offset,idx) => [
  9. vb/2 + radius*Math.cos(offset+radians((360/sides)*idx) + Math.PI/2),
  10. vb/2 + radius*Math.sin(offset+radians((360/sides)*idx) + Math.PI/2)
  11. ]);
  12. this.innerHTML = `<svg viewBox="0 0 ${vb} ${vb}">` +
  13. `<polygon points="${points.join(" ")}" fill="${this.getAttribute("fill")}"/>` +
  14. `</svg>`;
  15. }
  16. })
  1. svg { width:180px; background:pink }
  1. <svg-polygon sides="8" fill="red"></svg-polygon>
  2. <svg-polygon sides="9" fill="yellow"></svg-polygon>
  3. <svg-polygon sides="10" fill="blue"></svg-polygon>
英文:

You can reduce all that code to a native JavaScript Web Component (JSWC):

  1. &lt;svg-polygon sides=&quot;8&quot; fill=&quot;red&quot;&gt;&lt;/svg-polygon&gt;
  2. &lt;svg-polygon sides=&quot;9&quot; fill=&quot;yellow&quot;&gt;&lt;/svg-polygon&gt;
  3. &lt;svg-polygon sides=&quot;10&quot; fill=&quot;blue&quot;&gt;&lt;/svg-polygon&gt;

生成JavaScript中的正多边形,其中底边始终在底部。

<!-- begin snippet: js hide: false console: true babel: false -->

<!-- language: lang-js -->

  1. customElements.define(&quot;svg-polygon&quot;, class extends HTMLElement {
  2. connectedCallback() {
  3. var radians = (deg) =&gt; (Math.PI * deg) / 180;
  4. var sides = +this.getAttribute(&quot;sides&quot;) || 8;
  5. var radius = +this.getAttribute(&quot;radius&quot;) || 200;
  6. var vb = 2*radius + (this.getAttribute(&quot;padding&quot;) || 50);
  7. var points = Array(sides).fill(radians(90-((180-(360/sides))/2)))
  8. .map((offset,idx) =&gt; [
  9. vb/2 + radius*Math.cos(offset+radians((360/sides)*idx) + Math.PI/2),
  10. vb/2 + radius*Math.sin(offset+radians((360/sides)*idx) + Math.PI/2)
  11. ]);
  12. this.innerHTML = `&lt;svg viewBox=&quot;0 0 ${vb} ${vb}&quot;&gt;` +
  13. `&lt;polygon points=&quot;${points.join(&quot; &quot;)}&quot; fill=&quot;${this.getAttribute(&quot;fill&quot;)}&quot;/&gt;` +
  14. `&lt;/svg&gt;`;
  15. }
  16. })

<!-- language: lang-css -->

  1. svg { width:180px; background:pink }

<!-- language: lang-html -->

  1. &lt;svg-polygon sides=&quot;8&quot; fill=&quot;red&quot;&gt;&lt;/svg-polygon&gt;
  2. &lt;svg-polygon sides=&quot;9&quot; fill=&quot;yellow&quot;&gt;&lt;/svg-polygon&gt;
  3. &lt;svg-polygon sides=&quot;10&quot; fill=&quot;blue&quot;&gt;&lt;/svg-polygon&gt;

<!-- end snippet -->

答案3

得分: 1

Varun Vachhar的codepen "SVG多边形生成器"已经包含了一个用于调整起始角度的参数。要更改offsetDeg(在pts(sideCount, radius)中)的值,可以这样做:

  1. let offsetDeg = (180 - angle) / 2;

生成器根据当前半径值计算多边形顶点。因此,所有顶点都位于一个圆上。但viewBox不会自动根据渲染的多边形调整宽度和高度。

要实现裁剪多边形SVG,可以采取以下两种方法:

  • 在渲染多边形后通过getBBox()重新计算viewBox
  • 或者通过查找x和y的极值来重新计算多边形坐标以找到适当的偏移量

示例1:通过调整viewBox来裁剪

  1. // 在渲染多边形后,通过以下代码调整viewBox
  2. let bb = polygonEl.getBBox();
  3. svgEl.setAttribute('viewBox', `${bb.x} ${bb.y} ${bb.width} ${bb.height}`);

示例2:通过重新计算多边形坐标来裁剪

  1. // 计算多边形坐标后,通过以下代码找到x和y的极值
  2. let xVals = polyPoints.map(pt => pt[0]);
  3. let yVals = polyPoints.map(pt => pt[1]);
  4. let left = Math.min(...xVals);
  5. let right = Math.max(...xVals);
  6. let top = Math.min(...yVals);
  7. let bottom = Math.max(...yVals);
  8. let width = right - left;
  9. let height = bottom - top;

此外,上述示例还将多边形点转换为路径数据字符串。通常,SVG <path> 元素提供了更简洁的表示法,因为它们还支持相对命令并可以组合成复合路径。要将<polygon>元素轻松转换为等效的<path>,只需在points属性前添加"M",并追加"z"命令字母:

  1. <polygon points="244.949 386.37 141.421 386.37 51.764 334.607 0 244.949 0 141.421 51.764 51.764 141.421 0 244.949 0 334.607 51.764 386.37 141.421 386.37 244.949 334.607 334.607"/>

等同于:

  1. <path d="M 244.949 386.37 141.421 386.37 51.764 334.607 0 244.949 0 141.421 51.764 51.764 141.421 0 244.949 0 334.607 51.764 386.37 141.421 386.37 244.949 334.607 334.607 z"/>

偏移值通过循环遍历点数组来计算,如下所示:

  1. // 找到x和y的极值以裁剪和调整多边形和viewBox
  2. let xVals = polyPoints.map(pt => pt[0]);
  3. let yVals = polyPoints.map(pt => pt[1]);
  4. let left = Math.min(...xVals);
  5. let right = Math.max(...xVals);
  6. let top = Math.min(...yVals);
  7. let bottom = Math.max(...yVals);
  8. let width = right - left;
  9. let height = bottom - top;
英文:

Varun Vachhar's codepen "SVG Polygon Generator"
already includes a parameter to adjust the starting angle.
Change the offsetDeg (in pts(sideCount, radius)) value like so:

let offsetDeg = (180 - angle) / 2;

The generator calculates polygon vertices according to the current radius value. So all vertices will be on a circle. But the viewBox won't automatically adjust width and height according to the rendered polygon.

To achieve a cropped polygon svg you can either:

  • recalculate the viewBox via getBBox() after rendering the polygon
  • or recalculate the polygon coordinates by finding x and y extrema to find appropriate offsets

Example 1: crop via viewBox adjustment

<!-- begin snippet: js hide: true console: true babel: false -->

<!-- language: lang-js -->

  1. const sideCountEl = document.querySelector(&#39;#js-side-count&#39;);
  2. const radiusEl = document.querySelector(&#39;#js-radius&#39;);
  3. const cxEl = document.querySelector(&#39;#js-cx&#39;);
  4. const cyEl = document.querySelector(&#39;#js-cy&#39;);
  5. const generateEl = document.querySelector(&#39;#js-generate&#39;);
  6. const polygonEl = document.querySelector(&#39;#js-polygon&#39;);
  7. const resultEl = document.querySelector(&#39;#js-result&#39;);
  8. const svgEl = document.querySelector(&#39;#jsSvg&#39;);
  9. function pts(sideCount, radius) {
  10. const angle = 360 / sideCount;
  11. const vertexIndices = range(sideCount);
  12. let offsetDeg = (180 - angle) / 2;
  13. const offset = degreesToRadians(offsetDeg);
  14. return vertexIndices.map(index =&gt; {
  15. return {
  16. theta: offset + degreesToRadians(angle * index),
  17. r: radius
  18. };
  19. });
  20. }
  21. function range(count) {
  22. return Array.from(Array(count).keys());
  23. }
  24. function degreesToRadians(angleInDegrees) {
  25. return Math.PI * angleInDegrees / 180;
  26. }
  27. function polygon([cx, cy], sideCount, radius) {
  28. return pts(sideCount, radius).
  29. map(({
  30. r,
  31. theta
  32. }) =&gt; [
  33. cx + r * Math.cos(theta),
  34. cy + r * Math.sin(theta)
  35. ]);
  36. }
  37. function generatePolygon() {
  38. const sideCount = +sideCountEl.value;
  39. const radius = +radiusEl.value;
  40. const s = 2 * radius;
  41. let polyPoints = polygon(
    展开收缩
    , sideCount, radius);
  42. polygonEl.setAttribute(&#39;points&#39;, polyPoints.flat().join(&#39; &#39;));
  43. // crop by - adjust viewBox to current bbox
  44. let bb = polygonEl.getBBox();
  45. svgEl.setAttribute(&#39;viewBox&#39;, `${bb.x} ${bb.y} ${bb.width} ${bb.height}`);
  46. // show output
  47. resultEl.value = new XMLSerializer().serializeToString(jsSvg);
  48. }
  49. window.onload = generatePolygon;
  50. // Listen to changes in &lt;input /&gt;
  51. let inputs = document.querySelectorAll(&#39;input&#39;);
  52. inputs.forEach(input =&gt; {
  53. input.addEventListener(&#39;input&#39;, e =&gt; generatePolygon());
  54. });
  55. // convert to relative path data
  56. function polyPointsToPathRelative(polyPoints, precision) {
  57. let pointsRel = [];
  58. let offXrel = 0;
  59. let offYrel = 0;
  60. polyPoints.forEach(pt =&gt; {
  61. let [x, y] = [pt[0] - offXrel, pt[1] - offYrel];
  62. pointsRel.push(x, y)
  63. offXrel += x;
  64. offYrel += y;
  65. });
  66. // round
  67. pointsRel = pointsRel.map(val =&gt; {
  68. return +val.toFixed(precision)
  69. });
  70. let M = pointsRel.splice(0, 2);
  71. let pathData = &#39;M&#39; + M.join(&#39; &#39;) + &#39;l&#39; + pointsRel.join(&#39; &#39;) + &#39;z&#39;;
  72. return pathData;
  73. }

<!-- language: lang-css -->

  1. body {
  2. margin: 0;
  3. font-family: sans-serif;
  4. }
  5. input,
  6. textarea {
  7. font-family: inherit;
  8. font-size: 100%;
  9. line-height: 1.15;
  10. margin: 0;
  11. }
  12. body,
  13. div,
  14. fieldset,
  15. form,
  16. input[type=number],
  17. legend,
  18. main,
  19. textarea {
  20. box-sizing: border-box;
  21. }
  22. .ba {
  23. border-style: solid;
  24. border-width: 1px;
  25. }
  26. .bb {
  27. border-bottom-style: solid;
  28. border-bottom-width: 1px;
  29. }
  30. .b--dark-gray {
  31. border-color: #333;
  32. }
  33. .b--transparent {
  34. border-color: transparent;
  35. }
  36. .bw2 {
  37. border-width: .25rem;
  38. }
  39. .db {
  40. display: block;
  41. }
  42. .flex {
  43. display: -webkit-box;
  44. display: -ms-flexbox;
  45. display: flex;
  46. }
  47. .min-vh-100 {
  48. min-height: 100vh;
  49. }
  50. .tracked {
  51. letter-spacing: .1em;
  52. }
  53. .mw7 {
  54. max-width: 48rem;
  55. }
  56. .w-100 {
  57. width: 100%;
  58. }
  59. .pa0 {
  60. padding: 0;
  61. }
  62. .pa2 {
  63. padding: .5rem;
  64. }
  65. .pa3 {
  66. padding: 1rem;
  67. }
  68. .pa4 {
  69. padding: 2rem;
  70. }
  71. .pv2 {
  72. padding-top: .5rem;
  73. padding-bottom: .5rem;
  74. }
  75. .ph0 {
  76. padding-left: 0;
  77. padding-right: 0;
  78. }
  79. .mb0 {
  80. margin-bottom: 0;
  81. }
  82. .mb2 {
  83. margin-bottom: .5rem;
  84. }
  85. .mb4 {
  86. margin-bottom: 2rem;
  87. }
  88. .mt5 {
  89. margin-top: 4rem;
  90. }
  91. .mv4 {
  92. margin-top: 2rem;
  93. margin-bottom: 2rem;
  94. }
  95. .mh0 {
  96. margin-left: 0;
  97. margin-right: 0;
  98. }
  99. .ttu {
  100. text-transform: uppercase;
  101. }
  102. .f5 {
  103. font-size: 1rem;
  104. }
  105. .measure-wide {
  106. max-width: 34em;
  107. }
  108. .center {
  109. margin-right: auto;
  110. margin-left: auto;
  111. }
  112. .flex-row-ns {
  113. -webkit-box-orient: horizontal;
  114. -webkit-box-direction: normal;
  115. -ms-flex-direction: row;
  116. flex-direction: row;
  117. }
  118. .mr4-ns {
  119. margin-right: 2rem;
  120. }
  121. .mb0-ns {
  122. margin-bottom: 0;
  123. }
  124. legend,
  125. label {
  126. text-transform: uppercase;
  127. font-weight: 700;
  128. }
  129. pre {
  130. overflow-wrap: break-word;
  131. }
  132. svg {
  133. overflow: visible !important;
  134. border: 1px solid #ccc;
  135. }
  136. textarea {
  137. display: block;
  138. width: 100%;
  139. min-height: 20em;
  140. white-space: pre-wrap !important;
  141. white-space: pre-line !important;
  142. }

<!-- language: lang-html -->

  1. &lt;main class=&quot;pa4 dark-gray mw7 center sans-serif min-vh-100 flex flex-column flex-row-ns &quot;&gt;
  2. &lt;form class=&quot;measure-wide mr4-ns mb4 mb0-ns f5&quot;&gt;
  3. &lt;fieldset class=&quot;ba b--transparent pa0 mh0&quot;&gt;
  4. &lt;legend class=&quot;f5 b ph0 mh0 ttu tracked bb bw2 pv2 db w-100&quot;&gt;
  5. Polygon Generator
  6. &lt;/legend&gt;
  7. &lt;div class=&quot;mt5 mb4&quot;&gt;
  8. &lt;label class=&quot;f5 ttu tracked db b mb2&quot; for=&quot;js-side-count&quot;&gt;
  9. Number of Sides
  10. &lt;/label&gt;
  11. &lt;input class=&quot;b pa2 input-reset ba bw2 bg-white b--dark-gray w-100&quot; type=&quot;number&quot; id=&quot;js-side-count&quot; min=&quot;3&quot; value=&quot;8&quot;&gt;
  12. &lt;/div&gt;
  13. &lt;div class=&quot;mv4&quot;&gt;
  14. &lt;label class=&quot;f5 ttu tracked db b mb2&quot; for=&quot;js-radius&quot;&gt;
  15. Radius
  16. &lt;/label&gt;
  17. &lt;input class=&quot;b pa2 input-reset ba bw2 bg-white b--dark-gray w-100&quot; type=&quot;number&quot; id=&quot;js-radius&quot; value=&quot;200&quot;&gt;
  18. &lt;/div&gt;
  19. &lt;/fieldset&gt;
  20. &lt;/form&gt;
  21. &lt;div class=&quot;flex-auto flex items-center justify-center w-100&quot;&gt;
  22. &lt;div class=&quot;w-100&quot;&gt;
  23. &lt;svg class=&quot;w-100 white debug-grid mb4 db&quot; viewBox=&quot;0 0 800 800&quot; stroke=&quot;#111&quot; fill=&quot;none&quot; stroke-width=&quot;4&quot; id=&quot;jsSvg&quot;&gt;
  24. &lt;polygon id=&quot;js-polygon&quot; points=&quot;&quot; /&gt;
  25. &lt;/svg&gt;
  26. &lt;textarea id=&quot;js-result&quot; class=&quot;code pa3 bg-near-white mb0 overflow-scroll&quot;&gt;&lt;/textarea&gt;
  27. &lt;/div&gt;
  28. &lt;/div&gt;
  29. &lt;/main&gt;

<!-- end snippet -->

Example 2: crop by recalculating polygon coordinates

<!-- begin snippet: js hide: true console: true babel: false -->

<!-- language: lang-js -->

  1. const sideCountEl = document.querySelector(&#39;#js-side-count&#39;);
  2. const radiusEl = document.querySelector(&#39;#js-radius&#39;);
  3. const generateEl = document.querySelector(&#39;#js-generate&#39;);
  4. const polygonEl = document.querySelector(&#39;#js-polygon&#39;);
  5. const resultEl = document.querySelector(&#39;#js-result&#39;);
  6. const svgEl = document.querySelector(&#39;#jsSvg&#39;);
  7. function pts(sideCount, radius) {
  8. const angle = 360 / sideCount;
  9. const vertexIndices = range(sideCount);
  10. let offsetDeg = (180 - angle) / 2;
  11. const offset = degreesToRadians(offsetDeg);
  12. return vertexIndices.map(index =&gt; {
  13. return {
  14. theta: offset + degreesToRadians(angle * index),
  15. r: radius
  16. };
  17. });
  18. }
  19. function range(count) {
  20. return Array.from(Array(count).keys());
  21. }
  22. function degreesToRadians(angleInDegrees) {
  23. return Math.PI * angleInDegrees / 180;
  24. }
  25. function polygon([cx, cy], sideCount, radius) {
  26. return pts(sideCount, radius).
  27. map(({
  28. r,
  29. theta
  30. }) =&gt; [
  31. cx + r * Math.cos(theta),
  32. cy + r * Math.sin(theta)
  33. ]);
  34. }
  35. function generatePolygon() {
  36. const sideCount = +sideCountEl.value;
  37. const radius = +radiusEl.value;
  38. const s = 2 * radius;
  39. // calculate polygont points
  40. let polyPoints = polygon(
    展开收缩
    , sideCount, radius);
  41. /**
  42. * find x and y extrema
  43. * to crop and pan polygon and viewBox
  44. */
  45. let xVals = polyPoints.map(pt =&gt; {
  46. return pt[0]
  47. });
  48. let yVals = polyPoints.map(pt =&gt; {
  49. return pt[1]
  50. });
  51. let left = Math.min(...xVals);
  52. let right = Math.max(...xVals);
  53. let top = Math.min(...yVals);
  54. let bottom = Math.max(...yVals);
  55. let width = right - left;
  56. let height = bottom - top;
  57. // round coordinates
  58. let precision = +inputPrecision.value;
  59. // calculate cropped polygon
  60. polyPoints = polyPoints.map(pt =&gt; {
  61. return [+(pt[0] - left).toFixed(precision), +(pt[1] - top).toFixed(precision)];
  62. });
  63. // create relative path
  64. let pathData = polyPointsToPathRelative(polyPoints, precision)
  65. // shift points - adjust viewBox
  66. svgEl.setAttribute(&#39;viewBox&#39;, `0 0 ${+width.toFixed(precision)} ${+height.toFixed(precision)}`);
  67. polygonEl.setAttribute(&#39;d&#39;, pathData);
  68. let output = new XMLSerializer().serializeToString(jsSvg).
  69. replace(/\s{2,}/g, &quot; &quot;).
  70. replaceAll(&#39;&gt; &lt;&#39;, &#39;&gt;&lt;&#39;).
  71. replaceAll(&#39;&lt;&#39;, &#39;\n&lt;&#39;);
  72. //console.log(output);
  73. resultEl.value = output;
  74. }
  75. window.onload = generatePolygon;
  76. // Listen to changes in &lt;input /&gt;
  77. let inputs = document.querySelectorAll(&#39;input&#39;);
  78. inputs.forEach(input =&gt; {
  79. input.addEventListener(&#39;input&#39;, e =&gt; generatePolygon());
  80. });
  81. // convert to relative path data
  82. function polyPointsToPathRelative(polyPoints, precision) {
  83. let pointsRel = [];
  84. let offXrel = 0;
  85. let offYrel = 0;
  86. polyPoints.forEach(pt =&gt; {
  87. let [x, y] = [pt[0] - offXrel, pt[1] - offYrel];
  88. pointsRel.push(x, y)
  89. offXrel += x;
  90. offYrel += y;
  91. });
  92. // round
  93. pointsRel = pointsRel.map(val =&gt; {
  94. return +val.toFixed(precision)
  95. });
  96. let M = pointsRel.splice(0, 2);
  97. let pathData = &#39;M&#39; + M.join(&#39; &#39;) + &#39;l&#39; + pointsRel.join(&#39; &#39;) + &#39;z&#39;;
  98. return pathData;
  99. }

<!-- language: lang-css -->

  1. body {
  2. margin: 0;
  3. font-family: sans-serif;
  4. }
  5. input,
  6. textarea {
  7. font-family: inherit;
  8. font-size: 100%;
  9. line-height: 1.15;
  10. margin: 0;
  11. }
  12. body,
  13. div,
  14. fieldset,
  15. form,
  16. input[type=number],
  17. legend,
  18. main,
  19. textarea {
  20. box-sizing: border-box;
  21. }
  22. .ba {
  23. border-style: solid;
  24. border-width: 1px;
  25. }
  26. .bb {
  27. border-bottom-style: solid;
  28. border-bottom-width: 1px;
  29. }
  30. .b--dark-gray {
  31. border-color: #333;
  32. }
  33. .b--transparent {
  34. border-color: transparent;
  35. }
  36. .bw2 {
  37. border-width: .25rem;
  38. }
  39. .db {
  40. display: block;
  41. }
  42. .flex {
  43. display: -webkit-box;
  44. display: -ms-flexbox;
  45. display: flex;
  46. }
  47. .min-vh-100 {
  48. min-height: 100vh;
  49. }
  50. .tracked {
  51. letter-spacing: .1em;
  52. }
  53. .mw7 {
  54. max-width: 48rem;
  55. }
  56. .w-100 {
  57. width: 100%;
  58. }
  59. .pa0 {
  60. padding: 0;
  61. }
  62. .pa2 {
  63. padding: .5rem;
  64. }
  65. .pa3 {
  66. padding: 1rem;
  67. }
  68. .pa4 {
  69. padding: 2rem;
  70. }
  71. .pv2 {
  72. padding-top: .5rem;
  73. padding-bottom: .5rem;
  74. }
  75. .ph0 {
  76. padding-left: 0;
  77. padding-right: 0;
  78. }
  79. .mb0 {
  80. margin-bottom: 0;
  81. }
  82. .mb2 {
  83. margin-bottom: .5rem;
  84. }
  85. .mb4 {
  86. margin-bottom: 2rem;
  87. }
  88. .mt5 {
  89. margin-top: 4rem;
  90. }
  91. .mv4 {
  92. margin-top: 2rem;
  93. margin-bottom: 2rem;
  94. }
  95. .mh0 {
  96. margin-left: 0;
  97. margin-right: 0;
  98. }
  99. .ttu {
  100. text-transform: uppercase;
  101. }
  102. .f5 {
  103. font-size: 1rem;
  104. }
  105. .measure-wide {
  106. max-width: 34em;
  107. }
  108. .center {
  109. margin-right: auto;
  110. margin-left: auto;
  111. }
  112. .flex-row-ns {
  113. -webkit-box-orient: horizontal;
  114. -webkit-box-direction: normal;
  115. -ms-flex-direction: row;
  116. flex-direction: row;
  117. }
  118. .mr4-ns {
  119. margin-right: 2rem;
  120. }
  121. .mb0-ns {
  122. margin-bottom: 0;
  123. }
  124. legend,
  125. label {
  126. text-transform: uppercase;
  127. font-weight: 700;
  128. }
  129. pre {
  130. overflow-wrap: break-word;
  131. }
  132. svg {
  133. overflow: visible !important;
  134. border: 1px solid #ccc;
  135. }
  136. textarea {
  137. display: block;
  138. width: 100%;
  139. min-height: 20em;
  140. white-space: pre-wrap !important;
  141. white-space: pre-line !important;
  142. }

<!-- language: lang-html -->

  1. &lt;main class=&quot;pa4 dark-gray mw7 center sans-serif min-vh-100 flex flex-column flex-row-ns &quot;&gt;
  2. &lt;form class=&quot;measure-wide mr4-ns mb4 mb0-ns f5&quot;&gt;
  3. &lt;fieldset class=&quot;ba b--transparent pa0 mh0&quot;&gt;
  4. &lt;legend class=&quot;f5 b ph0 mh0 ttu tracked bb bw2 pv2 db w-100&quot;&gt;
  5. Polygon Generator
  6. &lt;/legend&gt;
  7. &lt;div class=&quot;mt5 mb4&quot;&gt;
  8. &lt;label class=&quot;f5 ttu tracked db b mb2&quot; for=&quot;js-side-count&quot;&gt;
  9. Number of Sides
  10. &lt;/label&gt;
  11. &lt;input class=&quot;b pa2 input-reset ba bw2 bg-white b--dark-gray w-100&quot; type=&quot;number&quot; id=&quot;js-side-count&quot; min=&quot;3&quot; value=&quot;8&quot;&gt;
  12. &lt;/div&gt;
  13. &lt;div class=&quot;mv4&quot;&gt;
  14. &lt;label class=&quot;f5 ttu tracked db b mb2&quot; for=&quot;js-radius&quot;&gt;
  15. Radius
  16. &lt;/label&gt;
  17. &lt;input class=&quot;b pa2 input-reset ba bw2 bg-white b--dark-gray w-100&quot; type=&quot;number&quot; id=&quot;js-radius&quot; value=&quot;200&quot;&gt;
  18. &lt;/div&gt;
  19. &lt;div class=&quot;mv4&quot;&gt;
  20. &lt;label class=&quot;f5 ttu tracked db b mb2&quot; for=&quot;js-radius&quot;&gt;
  21. precision
  22. &lt;/label&gt;
  23. &lt;input class=&quot;b pa2 input-reset ba bw2 bg-white b--dark-gray w-100&quot; type=&quot;number&quot; id=&quot;inputPrecision&quot; value=&quot;2&quot;&gt;
  24. &lt;/div&gt;
  25. &lt;/fieldset&gt;
  26. &lt;/form&gt;
  27. &lt;div class=&quot;flex-auto flex items-center justify-center w-100&quot;&gt;
  28. &lt;div class=&quot;w-100&quot;&gt;
  29. &lt;svg viewBox=&quot;0 0 800 800&quot; id=&quot;jsSvg&quot;&gt;
  30. &lt;path id=&quot;js-polygon&quot; d=&quot;&quot; /&gt;
  31. &lt;/svg&gt;
  32. &lt;textarea id=&quot;js-result&quot; class=&quot;code pa3 bg-near-white mb0 overflow-scroll&quot;&gt;&lt;/textarea&gt;
  33. &lt;/div&gt;
  34. &lt;/div&gt;
  35. &lt;/main&gt;

<!-- end snippet -->

The above example also converts polygon points to a path data string.

Usually, svg &lt;path&gt; elements offer a more concise notation – as they also support relative commands and can also be combined as compound paths.

You can easily convert a &lt;polygon&gt; element to a &lt;path&gt; equivalent by simply prepending a "M" and appending a "z" command letter to the points attribute.

  1. &lt;polygon points=&quot;244.949 386.37 141.421 386.37 51.764 334.607 0 244.949 0 141.421 51.764 51.764 141.421 0 244.949 0 334.607 51.764 386.37 141.421 386.37 244.949 334.607 334.607&quot;/&gt;

equals

  1. &lt;path d=&quot;M 244.949 386.37 141.421 386.37 51.764 334.607 0 244.949 0 141.421 51.764 51.764 141.421 0 244.949 0 334.607 51.764 386.37 141.421 386.37 244.949 334.607 334.607 z&quot;/&gt;

The offset values are calculated by looping through the point array like this:

  1. /**
  2. * find x and y extrema
  3. * to crop and pan polygon and viewBox
  4. */
  5. let xVals = polyPoints.map(pt =&gt; {
  6. return pt[0]
  7. });
  8. let yVals = polyPoints.map(pt =&gt; {
  9. return pt[1]
  10. });
  11. let left = Math.min(...xVals);
  12. let right = Math.max(...xVals);
  13. let top = Math.min(...yVals);
  14. let bottom = Math.max(...yVals);
  15. let width = right - left;
  16. let height = bottom - top;
  17. // round coordinates
  18. let precision = +inputPrecision.value;
  19. // calculate cropped polygon
  20. polyPoints = polyPoints.map(pt =&gt; {
  21. return [+(pt[0] - left).toFixed(precision), +(pt[1] - top).toFixed(precision)];
  22. });
  23. // shift points - adjust viewBox
  24. svgEl.setAttribute(&#39;viewBox&#39;, `0 0 ${+width.toFixed(precision)} ${+height.toFixed(precision)}`);

huangapple
  • 本文由 发表于 2023年3月9日 20:15:49
  • 转载请务必保留本文链接:https://go.coder-hub.com/75684482.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定