Lorsque j'ai récemment développé l'applet WeChat, je voulais créer une fonction d'animation (c'est-à-dire un effet de parallaxe à détection de gravité) qui fait que les nœuds de la page suivent le mouvement en détectant la direction du téléphone mobile. Il s'est avéré que l'applet WeChat a quelques trous:
L'applet WeChat ne prend pas en charge l' API du capteur de gravité HTML5 DeviceOrientationEvent , mais le wx.onAccelerometerChange implémenté par lui-même
Ce rappel API est implémenté avec une fréquence de 5 fois / s
Dans ce contexte, obtenir une expérience de parallaxe de détection de gravité fluide est si élégant, car les gens se sentent bien au moins 60 images par seconde d'animation. L'effet final sera le bégaiement.
Pendant l'implémentation, je pense qu'il y a requestAnimationFrame
cette API liée à l'animation, et ses performances fonctionnelles sont setTimeout
similaires, c'est-à-dire qu'une fonction de rappel est appelée à intervalles. Pour cette API, je n'en savais pas grand-chose auparavant, et cette fois je l'ai ramassée et j'ai posé une question: à la fois né setTimeout
et né requestAnimationFrame
? Avec des questions, commencez la recherche.
Différent de setTimeout
Sur MDN, requestAnimationFrame
la définition est:
window.requestAnimationFrame () Cette méthode est utilisée pour informer le navigateur d'appeler une fonction spécifiée avant que la page ne soit redessinée pour répondre aux besoins du développeur pour faire fonctionner l'animation. Cette méthode accepte une fonction comme paramètre, qui sera appelée avant d'être redessinée.
Ici, j'ai une question. Le soi-disant " avant redessiner la page " se réfère à ce que cette fonction spécifiée (ci-après dénommée cb) sera appelée avant la redessiner la page sous le fonctionnement du mécanisme sous-jacent, ou s'il faut définir manuellement un Temps d'intervalle, pour appeler cb, provoquant un redessin?
J'ai probablement déjà entendu parler du réarrangement et du redessinage des pages. L'animation peut être mise en œuvre par réarrangement et redessiner (ici, cela signifie que ces deux façons peuvent être utilisées pour atteindre des objectifs d'animation, les deux ne conviennent pas à la mise en œuvre de l'animation), et la définition mentionne uniquement le redessin, sans mentionner La raison du réarrangement, bien que je ne sois pas entré dans les détails, il est très important car les performances de redessin sont beaucoup plus élevées que le réarrangement. L'animation ne doit donc pas être réalisée par left, margin, etc., mais par translate attribute.
Depuis parler de translate, une petite extension, si vous voulez utiliser translate pour l'animation, il est préférable d'utiliser translate3d. Parce que par rapport à tranlate, tranlate3d peut être pris en charge par une accélération GPU plus complète , résultant en de meilleures performances.
Pour revenir à la vérité, continuez de résoudre le doute tout à l'heure. Lisez la suite et trouvez une telle explication:
Si vous souhaitez faire une animation image par image, vous devez utiliser cette méthode. Cela nécessite que l'exécution de votre fonction d'animation redessine l'action avant le navigateur. D'une manière générale, la fréquence est appelée 60 fois par seconde, mais suivra généralement la fréquence prescrite par la norme W3C. S'il s'agit d'une page à onglet d'arrière-plan, la fréquence de rafraîchissement sera considérablement réduite.
Comme vous pouvez le voir dans ce passage, après avoir utilisé cette méthode, le navigateur appellera cb avant chaque redessin selon sa propre fréquence de redessin. Vérifiez avec le code suivant:
var laststart
function test () {
laststart && console.log(Date.now() - laststart)
laststart = Date.now()
requestAnimationFrame(test)
}
requestAnimationFrame(test)
Pour obtenir le résultat, la fréquence de rafraîchissement de mon environnement de navigateur (mac + chrome [55.0.2883.95]) est d'environ 60 fois par seconde:
J'ai quelques questions à ce sujet:
Question 1: Si j'interfère avec la fréquence de redessin, sera-ce toujours une fréquence qui reste à près de 60 images par seconde (uniquement pour augmenter la fréquence)?
Question 2: L'appel de requestAnimationFrame produit-il une certaine fréquence de redessin?
Question 3: Si requestAnimationFrame n'est pas appelé et que la page est redessinée sans autre code, la page ne sera-t-elle pas redessinée?
Pour vérifier la question 1, j'ai ajouté ce code:
var x = 1
var style = document.querySelectorAll('.test')[0].style
function interference () {
x *= -1
style.transform = `translate3d(${x * 20}px, 0 ,0)`
setTimeout(interference, 5)
}
interference()
Le résultat obtenu est cohérent avec le résultat précédent.
On en conclut que la fréquence d'appel de cb est toujours seule dans le cas d'une fréquence de redessin d'interférence artificielle.
Attendez, la fréquence de rafraîchissement des interférences artificielles a-t-elle réussi? La interference
fréquence d'appel sera-t-elle 5 ms une fois, mais la fréquence de redessin du navigateur est toujours environ 60 fois par seconde, c'est-à-dire, interference
bien qu'essayant de déclencher le navigateur pour redessiner une fois 5 ms, mais le navigateur ne bloquera, attendez la prochaine fois Lorsque la fréquence par défaut du navigateur se redessine, puis redessine ensemble?
Afin d'explorer ce problème, j'ai défini interference
le temps setTimeout sur 16 ms (appelé i16), 10 ms (appelé i10), 8 ms (appelé i8), 5 ms (appelé i5), si la fréquence de redessin du navigateur ne peut pas être perturbée manuellement , Pour les deux raisons suivantes:
interference
Le$('.test')
changement de la paire de fonctions est que le déplacement horizontal alterne avec plus ou moins 20pxLa fréquence de rafraîchissement par défaut du navigateur est proche de 16 ms une fois
i8 en raison de 16ms est appelé deux fois, ce qui rend le $('.test')
retour in situ causée par les yeux nus $('.test')
fréquence de clignotement lent, et appeler i16 fréquence et redessiner fréquence la plus proche, dans ce cas, l'œil nu peut voir $('.test')
clignoter La fréquence sera la plus rapide.
Les résultats de l'œil nu pour voir la fréquence de clignotement de haut en bas est: i16 > i10、i5 > i8
.
Par conséquent, il est conclu qu'avec la méthode ci-dessus, il est impossible d'interférer artificiellement avec la fréquence de rafraîchissement par défaut du navigateur .
Existe-t-il un moyen de définir la fréquence de rafraîchissement du navigateur? Aucune réponse directe n'a été trouvée, mais dans l' explication détaillée de la gestion des performances de la page Web de Ruan Yifeng , il est mentionné que:
La fréquence de rafraîchissement de la plupart des moniteurs est de 60 Hz. Afin d'être cohérent avec le système et d'économiser de l'énergie, le navigateur rafraîchira automatiquement l'animation en fonction de cette fréquence.
Prouver que la fréquence de rafraîchissement du navigateur et le taux de rafraîchissement de l'affichage sont égaux. Par conséquent, il ne devrait pas être possible de définir directement la fréquence de rafraîchissement du navigateur. Après tout, la page est redessinée, mais l'affichage n'est pas actualisé, ce qui affecte les performances mais n'a aucun effet.
En fait, grâce à la fonction de chronologie dans les propres outils de développement de Chrome, vous pouvez clairement voir que peu importe la façon dont l'intervalle de la méthode ci-dessus est défini, cela ne changera pas la fréquence de rafraîchissement:
Avec cet outil, les deux problèmes restants sont résolus:
La réponse à la question 2: Lorsque requestAnimationFrame est appelée, une certaine fréquence de redessin sera générée, mais le redessin dans ce cas sera extrêmement court car il n'y a pas de contenu de redessin substantiel.
Réponse à la question 3: Si vous n'appelez pas requestAnimationFrame, s'il n'y a pas d'autre code pour redessiner la page, la page ne sera pas redessinée
En étudiant cette étape, j'ai trouvé que requestAnimationFrame et setTimeout ne sont pas du tout la même chose. requestAnimationFrame est une méthode appelée en fonction de la fréquence de rafraîchissement du navigateur, et setTimeout est une minuterie. Avec des définitions différentes, les scénarios applicables sont également complètement différents et il n'y a aucune différence de performances.
La compatibilité
La compatibilité de requestAnimationFrame donnée par MDN est la suivante:
En d'autres termes, requestAnimationFrame doit avoir des problèmes de compatibilité. Un traitement de déclassement est donc également nécessaire. Voici le code de déclassement:
;(function() {
var lastTime = 0;
// 兼容各种浏览器
var vendors = ['ms', 'moz', 'webkit', 'o'];
for(var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) {
window.requestAnimationFrame = window[vendors[x]+'RequestAnimationFrame'];
window.cancelAnimationFrame = window[vendors[x]+'CancelAnimationFrame'] || window[vendors[x]+'CancelRequestAnimationFrame'];
}
// 降级处理
if (!window.requestAnimationFrame) {
window.requestAnimationFrame = function(callback, element) {
// 保证如果重复执行callback的话,callback的执行起始时间相隔16ms
var currTime = new Date().getTime();
var timeToCall = Math.max(0, 16 - (currTime - lastTime));
var id = window.setTimeout(function() { callback(currTime + timeToCall); },
timeToCall);
lastTime = currTime + timeToCall;
return id;
};
}
if (!window.cancelAnimationFrame) {
window.cancelAnimationFrame = function(id) {
clearTimeout(id);
};
}
}());
Les performances de requestAnimationFrame dans le programme WeChat Mini
L'applet de version WeChat iOS ne prend pas du tout en charge requestAnimationFrame
Conclusion
requestAnimationFrame et setTimeout ne sont pas du tout la même chose, selon sa définition, ils peuvent être utilisés dans différents scénarios.
Comparé à tranlate, tranlate3d peut obtenir un support d'accélération GPU plus complet.
Le navigateur a une fréquence maximale par défaut pour redessiner la page. La fréquence maximale ne peut pas être définie manuellement ni nécessaire.
L'appel de requestAnimationFrame produira une certaine fréquence de redessin, mais le redessin dans ce cas sera très court car il n'y a pas de contenu de redessin substantiel.
Si vous n'appelez pas requestAnimationFrame, la page ne sera pas redessinée s'il n'y a pas d'autre code pour redessiner la page.
Enfin, joignez le code QR du petit programme de notre groupe de magasins amusants: