最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - How can I dynamically update SVG viewBox values from Polygon - Stack Overflow

programmeradmin2浏览0评论

I have created SVG maps of a ic book page panels.

<svg id="svg1413"  class="svg-pg" width="100%" height="100%" version="1.1" viewBox="0 0 178 254" xmlns="" xmlns:xlink="" style="transition: .6s ease-out;">
    <image id="image1966" width="178" height="254" clip-path="url('#SvgjsClipPath1413')" xlink:href=".jpg" />
    <g id="SvgjsG1413" clip-path="url('#SvgjsClipPath1413')" class="click-panels">
        <polygon fill="transparent" points=" 12,13 88,13 88,49 12,49"></polygon>
        <polygon fill="transparent" points=" 81,44 166,44 166,74 81,74"></polygon>
        <polygon fill="transparent" points=" 12,13 166,13 166,75 12,75"></polygon>
        <polygon fill="transparent" points=" 101,80 166,80 166,118 101,118"></polygon>
        <polygon fill="transparent" points=" 12,80 166,80 166,147 12,147"></polygon>
        <polygon fill="transparent" points=" 12,152 91,152 91,200 12,200"></polygon>
        <polygon fill="transparent" points=" 73,171 155,171 155,209 73,209"></polygon>
        <polygon fill="transparent" points=" 12,198 79,198 79,235 12,235"></polygon>
        <polygon fill="transparent" points=" 12,152 166,152 166,235 12,235"></polygon>
    </g>
    <defs id="SvgjsDefs1413">
        <clipPath id="SvgjsClipPath1413">
            <rect id="SvgjsRect1413" width="100%" height="100%" x="0" y="0">
            </rect>
        </clipPath>
    </defs>     
</svg>
<svg>
    ...svg content
</svg>
<svg>
    ...svg content
</svg>

What I want to achieve is:

  • Loop through all the <svg> tags
  • while on an <svg>, loop through the <polygon> tags contained within the <svg>
  • Each <polygon> contains 4 pairs of coordinates (eg. points=" x1,y1 x2,y2 x3,y3 x4,y4")
  • I want the parent <svg> viewBox values and the <rect> X and Y values to be updated with values from the current <polygon> points in the loop.

I have figured out how to calculate the values, but I don't know how to achieve this in Javascript.

My pseudo-code for calculating <svg> viewBox values

if <svg viewBox=" a b c d ">
a = x1, b = y1, c = x2 - x1, d = y3 - y2

Updating <rect> Values

if <rect x="e" y="f">
e = x1, f = y1

UPDATE: How do I control the transitions with click/swipe events rather than making it automatic?

<div id="controls" class="ctl-btn" style="width: 100%; position: absolute; bottom: 0; margin: 0 -8px; background-color: rgba(6,6,6,0.40);">
    <div style="max-width: 800px; text-align: center; margin: 0 auto;">
        <button class="pg-ctl-bk" style="margin: 8px; padding: 8px 10px;">  Back </button>
        <button class="pg-ctl-nxt" style="margin: 8px; padding: 8px 10px;"> Next  </button>
    </div>
</div>

I have created SVG maps of a ic book page panels.

<svg id="svg1413"  class="svg-pg" width="100%" height="100%" version="1.1" viewBox="0 0 178 254" xmlns="http://www.w3/2000/svg" xmlns:xlink="http://www.w3/1999/xlink" style="transition: .6s ease-out;">
    <image id="image1966" width="178" height="254" clip-path="url('#SvgjsClipPath1413')" xlink:href="https://i.imgur./yiZFUK4.jpg" />
    <g id="SvgjsG1413" clip-path="url('#SvgjsClipPath1413')" class="click-panels">
        <polygon fill="transparent" points=" 12,13 88,13 88,49 12,49"></polygon>
        <polygon fill="transparent" points=" 81,44 166,44 166,74 81,74"></polygon>
        <polygon fill="transparent" points=" 12,13 166,13 166,75 12,75"></polygon>
        <polygon fill="transparent" points=" 101,80 166,80 166,118 101,118"></polygon>
        <polygon fill="transparent" points=" 12,80 166,80 166,147 12,147"></polygon>
        <polygon fill="transparent" points=" 12,152 91,152 91,200 12,200"></polygon>
        <polygon fill="transparent" points=" 73,171 155,171 155,209 73,209"></polygon>
        <polygon fill="transparent" points=" 12,198 79,198 79,235 12,235"></polygon>
        <polygon fill="transparent" points=" 12,152 166,152 166,235 12,235"></polygon>
    </g>
    <defs id="SvgjsDefs1413">
        <clipPath id="SvgjsClipPath1413">
            <rect id="SvgjsRect1413" width="100%" height="100%" x="0" y="0">
            </rect>
        </clipPath>
    </defs>     
</svg>
<svg>
    ...svg content
</svg>
<svg>
    ...svg content
</svg>

What I want to achieve is:

  • Loop through all the <svg> tags
  • while on an <svg>, loop through the <polygon> tags contained within the <svg>
  • Each <polygon> contains 4 pairs of coordinates (eg. points=" x1,y1 x2,y2 x3,y3 x4,y4")
  • I want the parent <svg> viewBox values and the <rect> X and Y values to be updated with values from the current <polygon> points in the loop.

I have figured out how to calculate the values, but I don't know how to achieve this in Javascript.

My pseudo-code for calculating <svg> viewBox values

if <svg viewBox=" a b c d ">
a = x1, b = y1, c = x2 - x1, d = y3 - y2

Updating <rect> Values

if <rect x="e" y="f">
e = x1, f = y1

UPDATE: How do I control the transitions with click/swipe events rather than making it automatic?

<div id="controls" class="ctl-btn" style="width: 100%; position: absolute; bottom: 0; margin: 0 -8px; background-color: rgba(6,6,6,0.40);">
    <div style="max-width: 800px; text-align: center; margin: 0 auto;">
        <button class="pg-ctl-bk" style="margin: 8px; padding: 8px 10px;">  Back </button>
        <button class="pg-ctl-nxt" style="margin: 8px; padding: 8px 10px;"> Next  </button>
    </div>
</div>
Share Improve this question edited Apr 25, 2019 at 10:38 cnario asked Apr 25, 2019 at 4:01 cnariocnario 311 silver badge6 bronze badges 3
  • I don't get it, if you want to update your <svg>'s viewBox depending on all the polygons in a loop, why don't you just do it for the last polygon? viewBox can have only one value at a time, so it would do the same. Can you clarify a bit what you really are after? – Kaiido Commented Apr 25, 2019 at 4:09
  • I want to loop through all the polygons (like a carousel), on each polygon, I want the parent viewBox to be updated with the values calculated from the current polygon in the loop (carousel) – cnario Commented Apr 25, 2019 at 4:12
  • here is an example of what I want to achieve read.marvel./#book/41323 – cnario Commented Apr 25, 2019 at 4:14
Add a ment  | 

2 Answers 2

Reset to default 4

I am not sure why you used <polygon> elements here, when all you needed was a javascript Array/JSON, or ultimately <rect> elements which at least have x, y, width and height attribute as requested by viewBox attribute, but well... let's deal with <polygon>s then...

To convert your <polygon>'s point attribute value to a viewBox attribute, the simplest is to call polygonElement.getBBox() which will return an SVGRect with the needed values.

To have the viewBox property animated, the easiest is probably to use SMIL animations, and a polyfill for MS browsers.

You simply need to define an <animate> element targeting the viewBox attribute, and to update its to attribute to the target value, after you updated its from attribute to the current value.

// animate : <animate attributeName="viewBox" ...>
// rect : {SVGRect} result of polygonElement.getBBox();
function animateViewBox(animate, rect) {
  animate.setAttribute('from', animate.getAttribute('to'));
  animate.setAttribute('to', `${rect.x} ${rect.y} ${rect.width} ${rect.height}`);
  animate.beginElement(); // (re)start the animation
}

Once we have this, we only need to set up a function that will iterate over all the <polygon> elements in the svg.

function animateViewBox(animate, rect) {
  animate.setAttribute('from', animate.getAttribute('to'));
  animate.setAttribute('to', `${rect.x} ${rect.y} ${rect.width} ${rect.height}`);
  animate.beginElement(); // (re)start the animation
}

// container
const svg = document.getElementById('svg1413');
// all the <polygons> coordinates (would be better as JSON...)
const polygons = svg.querySelectorAll('polygon');
// <animate> element
const animator = svg.querySelector('.viewBoxAnimator');

// our iterator, we could call it on click
let i = 0;

function iterate() {
  if (i < polygons.length) {
    animateViewBox(animator, polygons[i++].getBBox());
    return true;
  }
}

// but we'll automate it
(async() => {
  while (iterate()) {
    await wait(1500);
  }
})();


function wait(time) {
  return new Promise(res => setTimeout(res, time));
}
svg {
  width: 100%;
  height: 100%;
  max-width: 100vw;
  max-height: 100vh;
  transition: all .6s;
}

html {
  background: black;
}
<!-- SMIL for IE -->
<script src="https://cdn.jsdelivr/gh/Kaiido/FakeSmile@1e50d675df616a8e784e0e6e931b3f0d595367d4/smil.user.js"></script>

<svg id="svg1413" class="svg-pg" width="154" height="83" version="1.1" viewBox="0 0 178 254" xmlns="http://www.w3/2000/svg" xmlns:xlink="http://www.w3/1999/xlink">
  <animate class="viewBoxAnimator" attributeType="XML" attributeName="viewBox" from="0 0 178 254" to="0 0 178 254" dur="0.6s" fill="freeze"/>
  <image id="image1966" width="178" height="254" xlink:href="https://i.imgur./yiZFUK4.jpg" />
  <g id="SvgjsG1413" class="click-panels">
    <polygon fill="transparent" points=" 12,13 88,13 88,49 12,49"></polygon>
    <polygon fill="transparent" points=" 81,44 166,44 166,74 81,74"></polygon>
    <polygon fill="transparent" points=" 12,13 166,13 166,75 12,75"></polygon>
    <polygon fill="transparent" points=" 101,80 166,80 166,118 101,118"></polygon>
    <polygon fill="transparent" points=" 12,80 166,80 166,147 12,147"></polygon>
    <polygon fill="transparent" points=" 12,152 91,152 91,200 12,200"></polygon>
    <polygon fill="transparent" points=" 73,171 155,171 155,209 73,209"></polygon>
    <polygon fill="transparent" points=" 12,198 79,198 79,235 12,235"></polygon>
    <polygon fill="transparent" points=" 12,152 166,152 166,235 12,235"></polygon>
  </g>
</svg>

You used the svg.js tag, so you get an svg.js answer:

// Reference to svg
const canvas = SVG('#svg1413')

// List of all polygons
const polygons = canvas.find('#SvgjsG1413 polygon')

// List of all bboxes
const boxes = polygons.bbox()

const nextImage = function (index) {
  // Animate viewbox over 1s to new box
  canvas.animate(1000).viewbox(boxes[index])

  // Next image in 2s
  setTimeout(() => nextImage(++index), 2000)
}

nextImage(0)
svg {
  width: 100%;
  height: 100%;
  max-width: 100vw;
  max-height: 100vh;
}

html {
  background: black;
}
<script src="https://cdn.jsdelivr/npm/@svgdotjs/svg.js@latest/dist/svg.min.js"></script>


<svg id="svg1413"  class="svg-pg" width="154" height="254" version="1.1" viewBox="0 0 178 254" xmlns="http://www.w3/2000/svg" xmlns:xlink="http://www.w3/1999/xlink" style="transition: .6s ease-out;">
    <image id="image1966" width="178" height="254" clip-path="url('#SvgjsClipPath1413')" xlink:href="https://i.imgur./yiZFUK4.jpg" />
    <g id="SvgjsG1413" clip-path="url('#SvgjsClipPath1413')" class="click-panels">
        <polygon fill="transparent" points=" 12,13 88,13 88,49 12,49"></polygon>
        <polygon fill="transparent" points=" 81,44 166,44 166,74 81,74"></polygon>
        <polygon fill="transparent" points=" 12,13 166,13 166,75 12,75"></polygon>
        <polygon fill="transparent" points=" 101,80 166,80 166,118 101,118"></polygon>
        <polygon fill="transparent" points=" 12,80 166,80 166,147 12,147"></polygon>
        <polygon fill="transparent" points=" 12,152 91,152 91,200 12,200"></polygon>
        <polygon fill="transparent" points=" 73,171 155,171 155,209 73,209"></polygon>
        <polygon fill="transparent" points=" 12,198 79,198 79,235 12,235"></polygon>
        <polygon fill="transparent" points=" 12,152 166,152 166,235 12,235"></polygon>
    </g>  
</svg>

发布评论

评论列表(0)

  1. 暂无评论