1

I have a task to make an SVG rotate in IE9.

I found the FakeSmile library which makes it rotate, but after the whole DOM is ready, which is not the behavior I want. I made an attempt to do it manually with JavaScript and ended with this code:

//init an array with values from 0 to 360 for degrees 
var degrees = [];
for(var i = 0; i <= 360; i++) {
    degress.push(i);
}
// function to rotate it, after it's fetched from the DOM
var rotate = function() {
    var deg = degrees.shift();
    element.style.msTransform = "rotate(" + deg + "deg)";
    degrees.push(deg);         
}
setInterval(rotate, 7);

Though it is working, I am worried if any performance hit will occur. Also if there is a better solution. Any suggestions are welcomed.

Rory O'Kane
  • 29,210
  • 11
  • 96
  • 131
Dimitar
  • 160
  • 1
  • 1
  • 10
  • https://stackoverflow.com/questions/14859322/css3-spin-animation – TheCrzyMan Jan 04 '18 at 13:26
  • 1
    css "keyframe" start support at IE10 :( – Dimitar Jan 04 '18 at 13:30
  • 1
    You can store the `angle` and then do a modulus operation combined with an increment. `angle = (++angle)%360`. That will iterate through all the angles and keep it less than 360. I would go with CSS if you are just trying to spin it though – TheCrzyMan Jan 04 '18 at 13:30
  • Your code here says `degress.push(i)` instead of `degrees.push(i)`. I assume this typo is only in your Stack Overflow question since you said your code is working. – Rory O'Kane Jan 04 '18 at 16:34

2 Answers2

1

A creator function and organized objects would be a good start. Remember you shouldn't pollute the global namespace if you can avoid it.

Also de-bounce request and animation. A request every 7 millisecond is two request per frame on a 60fps screen (the most common) and there is no need to calculate and throw away frames the user never sees.

In my example i use requestAnimationFrame because that will synchronize with the screens refreshrate. On every request i check if the handle is already drawing a frame and if it isn't i schedule a frame drawing.

Notice that you can still set JavaScript variables every 7 millisecond. It's just the DOM that's slows.

EDIT 1 - No requestAnimationFrame in IE9

My mistake about requestAnimationFrame, but de-bounce is still a good idea. With de-bounce, several factors can request a change and it will still only render when relevant.

I have replaced requestAnimationFrame with setTimeout(.... 1000/60) for close to 60 fps animation.

function createRotator(element) {
  var rotator;
  rotator = {
    degrees: 0,
    element: element,
    eventHandle: false,
    rotate: function rotate() {
      rotator.degrees = (rotator.degrees + 1) % 360;
      if (rotator.eventHandle === false)
        rotator.eventHandle = setTimeout(function() {
          rotator.element.style.transform = "rotate(" + rotator.degrees + "deg)";
          rotator.element.style.msTransform = "rotate(" + rotator.degrees + "deg)";
          rotator.eventHandle = false;
        }, 1000 / 60);
    }
  };
  return rotator;
}
//TEST
var nodes = 0;
var handle;
handle = setInterval(function() {
  nodes++;
  if (nodes > 10) {
    clearInterval(handle);
  }
  var testNode = document.body.appendChild(document.createElement("p"));
  testNode.innerHTML = "Hello dear World!";
  testNode.style.width = "115px";
  testNode.style.cssFloat = "left";
  testNode.style.marginTop = "100px";
  var rotator = createRotator(testNode);
  setInterval(rotator.rotate, 3);
}, 1000 / 4);
Emil S. Jørgensen
  • 6,216
  • 1
  • 15
  • 28
  • Good point about 7 ms being too short and about organizing the code, but `requestAnimationFrame` doesn’t exist in IE9. I guess they will have to use `setInterval(rotator.rotate, 16)` for 16 ms. – Rory O'Kane Jan 04 '18 at 16:37
  • Yikes, I hadn't even thought about the interval being faster than the refresh rate. Good call, that will most likely be the most to @Dimitar 's benefit as far as performance goes. +1 from me – TheCrzyMan Jan 04 '18 at 17:30
0

Yeah, with IE9, you're out of luck on CSS animations. My only suggestion would be a memory optimization

//init a variable to store the current angle
let angle = 0;

// function to rotate it
function rotate() {
    angle = (++angle)%360;
    element.style.msTransform = "rotate(" + angle+ "deg)";      
}
setInterval(rotate, 7);

This design change also lets you change the speed of the rotation on the fly without changing the interval length. All you would change is ++angle to angle + w where w is the angular velocity.

What is also unfortunate is that you can't use requestAnimationFrame instead of an interval. Oh well. It's not the end of the world.

EDIT: It was bugging me that the function was relying so heavily on global variables. So, here is a slightly "better", though heavier, way of doing it.

/** Takes in an element, an angular velocity, and an interval, and makes the element spin in IE9
PARAMS:
    element  : Element - The element we are spinning
    da       : Number  - The angular velocity in degrees per interval
    interval : Number  - The number of milliseconds per interval
RETURNS:
    Number - The ID of the interval that is created
**/
function makeRotate(element, da, interval){
    // Variable to store angle
    let a = 0;

    // If da isn't provided, make it 1
    da = da || 1;

    // If interval isn't provided, make it 7
    interval = interval || 7;

    // Get the ID and make the interval
    let id = window.setInterval(() => {

        // Increment the angle by the angular velocity, but wrap around 360
        a = (a + da)%360;

        // Apply the transform to the element
        element.style.msTransform = "rotate(" + a + "deg)";      
    }, interval);

    // Return the ID of the interval
    return id;
}
const intervalId = makeRotate(element, 1, 7);

Also, I made sure to return the interval id because it is always handy to be able to cancel those suckers! window.clearInterval(intervalId);

TheCrzyMan
  • 1,010
  • 1
  • 11
  • 25