How to animate svg height with CSS upward? - javascript
I am working with a svg element and I want to animate the height increasing upwards of these elements. But they are growing downward from the top probably due to SVG coordinate system. How can I make it go upwards from the bottom?
window.onload = function () {
var x1 = document.querySelector('svg').viewBox.baseVal.height;
var a = document.getElementById('wrapper1');
var bgArray = [];
for (var i = 1; i < a.children.length; i++) {
bgArray.push((a.children[i].getBBox().height / x1) * 100);
};
bgArray.forEach((x, i) => a.children[i + 1].style.setProperty("--h1", x + '%'));
bgArray.forEach((x, i) => a.children[i + 1].style.setProperty("--del", (i + 1) + 's')); //for staggered
}
.r1,
.r2 {
visibility: hidden;
animation: moveHeight 2s ease-in var(--del) 1 forwards;
}
#keyframes moveHeight {
0% {
visibility: visible;
height: 0%;
}
100% {
visibility: visible;
height: var(--h1);
}
}
<!DOCTYPE html>
<html>
<body>
<link rel="stylesheet" href="style.css">
</link>
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1280 720">
<g class="wrapper1" id="wrapper1" >
<rect x="10" y="20" width="120" height="120" stroke="black" fill="none"></rect>
<rect class="r1" id="r1" x="10" y="80" width="20" height="60" stroke="none" fill="orange"></rect>
<rect class="r2" id="r2" x="31" y="100" width="20" height="40" stroke="none" fill="green"></rect>
<!---->
</g>
</svg>
<script src="index.js"></script>
</body>
</html>
You could draw the bars but cover them and then gradually uncover them.
This covers them by adding white bars and shrinking those.
window.onload = function() {
var x1 = document.querySelector('svg').viewBox.baseVal.height;
var a = document.getElementById('wrapper1');
var bgArray = [];
for (var i = 1; i < a.children.length; i++) {
bgArray.push((a.children[i].getBBox().height / x1) * 100);
};
bgArray.forEach((x, i) => a.children[i + 1].style.setProperty("--h1", x + '%'));
bgArray.forEach((x, i) => a.children[i + 1].style.setProperty("--del", (i + 1) + 's')); //for staggered
}
.shrink {
animation: moveHeight 2s ease-in var(--del) 1 forwards;
}
#keyframes moveHeight {
0% {
height: var(--h1);
}
100% {
height: 0;
}
}
<!DOCTYPE html>
<html>
<body>
<link rel="stylesheet" href="style.css">
</link>
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1280 720">
<g class="wrapper1" id="wrapper1" >
<rect x="10" y="20" width="120" height="120" stroke="black" fill="none"></rect>
<rect class="r1" id="r1" x="10" y="80" width="20" height="60" stroke="none" fill="orange"></rect>
<rect class="r1 shrink" x="10" y="80" width="20" height="60" stroke="none" fill="white"></rect>
<rect class="r2" id="r2" x="31" y="100" width="20" height="40" stroke="none" fill="green"></rect>
<rect class="r2 shrink" x="30" y="99" width="22" height="41" stroke="none" fill="white"></rect>
<!---->
</g>
</svg>
<script src="index.js"></script>
</body>
</html>
Note, at some zoom levels a screen pixel of the underneath bars can be left showing (giving a faint vertical line at the start). To get round that the second white bar has been moved one left and made one wider (and similarly for height). Somewhat hacky so I hope someone can come up with a more straightforward solution.
I have looked into this brilliant answer but I had trouble visualizing and adapting it to my scenario. So after I figured out, I am trying to visually explain and show how to adapt to different scenarios.
The basic markup is following
<!DOCTYPE html>
<html>
<body>
<link rel="stylesheet" href="style.css">
</link>
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1280 720">
<rect class="wrapper1" id="wrapper1" x="10" y="345" width="120" height="180" stroke="teal" fill="none"></rect>
<line class="upper clone" x1="10" y1="80" x2="220" y2="80" style="stroke:rgb(110, 31, 194);stroke-width:1" />
<g class="elements">
<line class="upper" x1="10" y1="80" x2="220" y2="80" style="stroke:rgb(87, 202, 91);stroke-width:1"/>
<rect class="r0" id="r0" x="10" y="80" width="83" height="180" stroke="brown" fill="none"></rect>
<rect class="r1" id="r1" x="10" y="80" width="20" height="60" stroke="none" fill="orange"></rect>
<rect class="r2" id="r2" x="31" y="80" width="20" height="10" stroke="none" fill="green"></rect>
<rect class="r3" id="r3" x="52" y="80" width="20" height="100" stroke="none" fill="blue"></rect>
<rect class="r4" id="r4" x="73" y="80" width="20" height="140" stroke="none" fill="magenta"></rect>
<line class="lower" x1="10" y1="260" x2="220" y2="260" style="stroke:rgb(87, 202, 91);stroke-width:1"/>
</g>
<line class="lower clone" x1="10" y1="260" x2="220" y2="260" style="stroke:rgb(218, 149, 22);stroke-width:1" />
<g class="wrapper2" id="wrapper2">
<rect x="140" y="0" width="50" height="700" stroke="red" fill="none"></rect>
</g>
</svg>
<script src="index2.js"></script>
</body>
</html>
For ease of understanding, I have created the two lines called upper and lower which are the start and end of all the rects. I have also cloned those lines where the transform will not apply.
The thumb rule of converting svg coordinate system to cartesian coordinate is to
use this transform="translate(0, maxY) scale(1, -1)".
The maxY is actually max total of (y+height). In this case it is 260. now this value can be manipulated to correctly place the rects prior to animation
<g class="elements" transform="translate(0,260) scale(1, -1)"> positions
<g class="elements" transform="translate(0,340) scale(1, -1)"> 260+rectY(80)=340 positions
<g class="elements" transform="translate(0,425) scale(1, -1)"> 260+rectY(80)+(wrapperY 345 -260) positions
and lastly positions at the bottom of the wrapper
<g class="elements" transform="translate(0,605) scale(1, -1)"> 260+rectY(80)+(wrapperY 345 -260)+wrapperHeight 180 = 605
Putting everything together with javascript and css
//converting svg coordinates to cartesian coordinates
// transform="translate(0, maxY) scale(1, -1)".
// or transform="scale(1, -1) translate(0, -maxY)".
window.onload = function app() {
var bgArray1 = []; //to collect only height
var bgArray2 = []; //to collect height + y
var wrapperElement = document.getElementById('wrapper1');
var wrapperY = wrapperElement.getBBox().y //what is the y coordinate of wrapper rect
var wrapperHeight = wrapperElement.getBBox().height;
var getRect = document.querySelectorAll('[class^="r"]');
for (var i = 0; i < getRect.length; i++) {
var _bBox = getRect[i].getBBox(); // getting the bBox for each rect
var _height = _bBox.height + _bBox.y //getting each rect's height
var _y = _bBox.y // getting each rect's y coordinate
var _heightAndY = _height + _y //total of Y+height of each rect
bgArray1.push(_height);
bgArray2.push(_heightAndY);
}
var _maxHeight = Math.max(...bgArray1); // use this if you want to position the rect at (y=0)
var _maxTotal = Math.max(...bgArray2); // use this if you want to position the rect at (y=0)
var _wrapperTop = _maxTotal + (wrapperY - _maxHeight); //use this if you want to postion the rect at the the
//top of the wrapper
var _wrapperBottom = _wrapperTop + wrapperHeight //use this if you wantto position the rects at the
//bottom of the wrapper
bgArray1.forEach((x, i) => getRect[i].setAttribute("transform", `translate(0,${_wrapperBottom}) scale(1,-1)`));
bgArray1.forEach((x, i) => getRect[i].style.setProperty("--del", (i + 1) + 's'))
}
[class^="r"] {
visibility: hidden;
animation: moveHeight 2s ease-out var(--del) 1 forwards;
}
#keyframes moveHeight {
0% {
visibility: visible;
height: 0
}
100% {
visibility: visible;
height: 100;
}
}
<!DOCTYPE html>
<html>
<body>
<link rel="stylesheet" href="style.css">
</link>
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1280 720">
<rect class="wrapper1" id="wrapper1" x="10" y="345" width="120" height="180" stroke="teal" fill="none"></rect>
<line class="upper clone" x1="10" y1="80" x2="220" y2="80" style="stroke:rgb(110, 31, 194);stroke-width:1" />
<g>
<line class="upper" x1="10" y1="80" x2="220" y2="80" style="stroke:rgb(87, 202, 91);stroke-width:1"/>
<rect class="r0" id="r0" x="10" y="80" width="83" height="180" stroke="brown" fill="none"></rect>
<rect class="r1" id="r1" x="10" y="80" width="20" height="60" stroke="none" fill="orange"></rect>
<rect class="r2" id="r2" x="31" y="80" width="20" height="10" stroke="none" fill="green"></rect>
<rect class="r3" id="r3" x="52" y="80" width="20" height="100" stroke="none" fill="blue"></rect>
<rect class="r4" id="r4" x="73" y="80" width="20" height="140" stroke="none" fill="magenta"></rect>
<line class="lower" x1="10" y1="260" x2="220" y2="260" style="stroke:rgb(87, 202, 91);stroke-width:1"/>
</g>
<line class="lower clone" x1="10" y1="260" x2="220" y2="260" style="stroke:rgb(218, 149, 22);stroke-width:1" />
<g class="wrapper2" id="wrapper2">
<rect x="140" y="0" width="50" height="700" stroke="red" fill="none"></rect>
</g>
</svg>
<!--<script src="index.js"></script>-->
<script src="index2.js"></script>
</body>
</html>
Pure svg solution
The idea is to first hide the rectangles behind a masking strip.
Then raise them to the top by animating the y attribute of the rectangle
<animate id="anR1" xlink:href="#r1" attributeName="y" begin="Layer_1.click"
dur="1s" from="140" to="80" repeatCount="1" fill="freeze" />
<!DOCTYPE html>
<html>
<body>
<link rel="stylesheet" href="style.css">
</link>
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1280 720">
<g class="wrapper1" id="wrapper1" >
<rect x="10" y="20" width="120" height="120" stroke="black" fill="none"></rect>
<rect class="r1" id="r1" x="10" y="140" width="20" height="60" stroke="none" fill="orange"></rect>
<rect class="r2" id="r2" x="31" y="140" width="20" height="40" stroke="none" fill="green"></rect>
<rect class="r3" id="r3" x="52" y="140" width="20" height="80" stroke="none" fill="dodgerblue"></rect>
<!-- masking strip -->
<rect x="10" y="140" width="120" height="120" fill="white"></rect>
</g>
<animate id="anR1" xlink:href="#r1" attributeName="y" begin="Layer_1.click" dur="0.5s" from="140" to="80" repeatCount="1" fill="freeze" />
<animate id="anR2" xlink:href="#r2" attributeName="y" begin="anR1.end+0.25s" dur="0.5s" from="140" to="100" repeatCount="1" fill="freeze" />
<animate id="anR3" xlink:href="#r3" attributeName="y" begin="anR2.end+0.25s" dur="0.5s" from="140" to="60" repeatCount="1" fill="freeze" />
</svg>
<script src="index.js"></script>
</body>
</html>
Related
Fabricjjs SVG with javascript doesnot supports in canvas
I'm trying to develop a component-> timer, which is an SVG file with timer functionality(written in javascript). The Svg file is given below. <svg id="Timer" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 223.26 67.89"> <!--Styles for blink circles animation--> <style> #import url('http://fonts.cdnfonts.com/css/nexa-bold'); #keyframes up-right { 0% { transform: scale(1); opacity: .25 } 50% { transform: scale (1, 5); opacity: 1; } 100% { transform: scale(1); opacity: .25; } } .circle { border-radius: 50%; width: 80px; height: 80px; opacity: .25; } .red { background-color: red; position: absolute; top: 50%; left: 50%; -webkit-animation: up-right 1s infinite; -moz-animation: up-right 1s infinite; -o-animation: up-right 1s infinite; animation: up-right 1s infinite; } </style> <defs> <linearGradient id="linear-gradient" x1="833.87" y1="1338.03" x2="833.87" y2="1384.21" gradientTransform="translate(-792.51 -1332.57)" gradientUnits="userSpaceOnUse"> <stop offset="0" stop-color="#474747" /> <stop offset="1" stop-color="#333" /> </linearGradient> <clipPath id="clip-path"> <rect x="20.94" y="11.97" width="40.86" height="20.43" transform="translate(82.73 44.37) rotate(180)" fill="none" /> </clipPath> <linearGradient id="linear-gradient-2" x1="833.87" y1="1340.41" x2="833.87" y2="1384.36" gradientTransform="translate(-792.51 -1334.64)" xlink:href="#linear-gradient" /> <linearGradient id="linear-gradient-3" x1="763.61" y1="1337" x2="763.61" y2="1383.18" gradientTransform="translate(-651.98 -1330.51)" xlink:href="#linear-gradient" /> <clipPath id="clip-path-2"> <rect x="91.2" y="13" width="40.86" height="20.43" transform="translate(223.26 46.43) rotate(180)" fill="none" /> </clipPath> <linearGradient id="linear-gradient-4" x1="763.61" y1="1339.38" x2="763.61" y2="1383.33" gradientTransform="translate(-651.98 -1332.58)" xlink:href="#linear-gradient" /> <linearGradient id="linear-gradient-5" x1="693.34" y1="1337" x2="693.34" y2="1383.18" gradientTransform="translate(-511.45 -1330.51)" xlink:href="#linear-gradient" /> <clipPath id="clip-path-3"> <rect x="161.47" y="13" width="40.86" height="20.43" transform="translate(363.79 46.43) rotate(180)" fill="none" /> </clipPath> <linearGradient id="linear-gradient-6" x1="693.34" y1="1339.38" x2="693.34" y2="1383.33" gradientTransform="translate(-511.45 -1332.58)" xlink:href="#linear-gradient" /> </defs> <g style="isolation:isolate"> <g id="Layer_1" data-name="Layer 1"> <rect width="223.26" height="67.89" rx="5.59" fill="#fff" opacity="0.15" /> <rect x="4.51" y="2.92" width="215.16" height="62.3" rx="5.59" fill="#fff" opacity="0.07" /> <rect x="17.36" y="8.44" width="48" height="49.98" rx="5.84" transform="translate(82.73 66.86) rotate(180)" fill="#fff" opacity="0.09" /> <rect x="20.94" y="11.97" width="40.86" height="42.92" rx="5.84" transform="translate(82.73 66.86) rotate(180)" fill="url(#linear-gradient)" /> <g clip-path="url(#clip-path)"> <rect x="20.94" y="11.97" width="40.86" height="40.85" rx="5.84" transform="translate(82.73 64.8) rotate(180)" fill="url(#linear-gradient-2)" /> </g> <rect x="87.63" y="9.47" width="48" height="49.98" rx="5.84" transform="translate(223.26 68.92) rotate(180)" fill="#fff" opacity="0.09" /> <rect x="91.2" y="13" width="40.86" height="42.92" rx="5.84" transform="translate(223.26 68.92) rotate(180)" fill="url(#linear-gradient-3)" /> <g clip-path="url(#clip-path-2)"> <rect x="91.2" y="13" width="40.86" height="40.85" rx="5.84" transform="translate(223.26 66.86) rotate(180)" fill="url(#linear-gradient-4)" /> </g> <rect x="157.89" y="9.47" width="48" height="49.98" rx="5.84" transform="translate(363.79 68.92) rotate(180)" fill="#fff" opacity="0.09" /> <rect x="161.47" y="13" width="40.86" height="42.92" rx="5.84" transform="translate(363.79 68.92) rotate(180)" fill="url(#linear-gradient-5)" /> <g clip-path="url(#clip-path-3)"> <rect x="161.47" y="13" width="40.86" height="40.85" rx="5.84" transform="translate(363.79 66.86) rotate(180)" fill="url(#linear-gradient-6)" /> </g> <!--Time Display Text--> <!-- <text transform="translate(30.07 42.83) scale(1.02 1)" font-size="24" fill="#fff" x="-2" font-family="Nexa, sans-serif"> <tspan xml:space="preserve"></tspan> </text> --> <text transform="translate(24.25 41.95) scale(1.02 1)" font-size="24" fill="#00f9a6" font-family="Nexa, sans-serif" x="2"> <tspan xml:space="preserve"></tspan> </text> <text transform="translate(94.19 41.95) scale(1.02 1)" font-size="24" fill="#00f9a6" font-family="Nexa, sans-serif" x="1.2"> <tspan xml:space="preserve"></tspan> </text> <text transform="translate(164.94 41.97) scale(1.02 1)" font-size="24" fill="#00f9a6" font-family="Nexa, sans-serif"> <tspan xml:space="preserve"></tspan> </text> <!--Time Display Text--> <circle cx="77.18" cy="28.28" r="5.06" fill="#00f9a6" opacity="0.19" /> <circle class="circle red" cx="77.18" cy="28.28" r="3.07" fill="#00f9a6" opacity="0.11" /> <circle cx="77.18" cy="43.85" r="5.06" fill="#00f9a6" opacity="0.19" /> <circle class="circle red" cx="77.18" cy="43.85" r="3.07" fill="#00f9a6" opacity="0.11" /> <circle cx="147.3" cy="29.14" r="5.06" fill="#00f9a6" opacity="0.19" /> <circle class="circle red" cx="147.3" cy="29.14" r="3.07" fill="#00f9a6" opacity="0.11" /> <circle cx="147.3" cy="44.72" r="5.06" fill="#00f9a6" opacity="0.19" /> <circle class="circle red" cx="147.3" cy="44.72" r="3.07" fill="#00f9a6" opacity="0.11" /> </g> </g> <!-- Script For Time Display --> <script type="text/javascript"> <![CDATA[ var hr = document.getElementsByTagName('tspan')[0]; var min = document.getElementsByTagName('tspan')[1]; var sec = document.getElementsByTagName('tspan')[2]; function time() { var d = new Date(); var s = d.getSeconds(); var m = d.getMinutes(); var h = d.getHours(); hr.textContent = ("0" + h).substr(-2); min.textContent = ("0" + m).substr(-2); sec.textContent = ("0" + s).substr(-2); } setInterval(time, 1000); ]]> </script> </svg> We tried this code, which is working for all images except this SVG with scripts fabric.loadSVGFromURL(controller.value, (objects, options) => { const image = fabric.util.groupSVGElements(objects, options); image.set({ left: !_initPositionOfElement ? 50 : _initPositionOfElement.x + 10, top: !_initPositionOfElement ? 50 : _initPositionOfElement.y + 10, angle: 0, padding: 0, cornerSize: 15, shadow: new fabric.Shadow(shadow), lockRotation: false, hasRotatingPoint: false, perPixelTargetFind: true, //minScaleLimit: 1, scaleX: 1, scaleY: 1, }); this.canvas.add(image); this.canvas.setActiveObject(image); this.canvas.renderAll(); }); Is there any way to create a timer component in Fabricjs. Please help us with relevant information.
How to reverse the order of svg elements
I am working with a svg element which is following <svg class="layer1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 150 150"> <rect class="bg" id="bg" width="150" height="150" fill="#e6e6e6"></rect> <circle class="circ0" id="circ0" cx="75" cy="75" r="72" fill="none" stroke="blue" stroke-linecap="round" stroke-linejoin="round"></circle> <circle class="circ1" id="circ1" cx="75" cy="75" r="69" fill="none" stroke="green" stroke-linecap="round" stroke-linejoin="round"></circle> <circle class="circ2" id="circ2" cx="75" cy="75" r="66" fill="none" stroke="red" stroke-linecap="round" stroke-linejoin="round"></circle> <script href="index.js"></script> </svg> I want to reverse the order of these circles with javascript, which I am currently doing by this way const svg = document.querySelector("svg"); var x = document.querySelectorAll("[class^='circ']"); var bucket = []; x.forEach((a, i) => { bucket.push(a) }); bucket.reverse(); x.forEach( (a, i) => a.parentNode.removeChild(a) ); bucket.forEach( (a, i) => { a.setAttribute("class", 'circ' + [i]); a.setAttribute("id", "circ" + [i]); svg.appendChild(a); } ) <svg class="layer1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 150 150"> <rect class="bg" id="bg" width="150" height="150" fill="#e6e6e6"></rect> <circle class="circ0" id="circ0" cx="75" cy="75" r="72" fill="none" stroke="blue" stroke-linecap="round" stroke-linejoin="round"></circle> <circle class="circ1" id="circ1" cx="75" cy="75" r="69" fill="none" stroke="green" stroke-linecap="round" stroke-linejoin="round"></circle> <circle class="circ2" id="circ2" cx="75" cy="75" r="66" fill="none" stroke="red" stroke-linecap="round" stroke-linejoin="round"></circle> <script href="index.js"></script> </svg> It gives me this Is there a better way of doing this?
append(child) by itself moves DOM Nodes. So your code can be simplified. But for complexer SVG you probably want to swap DOM positions, because there could be other Elements in between you don't want to affect. Hold CTRL key to see what happens with append Click to see the swapping version, a matter of processing an Array and swapping the first with the last element. Note: append was not available in Internet Explorer, that is why you see most posts using appendChild. Modern browsers have loads more DOM goodies: replaceWith after , before etc. <svg viewBox="0 0 10 10" style="height:200px"> <style> text { font-size: 2px } [y="3"]{ fill:yellow } .first { stroke: black; stroke-width: 0.5 } </style> <rect class="bg" id="bg" width="10" height="10" fill="grey"></rect> <circle class="first" id="c0" cx="2" cy="5" r="2" fill="red" /> <text x="0" y="3">R</text> <circle class="second" id="c1" cx="4" cy="5" r="3" fill="green" /> <text x="1" y="3">G</text> <circle class="last" id="c2" cx="6" cy="5" r="4" fill="blue" /> <text x="2" y="3">B</text> <text x="1" y="6">Click Me!</text> </svg> <script> let svg = document.querySelector("svg"); function append() { [...svg.querySelectorAll("circle")] .reverse().forEach((c, i) => { c.parentNode.append(c); c.setAttribute("class", c.id = 'c' + i); }); } function swap() { function swapElements(e1, e2) { let {id,previousSibling,className:{baseVal:c2}} = e2; e1.after(e2); // put e2 after e1 e2.id = e1.id; e2.setAttribute("class", e1.getAttribute("class")); previousSibling.after(e1); // put e1 after where e2 WAS e1.id = id; e1.setAttribute("class", c2); } let circles = [...svg.querySelectorAll("circle")]; while (circles.length) { let c1 = circles.shift(); if (circles.length) swapElements(c1, circles.pop()) } } svg.onclick = (e) => (e.ctrlKey && append()) || swap(); </script>
how to change circle fill based on background color with javascript?
Basically I want the circle to invert color when the slider moves it across the flag (green when on white background, white when on green rect). function move() { let slider = document.querySelector("#slider"); let circle = document.querySelector("#player"); circle.setAttribute("cx", slider.value); } <h1>Flag</h1> <svg width="375" height="375"> <g id="flag"> <rect x="0" y="0" width="125" height="375" fill="green"/> <rect x="250" y="0" width="125" height="375" fill="green"/> </g> <circle id="player" cx="187.5" cy="187.5" r="10" fill="green" /> </svg><br> <input id="slider" type="range" min="0" max="375" value="150" style="width:370px" oninput="move()">
There isn't an easy way to do this with SVG filters or blend modes, etc. You probably have to just do the switch yourself. See below. function move() { let slider = document.querySelector("#slider"); let circle = document.querySelector("#player"); circle.setAttribute("cx", slider.value); circle.setAttribute('fill', (slider.value >= 125 && slider.value < 250) ? 'green' : 'white'); } <h1>Flag</h1> <svg width="375" height="375"> <g id="flag"> <rect x="0" y="0" width="125" height="375" fill="green"/> <rect x="250" y="0" width="125" height="375" fill="green"/> </g> <circle id="player" cx="187.5" cy="187.5" r="10" fill="green"/> </svg><br> <input id="slider" type="range" min="0" max="375" value="150" style="width:370px" oninput="move()">
How to change CSS class to an <use> children inside a SVG?
I am making and animation the objetive is change the xlink:href inside a SVG. (this is for change a shape), and change class respect to their position inside. This is my SVG <svg viewBox="-20 -20 600 200" id="main"> <defs id="test"> <rect width="80" height="80" id="circle" fill="red" class="first" /> <rect width="80" height="80" id="square" fill="pink" class="second" /> <rect width="80" height="80" id="cross" fill="blue" class="third" /> </defs> <g id="load-area"> <use x="0" xlink:href="#circle" /> <use x="100" xlink:href="#square" /> <use x="200" xlink:href="#cross" /> </g> </svg> The class in every rectelement, has a different animation-delay according to position (first execute at 0s, second at 2s, third at 4s and so on). With JS I change every <use> at #load-area main.children['load-area'].children[0].setAttribute("xlink:href", getFigure(random())); And it works, the shape changes but, suppose when it gets three times the id #cross then all elements have third CSS class. I need change CSS class inside every children of <use>, How can I do that? Below an element tree : I get all <use> with: main.children['load-area'].children but it does not have child element, as I show u below:
You can solve this using CSS variables that you combine with nth-child selector and you no more need the classes. Here is a basic example rect { animation:change 3s var(--d,0s) infinite; } #keyframes change { 0% { opacity:1; } 33%,100% { opacity:0; } } #load-area > use:nth-child(1) {--d:0s} #load-area > use:nth-child(2) {--d:1s} #load-area > use:nth-child(3) {--d:2s} /*#load-area > use:nth-child(N) {--d:Xs}*/ <svg viewBox="-20 -20 600 200" id="main"> <defs id="test"> <rect width="80" height="80" id="circle" fill="red" /> <rect width="80" height="80" id="square" fill="pink" /> <rect width="80" height="80" id="cross" fill="blue" /> </defs> <g id="load-area"> <use x="0" xlink:href="#circle" /> <use x="100" xlink:href="#square" /> <use x="200" xlink:href="#cross" /> </g> </svg> <svg viewBox="-20 -20 600 200" id="main"> <g id="load-area"> <use x="0" xlink:href="#square" /> <use x="100" xlink:href="#circle" /> <use x="200" xlink:href="#cross" /> </g> </svg> If the number is unknown or very big you can easily use a JS loop: var e = document.querySelectorAll('#load-area use'); for(var i=0;i<e.length;i++) { e[i].style.setProperty('--d',i+"s"); } rect { animation:change 3s var(--d,0s) infinite; } #keyframes change { 0% { opacity:1; } 33%,100% { opacity:0; } } <svg viewBox="-20 -20 600 200" id="main"> <defs id="test"> <rect width="80" height="80" id="circle" fill="red" /> <rect width="80" height="80" id="square" fill="pink" /> <rect width="80" height="80" id="cross" fill="blue" /> </defs> <g id="load-area"> <use x="0" xlink:href="#circle" /> <use x="100" xlink:href="#square" /> <use x="200" xlink:href="#cross" /> </g> </svg>
document.getElementsByTagName("use")[0].setAttribute("xlink:href", "#circle"); element.setAttribute(attributename, attributevalue) Here is how you can change element attributes
How to change the size of svg according to the output block?
I have to build this SVG and right now I am facing the issue is that it wasn't fitting on my page. I have to put this SVG in the column of my table and size of the column is fixed but my SVG is not fitting properly under that. It should fit in any column of the table and the size should be also perfectly fitted on that column. Size of the SVG should be dynamic. const SVG_NS = 'http://www.w3.org/2000/svg'; let colors = [ "rgb(255,0,0)", "rgb(255,0,0)", "rgb(255,128,0)", "rgb(255,128,0)", "rgb(255,128,0)", "rgb(255,128,0)", "rgb(255,128,0)", "rgb(102,204,0)", "rgb(102,204,0)", "rgb(102,178,255)" ]; let angle = Math.atan2(215,430); let n = 0; let offset = 10;// distance between the border of the triangle and the start of the rectangle for(let y = 40; y < 430; y+= 40){ let halfW = Math.tan(angle)*y - offset; let o = { x:430/2 - halfW, y: y, width: 2*halfW, height: 30, fill:colors[n] } drawRect(o, polys); n++; } function drawRect(o, parent) { let rect = document.createElementNS(SVG_NS, 'rect'); for (var name in o) { if (o.hasOwnProperty(name)) { rect.setAttributeNS(null, name, o[name]); } } parent.appendChild(rect); return rect; } .boxed { border: 1px solid black ; width: 500px; height: 130px; box-sizing: border-box; padding: 40px; <svg width="600" height="500"> <g transform="translate(0,40)"> <rect width="360" height="30" style="fill:rgb(255,0,0);" /> </g> <g transform="translate(0,80)"> <rect width="350" height="30" style="fill:rgb(255,0,0);" /> </g> <g transform="translate(0,120)"> <rect width="350" height="30" style="fill:rgb(255,128,0);" /> </g> <g transform="translate(0,160)"> <rect width="300" height="30" style="fill:rgb(255,128,0);" /> </g> <g transform="translate(0,200)"> <rect width="300" height="30" style="fill:rgb(255,128,0);" /> </g> <g transform="translate(0,240)"> <rect width="300" height="30" style="fill:rgb(255,128,0);" /> </g> <g transform="translate(0,280)"> <rect width="300" height="30" style="fill:rgb(255,128,0);" /> </g> <g transform="translate(0,320)"> <rect width="300" height="30" style="fill:rgb(102,204,0);" /> </g> <g transform="translate(0,360)"> <rect width="300" height="30" style="fill:rgb(102,204,0);" /> </g> <g transform="translate(0,400)"> <rect width="300" height="30" style="fill:rgb(102,178,255);" /> </g> <svg id="polys" height="500" width="500" viewBox="0 0 500 500" x="160" > <polygon points="215,0, 0,440 430,440 215,0" style="fill:rgb(255,255,204);stroke:black;stroke-width:2" /> </svg> </svg>
You need to change the width and height of the svg element with viewBox. This way the SVG element will take all the available space inside the parent container. However if your container is 500px wide and 130px height, the SVG will overflow the container. const SVG_NS = 'http://www.w3.org/2000/svg'; let colors = [ "rgb(255,0,0)", "rgb(255,0,0)", "rgb(255,128,0)", "rgb(255,128,0)", "rgb(255,128,0)", "rgb(255,128,0)", "rgb(255,128,0)", "rgb(102,204,0)", "rgb(102,204,0)", "rgb(102,178,255)" ]; let angle = Math.atan2(215,430); let n = 0; let offset = 10;// distance between the border of the triangle and the start of the rectangle for(let y = 40; y < 430; y+= 40){ let halfW = Math.tan(angle)*y - offset; let o = { x:430/2 - halfW, y: y, width: 2*halfW, height: 30, fill:colors[n] } drawRect(o, polys); n++; } function drawRect(o, parent) { let rect = document.createElementNS(SVG_NS, 'rect'); for (var name in o) { if (o.hasOwnProperty(name)) { rect.setAttributeNS(null, name, o[name]); } } parent.appendChild(rect); return rect; } .boxed { border: 1px solid black ; width: 500px; height: auto; box-sizing: border-box; padding: 40px; } svg{border:1px solid; display:block;} <div class="boxed"> <svg viewBox="0 0 600 500" height="130" > <g transform="translate(0,40)"> <rect width="360"viewBox="0 0 600 500" height="30" style="fill:rgb(255,0,0);" /> </g> <g transform="translate(0,80)"> <rect width="350" height="30" style="fill:rgb(255,0,0);" /> </g> <g transform="translate(0,120)"> <rect width="350" height="30" style="fill:rgb(255,128,0);" /> </g> <g transform="translate(0,160)"> <rect width="300" height="30" style="fill:rgb(255,128,0);" /> </g> <g transform="translate(0,200)"> <rect width="300" height="30" style="fill:rgb(255,128,0);" /> </g> <g transform="translate(0,240)"> <rect width="300" height="30" style="fill:rgb(255,128,0);" /> </g> <g transform="translate(0,280)"> <rect width="300" height="30" style="fill:rgb(255,128,0);" /> </g> <g transform="translate(0,320)"> <rect width="300" height="30" style="fill:rgb(102,204,0);" /> </g> <g transform="translate(0,360)"> <rect width="300" height="30" style="fill:rgb(102,204,0);" /> </g> <g transform="translate(0,400)"> <rect width="300" height="30" style="fill:rgb(102,178,255);" /> </g> <svg id="polys" height="500" width="500" viewBox="0 0 500 500" x="160" > <polygon points="215,0, 0,440 430,440 215,0" style="fill:rgb(255,255,204);stroke:black;stroke-width:2" /> </svg> </svg> </div> However if you try put