Parece que requestAnimationFrame
é a maneira de fato de animar as coisas agora. Na maioria das vezes, funcionou muito bem para mim, mas agora estou tentando fazer algumas animações de tela e fiquei imaginando: existe alguma maneira de garantir que ele funcione a um certo fps? Entendo que o objetivo do rAF é animações consistentemente suaves, e posso correr o risco de tornar minha animação instável, mas agora parece correr a velocidades drasticamente diferentes de maneira bastante arbitrária, e estou me perguntando se há uma maneira de combater de alguma forma.
Eu usaria, setInterval
mas quero as otimizações que o rAF oferece (especialmente parando automaticamente quando a guia está em foco).
Caso alguém queira ver meu código, é praticamente:
animateFlash: function() {
ctx_fg.clearRect(0,0,canvasWidth,canvasHeight);
ctx_fg.fillStyle = 'rgba(177,39,116,1)';
ctx_fg.strokeStyle = 'none';
ctx_fg.beginPath();
for(var i in nodes) {
nodes[i].drawFlash();
}
ctx_fg.fill();
ctx_fg.closePath();
var instance = this;
var rafID = requestAnimationFrame(function(){
instance.animateFlash();
})
var unfinishedNodes = nodes.filter(function(elem){
return elem.timer < timerMax;
});
if(unfinishedNodes.length === 0) {
console.log("done");
cancelAnimationFrame(rafID);
instance.animate();
}
}
Onde Node.drawFlash () é apenas um código que determina o raio com base em uma variável do contador e desenha um círculo.
fonte
requestAnimationFrame
é (como o nome sugere) solicitar um quadro de animação somente quando necessário. Digamos que você mostre uma tela preta estática, você deve obter 0 qps porque nenhum novo quadro é necessário. Mas se você estiver exibindo uma animação que requer 60fps, você também deve obtê-la.rAF
permite apenas "pular" quadros inúteis e salvar a CPU.Respostas:
Como acelerar requestAnimationFrame para uma taxa de quadros específica
Controle de demonstração a 5 FPS: http://jsfiddle.net/m1erickson/CtsY3/
Esse método funciona testando o tempo decorrido desde a execução do último loop de quadro.
Seu código de desenho é executado somente quando o intervalo FPS especificado tiver decorrido.
A primeira parte do código define algumas variáveis usadas para calcular o tempo decorrido.
E esse código é o loop requestAnimationFrame real que desenha no seu FPS especificado.
fonte
Atualização 2016/6
O problema da limitação da taxa de quadros é que a tela tem uma taxa de atualização constante, geralmente 60 FPS.
Se quisermos 24 FPS, nunca obteremos os verdadeiros 24 fps na tela, podemos cronometrar como tal, mas não mostrá-lo, pois o monitor só pode mostrar quadros sincronizados a 15 fps, 30 fps ou 60 fps (alguns monitores também 120 fps )
No entanto, para fins de tempo, podemos calcular e atualizar sempre que possível.
Você pode criar toda a lógica para controlar a taxa de quadros encapsulando cálculos e retornos de chamada em um objeto:
Em seguida, adicione algum controlador e código de configuração:
Uso
Torna-se muito simples - agora, tudo o que precisamos fazer é criar uma instância, definindo a função de retorno de chamada e a taxa de quadros desejada da seguinte maneira:
Em seguida, inicie (que pode ser o comportamento padrão, se desejado):
É isso aí, toda a lógica é tratada internamente.
Demo
Resposta antiga
O principal objetivo
requestAnimationFrame
é sincronizar atualizações com a taxa de atualização do monitor. Isso exigirá que você anime no FPS do monitor ou em um fator dele (ou seja, 60, 30, 15 FPS para uma taxa de atualização típica a 60 Hz).Se você deseja um FPS mais arbitrário, não faz sentido usar rAF, pois a taxa de quadros nunca corresponderá à frequência de atualização do monitor (apenas um quadro aqui e ali) que simplesmente não pode fornecer uma animação suave (como em todos os tempos de novo quadro) ) e você pode usar
setTimeout
ousetInterval
não.Esse também é um problema conhecido no setor de vídeo profissional quando você deseja reproduzir um vídeo em um FPS diferente do dispositivo em que ele é atualizado. Muitas técnicas têm sido usadas, como mesclagem de quadros e re-construção complexa de quadros intermediários com base em vetores de movimento, mas com o canvas essas técnicas não estão disponíveis e o resultado será sempre um vídeo irregular.
A razão pela qual colocamos em
setTimeout
primeiro lugar (e por que alguns colocam emrAF
primeiro lugar quando um preenchimento múltiplo é usado) é que isso será mais preciso, poissetTimeout
enfileirará um evento imediatamente quando o loop iniciar, para que, não importa quanto tempo o código restante use. (desde que não exceda o intervalo de tempo limite), a próxima chamada será no intervalo que representa (para rAF puro, isso não é essencial, pois o rAF tentará pular para o próximo quadro, em qualquer caso).Também vale a pena notar que colocá-lo em primeiro lugar também arriscará o empilhamento de chamadas, como acontece com
setInterval
.setInterval
pode ser um pouco mais preciso para esse uso.E você pode usar
setInterval
em vez fora do circuito para fazer o mesmo.E para parar o loop:
Para reduzir a taxa de quadros quando a guia fica embaçada, você pode adicionar um fator como este:
Dessa forma, você pode reduzir o FPS para 1/4, etc.
fonte
requestAnimationFrame
é sincronizar as operações do DOM (leitura / gravação), portanto, não usá-lo prejudicará o desempenho ao acessar o DOM, pois as operações não serão enfileiradas para serem executadas juntas e forçarão a repintura de layout desnecessariamente.Sugiro encerrar sua chamada
requestAnimationFrame
em umsetTimeout
. Se você chamarsetTimeout
de dentro da função da qual solicitou o quadro de animação, estará perdendo o objetivo derequestAnimationFrame
. Mas se você ligarrequestAnimationFrame
de dentrosetTimeout
, funciona sem problemas:fonte
Todas essas são boas idéias em teoria, até você se aprofundar. O problema é que você não pode estrangular um RAF sem dessincronizá-lo, derrotando seu objetivo de existir. Assim, você o deixa rodar em velocidade máxima e atualiza seus dados em um loop separado , ou mesmo em um thread separado!
Sim, eu disse. Você pode fazer JavaScript multiencadeado no navegador!
Sei que existem dois métodos que funcionam extremamente bem sem brincadeiras, usando muito menos suco e criando menos calor. O tempo preciso em escala humana e a eficiência da máquina são o resultado líquido.
Desculpas se isso é um pouco prolixo, mas aqui vai ...
Método 1: Atualize dados via setInterval e gráficos via RAF.
Use um setInterval separado para atualizar os valores de conversão e rotação, física, colisões etc. Mantenha esses valores em um objeto para cada elemento animado. Atribua a sequência de transformação a uma variável no objeto cada 'frame' setInterval. Mantenha esses objetos em uma matriz. Defina seu intervalo para os fps desejados em ms: ms = (1000 / fps). Isso mantém um relógio constante que permite os mesmos fps em qualquer dispositivo, independentemente da velocidade RAF. Não atribua as transformações aos elementos aqui!
Em um loop requestAnimationFrame, itere através de sua matriz com um loop for old-school - não use os formulários mais recentes aqui, eles são lentos!
Na sua função rafUpdate, obtenha a string de transformação do seu objeto js na matriz e o ID dos seus elementos. Você já deve ter seus elementos de 'sprite' anexados a uma variável ou facilmente acessíveis por outros meios, para não perder tempo 'obtendo-os' na RAF. Mantê-los em um objeto com o nome de seus IDs html funciona muito bem. Configure essa peça antes mesmo de entrar no seu SI ou RAF.
Use o RAF para atualizar apenas suas transformações , use apenas transformações 3D (mesmo para 2d) e defina o css "will-change: transform;" em elementos que mudarão. Isso mantém suas transformações sincronizadas com a taxa de atualização nativa o máximo possível, ativa a GPU e informa ao navegador onde se concentrar mais.
Então você deve ter algo como este pseudocódigo ...
Isso mantém suas atualizações nos objetos de dados e as seqüências de transformação sincronizadas com a taxa de 'quadro' desejada no SI, e as atribuições de transformação reais no RAF sincronizadas com a taxa de atualização da GPU. Portanto, as atualizações gráficas reais estão apenas no RAF, mas as alterações nos dados e a construção da cadeia de transformação estão no SI, portanto, não há bobagens, mas o 'tempo' flui na taxa de quadros desejada.
Fluxo:
Método 2. Coloque o SI em um trabalhador da Web. Este é FAAAST e suave!
Igual ao método 1, mas coloque o SI no web-worker. Ele será executado em um segmento totalmente separado, deixando a página para lidar apenas com o RAF e a interface do usuário. Passe a matriz de sprites para frente e para trás como um 'objeto transferível'. Isso é buko rápido. Não leva tempo para clonar ou serializar, mas não é como passar por referência, pois a referência do outro lado é destruída; portanto, você precisará que os dois lados passem para o outro lado e os atualize apenas quando presentes, classifique de passar uma nota para frente e para trás com sua namorada no ensino médio.
Somente um pode ler e escrever de cada vez. Isso é bom desde que verifique se não está indefinido para evitar um erro. O RAF é RÁPIDO e o retrocede imediatamente, passando por vários quadros de GPU, verificando se já foi enviado de volta. O SI no trabalhador da Web terá o conjunto de sprites na maior parte do tempo e atualizará os dados posicionais, de movimento e físicos, além de criar a nova sequência de transformação, depois passará para a RAF na página.
Essa é a maneira mais rápida que conheço de animar elementos via script. As duas funções serão executadas como dois programas separados, em dois threads separados, aproveitando as CPUs de vários núcleos de uma maneira que um único script js não. Animação em javascript multiencadeada.
E será feito sem problemas, mas na taxa de quadros especificada real, com muito pouca divergência.
Resultado:
Qualquer um desses dois métodos garantirá que o script seja executado na mesma velocidade em qualquer PC, telefone, tablet etc. (dentro dos recursos do dispositivo e do navegador, é claro).
fonte
visibilitychange
evento.Como acelerar facilmente para um FPS específico:
Fonte: Uma explicação detalhada dos intervalos e tempo dos jogos em JavaScript de Isaac Sukin
fonte
Ignorar requestAnimationFrame não causa animação suave (desejada) em fps personalizados.
Código original de @tavnab.
fonte
fonte
Eu sempre faço isso de maneira muito simples, sem mexer nos carimbos de data e hora:
fonte
Aqui está uma boa explicação que encontrei: CreativeJS.com , para agrupar uma chamada setTimeou) dentro da função passada para requestAnimationFrame. Minha preocupação com uma requestAnimationFrame "simples" seria "e se eu apenas desejar animar três vezes por segundo?" Mesmo com requestAnimationFrame (ao contrário de setTimeout) é que ele ainda desperdiça (parte) uma quantidade de "energia" (o que significa que o código do navegador está fazendo algo e possivelmente diminuindo a velocidade do sistema) 60 ou 120 ou, no entanto, muitas vezes por segundo, como em vez de apenas duas ou três vezes por segundo (como desejar).
Na maioria das vezes, eu executo meus navegadores com JavaScript desativado por esse motivo. Mas estou usando o Yosemite 10.10.3 e acho que há algum tipo de problema com o temporizador - pelo menos no meu sistema antigo (relativamente antigo - ou seja, 2011).
fonte