移动鼠标时Firefox中的生涩javascript动画
Posted
技术标签:
【中文标题】移动鼠标时Firefox中的生涩javascript动画【英文标题】:Jerky javascript animation in Firefox when moving the mouse 【发布时间】:2021-05-31 11:21:10 【问题描述】:我是一名物理学家,我开始学习 JS 是为了好玩。我正在尝试编写一个钟摆的动画。当我在 Firefox 中运行它时,当我仅仅移动鼠标时它会变得非常生涩,但是当我在 Edge 或 Chrome 中运行它时,问题不会发生。除了知道一点 C++ 之外,我还是个菜鸟!我试图在网上阅读,但找不到答案。我在下面添加了一个最小的示例。为什么会发生这种情况,我该如何阻止它?
我实际上还添加了一个函数(未包含在下面的示例中),它计算主循环的后续重复之间经过的时间,并基于此显示丢帧数。当我运行该程序 10-20 秒并且我不断移动鼠标时,在 Edge 或 Chrome 中只有 5-6 个丢帧。在 Firefox 中有超过 100 个
Edit 04.03.2021:这是带有 fps 计数器的代码。最后一行用于显示丢帧数:连续 1、2、3、4 或更多。我还在 Firefox 上检查了另一台计算机上的程序(版本 75.0,如果重要的话) - 没有丢帧!在这台计算机上,我已经干净地重新安装了 Firefox (86.0) - 当我移动鼠标时仍然有这些滴。
我在这里进行了性能检查。在大约 4500 毫秒时,我开始移动鼠标。我不太了解它,但我可以告诉 requestAnimationFrame 甚至没有在每一帧中调用。
var canvas = document.querySelector('canvas');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
var ctx = canvas.getContext('2d');
var color_obj = '#00AA00'
var pivot_x = window.innerWidth/2 , pivot_y = window.innerHeight/2;
var pivot_wx = window.innerHeight/25 , pivot_wy = pivot_wx;
var pivot_ax = 0 , pivot_ay = 0;
var pivot_vx1 = 0 , pivot_vy1 = 0 , pivot_vx2 = 0 , pivot_vy2 = 0;
var ball_l = window.innerHeight/3;
var ball_ang = Math.PI/3;
var ball_sin , ball_cos;
var ball_x = pivot_x + ball_l * Math.sin(ball_ang);
var ball_y = pivot_y + ball_l * Math.cos(ball_ang);
var ball_r = window.innerHeight/25;
var ball_eps = 0;
var ball_om = 0;
var g = 10 * window.innerHeight/50;
var dt = 0.1;
var timer = new o_timer();
var i;
ctx.font = 20 +'px Arial';
main();
// --------------------------------------------------- MAIN LOOP
function main ()
f_calculate();
f_clearscreen();
timer.tick();
timer.display(0.7*window.innerWidth,10);
f_draw();
window.requestAnimationFrame(main);
//-------------------------------------------- PHYSICS
function f_calculate()
ball_sin = (ball_x - pivot_x) / ball_l;
ball_cos = (ball_y - pivot_y) / ball_l;
ball_eps = ( - g * ball_sin - pivot_ax * ball_cos + pivot_ay * ball_sin ) / ball_l;
ball_om = ball_om + ball_eps * dt;
ball_ang = ball_ang + ball_om * dt;
if(ball_ang > Math.PI) ball_ang = ball_ang - 2*Math.PI;
else if(ball_ang < -Math.PI) ball_ang = ball_ang + 2*Math.PI;
ball_x = pivot_x + ball_l * Math.sin(ball_ang);
ball_y = pivot_y + ball_l * Math.cos(ball_ang);
// ---------------------------------------------- DRAWING
function f_clearscreen()
ctx.clearRect(0, 0, canvas.width, canvas.height);
function f_draw ()
// Draws string
ctx.beginPath();
ctx.moveTo( pivot_x , pivot_y );
ctx.lineTo( ball_x , ball_y );
ctx.stroke();
// Draws pivot
ctx.fillStyle = color_obj;
ctx.fillRect(pivot_x - pivot_wx/2, pivot_y - pivot_wy/2 , pivot_wx , pivot_wy);
ctx.strokeRect(pivot_x - pivot_wx/2, pivot_y - pivot_wy/2 , pivot_wx , pivot_wy);
// Draws ball
ctx.fillStyle = color_obj;
ctx.beginPath();
ctx.arc(ball_x, ball_y, ball_r, 0, 2*Math.PI);
ctx.closePath();
ctx.fill();
ctx.stroke();
// --------------------------------------------------------
function o_timer()
this.t_old = 0;
this.t_new = 0;
this.duration = 0;
this.duration_frames = 0;
this.dt = 0;
this.fps = 0;
this.fps_displayT = 500;
this.fps_counter_old = 0;
this.fps_counter_new = 0;
this.fps_tick_number = 0;
this.fps_drop_count = [ 0 , 0 , 0 , 0 , 0 ];
this.tick = function()
// calculates duration between subsequent ticks
this.t_old = this.t_new;
this.t_new = performance.now();
this.duration = this.t_new - this.t_old;
// provides realistic dt for physics
this.dt = this.duration / 1000;
// calculates tick number per every fps display
this.fps_tick_number = this.fps_tick_number + 1;
this.fps_counter_old = this.fps_counter_new;
this.fps_counter_new = this.t_new % this.fps_displayT;
if(this.fps_counter_old > this.fps_counter_new)
this.fps = 1000 * this.fps_tick_number / this.fps_displayT;
this.fps_tick_number = 0;
// calculates the duration in frames (assuming 60 Hz)
this.duration_frames = Math.round( 60 * this.duration / 1000 );
// counts dropped frames (1 , 2 , 3 , 4 , more)
if( this.duration_frames >= 2 && this.duration_frames <= 5 )
this.fps_drop_count[ this.duration_frames - 2 ] += 1;
else if ( this.duration_frames > 5 )
this.fps_drop_count[4] += 1;
// for displaying fps etc.
this.line_height = 25;
this.display = function(_x , _y )
ctx.fillStyle = '#00AA00';
ctx.fillText(this.duration + ' ms', _x , _y + this.line_height);
ctx.fillText(this.fps + ' fps', _x , _y + 2*this.line_height );
ctx.fillText(this.fps_drop_count , _x , _y + 3*this.line_height );
html
-ms-touch-action: none; /* Direct all pointer events to javascript code. */
canvas
background: white;
display: block;
body
margin: 0;
<canvas></canvas>
【问题讨论】:
这里没有什么明显的错误。您的 fps 计真的是这个问题上没有的全部吗?你也可以加吗?否则,您能否删除您身边的其他任何东西以确保这是原因?还要确保停用浏览器上的任何扩展程序。最后使用浏览器的“性能”选项卡检查导致丢帧的原因。 我正在使用 Firefox。对我来说似乎很顺利。 感谢您的回答。我已经编辑了帖子以包含您所写的内容。 【参考方案1】:这里有一些更新,供将来可能会阅读此内容的任何人使用。
帧丢失的原因不在代码中。事实上,当我移除钟摆并只留下带有 fps 计数器的空画布时,我得到了相同的帧丢失。
原因是我在 Surface Pro 上使用电池运行它。当 Surface 插入电源时,没有掉帧。我还检查了使用 Surface 电池的不同浏览器,我在 Chrome 上的丢帧更少。所以原因一定是我的电脑上的一些省电选项以及浏览器的某种有限的效率。
问题仍然是我可以做些什么来减少这种情况。也许创建更小的画布,在上面画画然后放大?或者以某种方式仅在情况发生变化的区域更新屏幕?我还没试过。但这是另一个主题的主题。
【讨论】:
以上是关于移动鼠标时Firefox中的生涩javascript动画的主要内容,如果未能解决你的问题,请参考以下文章
webkit中的错误? Windows 媒体播放器运行时鼠标移动事件连续触发