【问题标题】:JavaScript click event not occurring while busy忙时未发生Javascript单击事件
【发布时间】:2016-07-07 13:11:08
【问题描述】:

我似乎遇到了某种并发问题,试图让点击事件发生。问题是我正在使用 setInterval 运行动画,但没有触发停止按钮,可能是因为 Javascript 很忙。如果我一遍又一遍地快速单击“停止”按钮,我可以让它停止。代码如下所示:

    var timer_id = null;
    function play(){
        timer_id = setInterval( function(){ 
            time_second++;
            if( time_second == 60 ){
                time_minute++;
                time_second = 0;
            }
            render();
        }, 1000 / time_compression_factor );
        console.log( "timer id: " + timer_id );
    }

    function stop(){
        console.log( "stopped" );
        if( timer_id != null );
        clearInterval( timer_id );
        timer_id = null;
    }
    function drawVCRControls(){
        var width_timeslice = 7 + 3;
        var ul_x = 20 + 120 * width_timeslice + 15;
        var ul_y = viewportHeight - 40;
        var pathPlayBack    = "m 20  0  l 10 -8  l  0 16  Z";
        var pathStop        = "m 35 -8  l 16  0  l  0 16  l -16 0 Z";
        var pathPlayForward = "m 57  0  l  0 -8  l 10  8  l -10 8 Z";
        var pathStepBack    = "m 12 20  l 10 -8  l  0 16  Z  m 13 -8  l 5 0  l 0 16  l -5 0 Z";
        var pathPause       = "m 35 12  h 6 v 16  h -6 Z  m 10 0  h 6 v 16  h -6 Z ";
        var pathStepForward = "m 57 12  h 5 v 16  h -5 Z  m  8 0  l 10 8  l -10 8 Z";
        createPath( "vcrPlayBack", ul_x, ul_y, pathPlayBack, 1, "orangered", "orange", 1, 0 );
        createPath( "vcrStop", ul_x, ul_y, pathStop, 1, "orangered", "orange", 1, 0 );
        createPath( "vcrPlayForward", ul_x, ul_y, pathPlayForward, 1, "orangered", "orange", 1, 0 );    
        createPath( "vcrStepBack", ul_x, ul_y, pathStepBack, 1, "orangered", "orange", 1, 0 );
        createPath( "vcrPause", ul_x, ul_y, pathPause, 1, "orangered", "orange", 1, 0 );    
        createPath( "vcrStepForward", ul_x, ul_y, pathStepForward, 1, "orangered", "orange", 1, 0 );
        vcrPlayForward.addEventListener( "click", function(){
            play();
        }, false );
        vcrStop.addEventListener( "click", function(){
            stop();
        }, false );
    }

play() 函数中的render() 调用绘制了很多东西,所以我认为当它忙于绘制时,它本质上是忽略了 vcrStop 按钮上的点击事件。我可以看到这一点,因为我有一个 console.log 语句记录每当stop() 被调用并且当我单击 vcrStop 按钮时它没有被调用。

我知道处理程序运行正常,因为如果我在开始动画之前单击 vcrStop 按钮,它会被记录到控制台。

请注意,如果我以 1 秒运行间隔,则一切正常,但间隔越短,停止动画就越困难。例如,如果我使用 500 毫秒的间隔,我可能需要单击两到三下才能停止它,但如果我使用 100 毫秒的间隔(所需的间隔),我必须非常快地单击 10 次或更多。

我该如何解决这个问题?

【问题讨论】:

  • 时间片有多大?如果它是 10 或更大,则 UI 很可能被 JS 执行锁定。尝试将它设置得非常低,看看它是否有效。如果是这样,那么你就有了解决方案 :) 然后你可以继续使用更多的 CSS 动画。
  • 通常加速因子是10,所以间隔是100毫秒。这可能太快了。
  • 为什么每次迭代都添加eventListener?

标签: javascript javascript-events event-handling


【解决方案1】:

是的,您可能是对的,render() 可能会阻止要触发的 click 事件。而且点击事件需要一些时间才能触发,因为它是 mousedownmouseup 事件的组合。这可能大于您的间隔。您可以尝试 mousedown 事件,我认为这比点击冒泡花费的时间更短。

祝你好运

【讨论】:

    【解决方案2】:

    为您的特定问题提供 JSFiddle 会很有帮助,这将非常有帮助。

    我认为您的问题正是您所说的 - 脚本很忙。问题是 JavaScript 是在单线程庄园中执行的。因此,点击事件只能在渲染完成后处理。为了使渲染的取消成为可能,您可以将渲染拆分为几个步骤,并在链式回调中调用每个步骤,即,在第一个步骤完成后,当且仅当该过程未被取消时才调用渲染的第二个步骤。

    无论如何,您提到您有一个用于单击的 console.log。渲染完成后,你看到日志的输出了吗?

    【讨论】:

    • 不,事件被丢弃了。就像我快速单击停止按钮 20 次一样,在我幸运并处理一个事件之前不会有控制台输出,然后只记录一个事件。因此,事件没有排队,它们被丢弃了。
    • 如何修改渲染以检查进程是否“取消”?如果按钮单击没有得到处理,那么我无法知道它是否被取消。在 Java 中有一个 Thread.yield 命令给事件队列一个机会,但在 JS 中没有这样的队列。
    • JavaScript 执行会锁定 UI。尝试将间隔设置为 2 秒并再次检查。
    • 您可能需要查看 Web Workers:w3schools.com/html/html5_webworkers.aspcaniuse.com/#search=Web%20Workers 以了解兼容性
    • 不,网络工作者无权访问 DOM。您通常在之后或以增量方式进行操作。所以你监听Web Worker的onmessage事件,根据事件信息改变DOM,看我之前加的w3schools链接!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-05-25
    • 1970-01-01
    • 2012-03-14
    • 2021-07-07
    相关资源
    最近更新 更多