Javascript requestAnimationFrame spinner

After learning all the possible ways to create an easy and flexible counter, I ended up using requestAnimationFrame , which is pretty brilliant. It basically does the same thing as CSS3 animation : performs calculations and passes the result to the browser in order to synchronize the redrawing with the redrawing of the screen (usually at 60 frames per second). Although CSS3 transition and animation are suitable for very simple use, since there is only a transitionend event that may not fire under certain circumstances, requestAnimationFrame offers full control, and you can perform many complex calculations perfectly in sync with the screen redrawing.

Would it be worthwhile to surpass this code in a working HTML5 document?

CSS

 i.spinner {position:relative;display:inline-block;margin:20px} i.bar {display:block;position:absolute;top:0;left:50%;height:inherit} i.bar i {display:block;width:100%;height:29%;background:#000} i.bar:nth-child(2) {transform:rotate(45deg);-webkit-Transform:rotate(45deg);-moz-Transform:rotate(45deg);-ms-Transform:rotate(45deg)} i.bar:nth-child(3) {transform:rotate(90deg);-webkit-Transform:rotate(90deg);-moz-Transform:rotate(90deg);-ms-Transform:rotate(90deg)} i.bar:nth-child(4) {transform:rotate(135deg);-webkit-Transform:rotate(135deg);-moz-Transform:rotate(135deg);-ms-Transform:rotate(135deg)} i.bar:nth-child(5) {transform:rotate(180deg);-webkit-Transform:rotate(180deg);-moz-Transform:rotate(180deg);-ms-Transform:rotate(180deg)} i.bar:nth-child(6) {transform:rotate(225deg);-webkit-Transform:rotate(225deg);-moz-Transform:rotate(225deg);-ms-Transform:rotate(225deg)} i.bar:nth-child(7) {transform:rotate(270deg);-webkit-Transform:rotate(270deg);-moz-Transform:rotate(270deg);-ms-Transform:rotate(270deg)} i.bar:nth-child(8) {transform:rotate(315deg);-webkit-Transform:rotate(315deg);-moz-Transform:rotate(315deg);-ms-Transform:rotate(315deg)} 

Js

 function buildspinner(size, invert) { var color = '#000', spinner = document.createElement('i'), bar = document.createElement('i'), hand = document.createElement('i'), opacitymap = [0.8, 0.2, 0.2, 0.2, 0.2, 0.5, 0.6, 0.7], nodemap = []; if (invert) {color = '#fff'}; spinner.className = 'spinner'; spinner.style.cssText = 'width:' + size + 'px;height:' + size + 'px'; bar.className = 'bar'; bar.style.cssText = 'width:' + (size / 9) + 'px;height:' + size + 'px;margin-left:' + (-size / 18) + 'px'; hand.style.cssText = 'border-radius:' + size + 'px;background:' + color; bar.appendChild(hand); for (var j = 0; j < 8; j++) { var clone = bar.cloneNode(true); clone.style.opacity = opacitymap[j]; spinner.appendChild(clone); nodemap.push(clone) } document.body.appendChild(spinner); requestAnimationFrame(function(timestamp) {animatespinner(timestamp, timestamp, 125, opacitymap, nodemap, 0)}) } function animatespinner(starttime, timestamp, duration, opacitymap, nodemap, counter) { var progress = (timestamp - starttime) / duration; counter++; if (counter % 3 == 0) { for (var j = 0; j < 8; j++) { var next = j - 1; if (next < 0) { next = 7 }; nodemap[j].style.opacity = (opacitymap[j] + (opacitymap[next] - opacitymap[j]) * progress) } } if (progress < 1) { requestAnimationFrame(function(timestamp) {animatespinner(starttime, timestamp, 125, opacitymap, nodemap, counter)}) } else { var rotatearray = opacitymap.pop(); opacitymap.unshift(rotatearray); requestAnimationFrame(function(timestamp) {animatespinner(timestamp, timestamp, 125, opacitymap, nodemap, 0)}) } } 

The counter variable is used for throttling. You want the animation to be smooth, but you want the CPU to be low. In this example, we change the opacity every 3 frames instead of each frame, greatly reducing the CPU load with a noticeable effect on smoothness. (CPU usage decreased from 12% to 5% on the Quadcore 3GHz processor).

Since CSS3 animation relies on keyframes , you will need to create a separate keyframe for each side of the counter, which will lead to too much computation. The same counter created using CSS3 animations resulted in 30% CPU utilization.

Demo

+4
source share
2 answers

The point of requestAnimationFrame is that it is effectively called by the browser as close to 60 frames per second as possible or regardless of the frame rate of the browser animation mechanism, and therefore you should not do the work in requestAnimationFrame , which takes longer than the frame time. Keep in mind that javascript runs very fast in a browser ... it takes a very complicated amount of javascript calculations to actually take more time than executing a frame. The main problems you will encounter are the layout, drawing and redrawing of elements on the screen. And for this, the web worker will not help you. The web worker will only help you if you have really heavy javascript, which will take longer than executing the frame.

Too easy to profile ... You can look in the Chrome history tool to see how long your javascript function takes to execute. Most likely, this is only 1 ms max, and if your animation works at a speed of less than 60 frames per second, this is due to the fact that the layout and repainting take longer than the remaining 16.7 ms in frame time, but in the mock browser, not something that you can unload through the webworker anyway.

+2
source

This code shows how long the browser takes only to send and receive a message to the employee. In my car, it takes about 3 ms. You need to save each frame of your JS for less than 10 ms if you want to reach 60 frames per second (remember that the browser still needs the style, layout, drawing and layout of each frame).

 var myWorker, send = document.querySelector('.send'), receive = document.querySelector('.receive'), time = document.querySelector('.time'), start, end; var sendMessage = function () { start = performance.now(); myWorker.postMessage('My message'); console.log('Sending message to worker ' + start); }; var receiveMessage = function(event) { end = performance.now(); time.textContent = (end - start) + 'ms'; receive.textContent = event.data; console.log('Message received from worker ' + end); }; var workerFunction = function(event) { self.postMessage('Worker response: ' + event.data); }; var createWorker = function () { if (window.Worker && window.Blob && window.URL) { var workerContent = "self.onmessage = " + workerFunction.toString(); var blob = new Blob([workerContent], {type: 'application/javascript'}); myWorker = new Worker(URL.createObjectURL(blob)); myWorker.onmessage = receiveMessage; } }; createWorker(); send.addEventListener('click', sendMessage); 
 <button class="send">Send</button> <p class="receive"></p> <p class="time"></p> 
+2
source

All Articles