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
2 Answers
Reset to default 4I 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>