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

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

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 弧度):

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

查看 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:

function polygon([cx, cy], sideCount, radius) {
  return pts(sideCount, radius)
    .map(({ r, theta }) => {
      // NOTE: Update theta to add 90deg clockwise rotation
      return [
        cx + r * Math.cos(theta + Math.PI * 0.5),
        cy + r * Math.sin(theta + Math.PI * 0.5)
      ];
    })
    .join(" ");
}

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

答案2

得分: 2

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

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

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

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

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

&lt;svg-polygon sides=&quot;8&quot;  fill=&quot;red&quot;&gt;&lt;/svg-polygon&gt;
&lt;svg-polygon sides=&quot;9&quot;  fill=&quot;yellow&quot;&gt;&lt;/svg-polygon&gt;
&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 -->

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

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

svg { width:180px; background:pink }

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

&lt;svg-polygon sides=&quot;8&quot; fill=&quot;red&quot;&gt;&lt;/svg-polygon&gt;
&lt;svg-polygon sides=&quot;9&quot; fill=&quot;yellow&quot;&gt;&lt;/svg-polygon&gt;
&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)中)的值,可以这样做:

let offsetDeg = (180 - angle) / 2;

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

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

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

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

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

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

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

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

<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"/>

等同于:

<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"/>

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

// 找到x和y的极值以裁剪和调整多边形和viewBox
let xVals = polyPoints.map(pt => pt[0]);
let yVals = polyPoints.map(pt => pt[1]);
let left = Math.min(...xVals);
let right = Math.max(...xVals);
let top = Math.min(...yVals);
let bottom = Math.max(...yVals);
let width = right - left;
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 -->

const sideCountEl = document.querySelector(&#39;#js-side-count&#39;);
const radiusEl = document.querySelector(&#39;#js-radius&#39;);
const cxEl = document.querySelector(&#39;#js-cx&#39;);
const cyEl = document.querySelector(&#39;#js-cy&#39;);
const generateEl = document.querySelector(&#39;#js-generate&#39;);
const polygonEl = document.querySelector(&#39;#js-polygon&#39;);
const resultEl = document.querySelector(&#39;#js-result&#39;);
const svgEl = document.querySelector(&#39;#jsSvg&#39;);

function pts(sideCount, radius) {
  const angle = 360 / sideCount;
  const vertexIndices = range(sideCount);
  let offsetDeg = (180 - angle) / 2;
  const offset = degreesToRadians(offsetDeg);

  return vertexIndices.map(index =&gt; {
    return {
      theta: offset + degreesToRadians(angle * index),
      r: radius
    };

  });
}

function range(count) {
  return Array.from(Array(count).keys());
}

function degreesToRadians(angleInDegrees) {
  return Math.PI * angleInDegrees / 180;
}


function polygon([cx, cy], sideCount, radius) {
  return pts(sideCount, radius).
  map(({
    r,
    theta
  }) =&gt; [
    cx + r * Math.cos(theta),
    cy + r * Math.sin(theta)
  ]);

}

function generatePolygon() {
  const sideCount = +sideCountEl.value;
  const radius = +radiusEl.value;
  const s = 2 * radius;

  let polyPoints = polygon(
展开收缩
, sideCount, radius); polygonEl.setAttribute(&#39;points&#39;, polyPoints.flat().join(&#39; &#39;)); // crop by - adjust viewBox to current bbox let bb = polygonEl.getBBox(); svgEl.setAttribute(&#39;viewBox&#39;, `${bb.x} ${bb.y} ${bb.width} ${bb.height}`); // show output resultEl.value = new XMLSerializer().serializeToString(jsSvg); } window.onload = generatePolygon; // Listen to changes in &lt;input /&gt; let inputs = document.querySelectorAll(&#39;input&#39;); inputs.forEach(input =&gt; { input.addEventListener(&#39;input&#39;, e =&gt; generatePolygon()); }); // convert to relative path data function polyPointsToPathRelative(polyPoints, precision) { let pointsRel = []; let offXrel = 0; let offYrel = 0; polyPoints.forEach(pt =&gt; { let [x, y] = [pt[0] - offXrel, pt[1] - offYrel]; pointsRel.push(x, y) offXrel += x; offYrel += y; }); // round pointsRel = pointsRel.map(val =&gt; { return +val.toFixed(precision) }); let M = pointsRel.splice(0, 2); let pathData = &#39;M&#39; + M.join(&#39; &#39;) + &#39;l&#39; + pointsRel.join(&#39; &#39;) + &#39;z&#39;; return pathData; }

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

body {
  margin: 0;
  font-family: sans-serif;
}

input,
textarea {
  font-family: inherit;
  font-size: 100%;
  line-height: 1.15;
  margin: 0;
}

body,
div,
fieldset,
form,
input[type=number],
legend,
main,
textarea {
  box-sizing: border-box;
}

.ba {
  border-style: solid;
  border-width: 1px;
}

.bb {
  border-bottom-style: solid;
  border-bottom-width: 1px;
}

.b--dark-gray {
  border-color: #333;
}

.b--transparent {
  border-color: transparent;
}

.bw2 {
  border-width: .25rem;
}

.db {
  display: block;
}

.flex {
  display: -webkit-box;
  display: -ms-flexbox;
  display: flex;
}

.min-vh-100 {
  min-height: 100vh;
}

.tracked {
  letter-spacing: .1em;
}

.mw7 {
  max-width: 48rem;
}

.w-100 {
  width: 100%;
}

.pa0 {
  padding: 0;
}

.pa2 {
  padding: .5rem;
}

.pa3 {
  padding: 1rem;
}

.pa4 {
  padding: 2rem;
}

.pv2 {
  padding-top: .5rem;
  padding-bottom: .5rem;
}

.ph0 {
  padding-left: 0;
  padding-right: 0;
}

.mb0 {
  margin-bottom: 0;
}

.mb2 {
  margin-bottom: .5rem;
}

.mb4 {
  margin-bottom: 2rem;
}

.mt5 {
  margin-top: 4rem;
}

.mv4 {
  margin-top: 2rem;
  margin-bottom: 2rem;
}

.mh0 {
  margin-left: 0;
  margin-right: 0;
}

.ttu {
  text-transform: uppercase;
}

.f5 {
  font-size: 1rem;
}

.measure-wide {
  max-width: 34em;
}

.center {
  margin-right: auto;
  margin-left: auto;
}

.flex-row-ns {
  -webkit-box-orient: horizontal;
  -webkit-box-direction: normal;
  -ms-flex-direction: row;
  flex-direction: row;
}

.mr4-ns {
  margin-right: 2rem;
}

.mb0-ns {
  margin-bottom: 0;
}

legend,
label {
  text-transform: uppercase;
  font-weight: 700;
}

pre {
  overflow-wrap: break-word;
}

svg {
  overflow: visible !important;
  border: 1px solid #ccc;
}

textarea {
  display: block;
  width: 100%;
  min-height: 20em;
  white-space: pre-wrap !important;
  white-space: pre-line !important;
}

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

&lt;main class=&quot;pa4 dark-gray mw7 center sans-serif min-vh-100 flex flex-column flex-row-ns &quot;&gt;
  &lt;form class=&quot;measure-wide mr4-ns mb4 mb0-ns f5&quot;&gt;
    &lt;fieldset class=&quot;ba b--transparent pa0 mh0&quot;&gt;

      &lt;legend class=&quot;f5 b ph0 mh0 ttu tracked bb bw2 pv2 db w-100&quot;&gt;
        Polygon Generator
      &lt;/legend&gt;

      &lt;div class=&quot;mt5 mb4&quot;&gt;
        &lt;label class=&quot;f5 ttu tracked db b mb2&quot; for=&quot;js-side-count&quot;&gt;
          Number of Sides
        &lt;/label&gt;
        &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;
      &lt;/div&gt;
      &lt;div class=&quot;mv4&quot;&gt;
        &lt;label class=&quot;f5 ttu tracked db b mb2&quot; for=&quot;js-radius&quot;&gt;
          Radius
        &lt;/label&gt;
        &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;
      &lt;/div&gt;
    &lt;/fieldset&gt;
  &lt;/form&gt;

  &lt;div class=&quot;flex-auto flex items-center justify-center w-100&quot;&gt;
    &lt;div class=&quot;w-100&quot;&gt;
      &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;
        &lt;polygon id=&quot;js-polygon&quot; points=&quot;&quot; /&gt;
      &lt;/svg&gt;
      &lt;textarea id=&quot;js-result&quot; class=&quot;code pa3 bg-near-white mb0 overflow-scroll&quot;&gt;&lt;/textarea&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/main&gt;

<!-- end snippet -->

Example 2: crop by recalculating polygon coordinates

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

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

const sideCountEl = document.querySelector(&#39;#js-side-count&#39;);
const radiusEl = document.querySelector(&#39;#js-radius&#39;);
const generateEl = document.querySelector(&#39;#js-generate&#39;);
const polygonEl = document.querySelector(&#39;#js-polygon&#39;);
const resultEl = document.querySelector(&#39;#js-result&#39;);
const svgEl = document.querySelector(&#39;#jsSvg&#39;);

function pts(sideCount, radius) {
  const angle = 360 / sideCount;
  const vertexIndices = range(sideCount);
  let offsetDeg = (180 - angle) / 2;
  const offset = degreesToRadians(offsetDeg);

  return vertexIndices.map(index =&gt; {
    return {
      theta: offset + degreesToRadians(angle * index),
      r: radius
    };

  });
}

function range(count) {
  return Array.from(Array(count).keys());
}

function degreesToRadians(angleInDegrees) {
  return Math.PI * angleInDegrees / 180;
}


function polygon([cx, cy], sideCount, radius) {
  return pts(sideCount, radius).
  map(({
    r,
    theta
  }) =&gt; [
    cx + r * Math.cos(theta),
    cy + r * Math.sin(theta)
  ]);

}

function generatePolygon() {
  const sideCount = +sideCountEl.value;
  const radius = +radiusEl.value;
  const s = 2 * radius;

  // calculate polygont points
  let polyPoints = polygon(
展开收缩
, sideCount, radius); /** * find x and y extrema * to crop and pan polygon and viewBox */ let xVals = polyPoints.map(pt =&gt; { return pt[0] }); let yVals = polyPoints.map(pt =&gt; { return pt[1] }); let left = Math.min(...xVals); let right = Math.max(...xVals); let top = Math.min(...yVals); let bottom = Math.max(...yVals); let width = right - left; let height = bottom - top; // round coordinates let precision = +inputPrecision.value; // calculate cropped polygon polyPoints = polyPoints.map(pt =&gt; { return [+(pt[0] - left).toFixed(precision), +(pt[1] - top).toFixed(precision)]; }); // create relative path let pathData = polyPointsToPathRelative(polyPoints, precision) // shift points - adjust viewBox svgEl.setAttribute(&#39;viewBox&#39;, `0 0 ${+width.toFixed(precision)} ${+height.toFixed(precision)}`); polygonEl.setAttribute(&#39;d&#39;, pathData); let output = new XMLSerializer().serializeToString(jsSvg). replace(/\s{2,}/g, &quot; &quot;). replaceAll(&#39;&gt; &lt;&#39;, &#39;&gt;&lt;&#39;). replaceAll(&#39;&lt;&#39;, &#39;\n&lt;&#39;); //console.log(output); resultEl.value = output; } window.onload = generatePolygon; // Listen to changes in &lt;input /&gt; let inputs = document.querySelectorAll(&#39;input&#39;); inputs.forEach(input =&gt; { input.addEventListener(&#39;input&#39;, e =&gt; generatePolygon()); }); // convert to relative path data function polyPointsToPathRelative(polyPoints, precision) { let pointsRel = []; let offXrel = 0; let offYrel = 0; polyPoints.forEach(pt =&gt; { let [x, y] = [pt[0] - offXrel, pt[1] - offYrel]; pointsRel.push(x, y) offXrel += x; offYrel += y; }); // round pointsRel = pointsRel.map(val =&gt; { return +val.toFixed(precision) }); let M = pointsRel.splice(0, 2); let pathData = &#39;M&#39; + M.join(&#39; &#39;) + &#39;l&#39; + pointsRel.join(&#39; &#39;) + &#39;z&#39;; return pathData; }

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

body {
  margin: 0;
  font-family: sans-serif;
}

input,
textarea {
  font-family: inherit;
  font-size: 100%;
  line-height: 1.15;
  margin: 0;
}

body,
div,
fieldset,
form,
input[type=number],
legend,
main,
textarea {
  box-sizing: border-box;
}

.ba {
  border-style: solid;
  border-width: 1px;
}

.bb {
  border-bottom-style: solid;
  border-bottom-width: 1px;
}

.b--dark-gray {
  border-color: #333;
}

.b--transparent {
  border-color: transparent;
}

.bw2 {
  border-width: .25rem;
}

.db {
  display: block;
}

.flex {
  display: -webkit-box;
  display: -ms-flexbox;
  display: flex;
}

.min-vh-100 {
  min-height: 100vh;
}

.tracked {
  letter-spacing: .1em;
}

.mw7 {
  max-width: 48rem;
}

.w-100 {
  width: 100%;
}

.pa0 {
  padding: 0;
}

.pa2 {
  padding: .5rem;
}

.pa3 {
  padding: 1rem;
}

.pa4 {
  padding: 2rem;
}

.pv2 {
  padding-top: .5rem;
  padding-bottom: .5rem;
}

.ph0 {
  padding-left: 0;
  padding-right: 0;
}

.mb0 {
  margin-bottom: 0;
}

.mb2 {
  margin-bottom: .5rem;
}

.mb4 {
  margin-bottom: 2rem;
}

.mt5 {
  margin-top: 4rem;
}

.mv4 {
  margin-top: 2rem;
  margin-bottom: 2rem;
}

.mh0 {
  margin-left: 0;
  margin-right: 0;
}

.ttu {
  text-transform: uppercase;
}

.f5 {
  font-size: 1rem;
}

.measure-wide {
  max-width: 34em;
}

.center {
  margin-right: auto;
  margin-left: auto;
}

.flex-row-ns {
  -webkit-box-orient: horizontal;
  -webkit-box-direction: normal;
  -ms-flex-direction: row;
  flex-direction: row;
}

.mr4-ns {
  margin-right: 2rem;
}

.mb0-ns {
  margin-bottom: 0;
}

legend,
label {
  text-transform: uppercase;
  font-weight: 700;
}

pre {
  overflow-wrap: break-word;
}

svg {
  overflow: visible !important;
  border: 1px solid #ccc;
}

textarea {
  display: block;
  width: 100%;
  min-height: 20em;
  white-space: pre-wrap !important;
  white-space: pre-line !important;
}

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

&lt;main class=&quot;pa4 dark-gray mw7 center sans-serif min-vh-100 flex flex-column flex-row-ns &quot;&gt;
  &lt;form class=&quot;measure-wide mr4-ns mb4 mb0-ns f5&quot;&gt;
    &lt;fieldset class=&quot;ba b--transparent pa0 mh0&quot;&gt;
      &lt;legend class=&quot;f5 b ph0 mh0 ttu tracked bb bw2 pv2 db w-100&quot;&gt;
        Polygon Generator
      &lt;/legend&gt;
      &lt;div class=&quot;mt5 mb4&quot;&gt;
        &lt;label class=&quot;f5 ttu tracked db b mb2&quot; for=&quot;js-side-count&quot;&gt;
          Number of Sides
        &lt;/label&gt;
        &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;
      &lt;/div&gt;
      &lt;div class=&quot;mv4&quot;&gt;
        &lt;label class=&quot;f5 ttu tracked db b mb2&quot; for=&quot;js-radius&quot;&gt;
          Radius
        &lt;/label&gt;
        &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;
      &lt;/div&gt;
      &lt;div class=&quot;mv4&quot;&gt;
        &lt;label class=&quot;f5 ttu tracked db b mb2&quot; for=&quot;js-radius&quot;&gt;
          precision
        &lt;/label&gt;
        &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;
      &lt;/div&gt;
    &lt;/fieldset&gt;
  &lt;/form&gt;

  &lt;div class=&quot;flex-auto flex items-center justify-center w-100&quot;&gt;
    &lt;div class=&quot;w-100&quot;&gt;
      &lt;svg viewBox=&quot;0 0 800 800&quot; id=&quot;jsSvg&quot;&gt;
        &lt;path id=&quot;js-polygon&quot; d=&quot;&quot; /&gt;
      &lt;/svg&gt;

      &lt;textarea id=&quot;js-result&quot; class=&quot;code pa3 bg-near-white mb0 overflow-scroll&quot;&gt;&lt;/textarea&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&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.

&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

&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:

  /**
   * find x and y extrema
   * to crop and pan polygon and viewBox
   */
  let xVals = polyPoints.map(pt =&gt; {
    return pt[0]
  });
  let yVals = polyPoints.map(pt =&gt; {
    return pt[1]
  });

  let left = Math.min(...xVals);
  let right = Math.max(...xVals);
  let top = Math.min(...yVals);
  let bottom = Math.max(...yVals);
  let width = right - left;
  let height = bottom - top;

  // round coordinates
  let precision = +inputPrecision.value;

  // calculate cropped polygon
  polyPoints = polyPoints.map(pt =&gt; {
    return [+(pt[0] - left).toFixed(precision), +(pt[1] - top).toFixed(precision)];
  });
    

  // shift points - adjust viewBox
  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:

确定