Reputation: 133
I've got the following svg line:
<svg height="100%" width="100%">
<line id="skyline" x1="50%" y1="50%" x2="50%" y2="90%" style="stroke:rgb(0,0,0);stroke-width:10" />
</svg>
Trying to rotate the line multiple times seems to add many rotate(X) to the transform attribute, not simply overriding the value every time:
var skyline = document.getElementById("skyline");
for (i = 0; i < 100; i++) {
var rotation = skyline.getAttribute("transform") + i;
skyline.setAttribute("transform", "rotate(" + rotation + ")");
}
How can I properly get the rotate attribute, and how can I properly override in multiple times?
Upvotes: 1
Views: 1247
Reputation: 101938
Assuming your line doesn't already have a transform
, you can just do:
var skyline = document.getElementById("skyline");
for (i = 0; i < 100; i++) {
skyline.setAttribute("transform", "rotate(" + i + ")");
}
But that doesn't work as is because you aren't giving the browser a chance to render the updated SVG. Also it would run too fast even if it did update.
So you need to use an interval, timeout or requestAnimationFrame()
.
var i = 0;
function rotate() {
skyline.setAttribute("transform", "rotate(" + (i++) + ")");
if (i < 100)
setTimeout(rotate, 100);
}
setTimeout(rotate, 100);
If your elements could already have a transform, then you will need to get the current transform and manipulate it. For example:
var i = 0;
function rotate() {
var matrixList = skyline.transform.baseVal;
if (matrixList.length === 0) {
skyline.setAttribute("transform", "rotate(" + (i++) + ")");
} else {
var firstTransform = matrixList.getItem(0);
firstTransform.setMatrix(firstTransform.matrix.rotate(1));
i++;
}
if (i < 100)
setTimeout(rotate, 100);
}
setTimeout(rotate, 100);
var skyline = document.getElementById("skyline");
var i = 0;
function rotate() {
var matrixList = skyline.transform.baseVal;
if (matrixList.length === 0) {
skyline.setAttribute("transform", "rotate(" + (i++) + ")");
} else {
var firstTransform = matrixList.getItem(0);
firstTransform.setMatrix(firstTransform.matrix.rotate(1));
i++;
}
if (i < 100)
setTimeout(rotate, 100);
}
setTimeout(rotate, 100);
<svg height="100%" width="100%">
<line id="skyline" x1="50%" y1="50%" x2="50%" y2="90%" style="stroke:rgb(0,0,0);stroke-width:10" transform="rotate(-10)"/>
</svg>
Upvotes: 1
Reputation: 2829
Using: https://css-tricks.com/get-value-of-css-rotation-through-javascript/
function getRotate(el) {
var st = window.getComputedStyle(el, null);
var tr = st.getPropertyValue("-webkit-transform") ||
st.getPropertyValue("-moz-transform") ||
st.getPropertyValue("-ms-transform") ||
st.getPropertyValue("-o-transform") ||
st.getPropertyValue("transform") ||
"fail...";
var values = tr.split('(')[1];
values = values.split(')')[0];
values = values.split(',');
var a = values[0];
var b = values[1];
var c = values[2];
var d = values[3];
var scale = Math.sqrt(a * a + b * b);
// arc sin, convert from radians to degrees, round
var sin = b / scale;
// next line works for 30deg but not 130deg (returns 50);
// var angle = Math.round(Math.asin(sin) * (180/Math.PI));
var angle = Math.round(Math.atan2(b, a) * (180 / Math.PI));
return angle
}
setInterval(rotate, 100);
function rotate() {
var rotation = getRotate(document.getElementById("skyline")) + 5;
console.log(rotation);
skyline.setAttribute("style", "stroke:rgb(255,0,0);stroke-width:2;transform: rotate(" + rotation + "deg);transform-origin: center");
}
<svg height="100%" width="100%">
<line id="skyline" x1="80" y1="20" x2="100" y2="40" style="stroke:rgb(255,0,0);stroke-width:2;transform: rotate(30deg);transform-origin: center" />
</svg>
Upvotes: 0
Reputation: 45106
Changing the same attribute in synchronous loop doesn't make sense. If you want some sort of an animation you need to introduce time delay. The most straight forward way is to use setTimeout
var skyline = document.getElementById("skyline");
var angle = getOriginalAngle(),
finalAngle = angle + 100;
function rotate() {
skyline.setAttribute("transform", "rotate(" + (angle++) + "deg)");
(angle < finalAngle) && setTimeout(rotate, 12); // repeat every 12 ms
}
rotate(); // launch animation
UPD Or if you want your changes to be in sync with browser rendering loop you can use requestAnimationFrame
function rotate() {
skyline.setAttribute("transform", "rotate(" + (angle++) + "deg)");
(angle < finalAngle) && requestAnimationFrame(rotate); // +- 60 fps
}
Upvotes: 2
Reputation: 56
Greensock is a great extension for CSS and JS which allows you to manipulate SVG's based on id's or classnames of the element in question:
Here's an example:
TweenMax.staggerTo('.skyline', 2, {rotation: -90, repeat:-1, yoyo:true});
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/1.18.5/TweenMax.min.js"></script>
<svg height="100%" width="100%">
<line class="skyline" x1="50%" y1="50%" x2="50%" y2="90%" style="stroke:rgb(0,0,0);stroke-width:10" />
</svg>
The best part is it will run in IE as well as being simple, easy to implement and easy to understand and well documented! It can be set to run automatically on page load, or when an event is fired such as a hover etc etc. you can choose whether it runs indefinitely or how many times you want it to run, and if you want it to have a yoyo effect or not (animation to run back and forth)
Upvotes: 1