如何改变 AnimationTimer 的速度?

Posted

技术标签:

【中文标题】如何改变 AnimationTimer 的速度?【英文标题】:How to change AnimationTimer speed? 【发布时间】:2015-07-20 17:04:24 【问题描述】:

我将AnimationTimer 用于几项任务,例如带有变化图片的动画和ProgressIndicator 动画。为了达到所需的速度,我让线程进入睡眠状态,但是当多个动画同时运行时,它们会影响彼此的速度。有没有其他方法可以改变AnimationTimer 的速度?代码示例:

private void initialize() 
 programButtonAnimation=new AnimationTimer()
            @Override
            public void handle(long now) 
                    showClockAnimation();
            
        ;
 programButtonAnimation.start();


private void showClockAnimation()
    String imageName = "%s_"+"%05d"+".%s";
    String picturePath="t093760/diploma/view/styles/images/pink_frames/"+String.format( imageName,"pink" ,frameCount,"png");
    programButton.setStyle("-fx-background-image:url('"+picturePath+"')");
    frameCount++;
    try 
        Thread.sleep(28);
     catch (InterruptedException e) 
        // TODO Auto-generated catch block
        e.printStackTrace();
    
    if(frameCount>=120)
        programButtonAnimation.stop();
        frameCount=0;
    

【问题讨论】:

【参考方案1】:
new AnimationTimer() 
int frameCount = 0;
        @Override
        public void handle(long currentNanoTime) 

            if (frameCount % 2 == 0) 
             // 30 fps ?
                    
           frameCount++;

可能有点老套,但可以完成工作

【讨论】:

【参考方案2】:

由于我已经编写了代码,并且 James_D 更快地通知您您阻塞了 UI,我仍然想补充一点,如果您有多个不同时间的 AnimationTimer,您应该为此创建一个专用类。如果它们每个以不同的速度运行,您可以这样实现:

import javafx.animation.AnimationTimer;

public abstract class AnimationTimerExt extends AnimationTimer 

    private long sleepNs = 0;

    long prevTime = 0;

    public AnimationTimerExt( long sleepMs) 
        this.sleepNs = sleepMs * 1_000_000;
    

    @Override
    public void handle(long now) 

         // some delay
        if ((now - prevTime) < sleepNs) 
            return;
        

        prevTime = now;

        handle();
    

    public abstract void handle();


这意味着至少在 sleepMs 毫秒过后才调用 handle() 方法。

或者您更改参数并指定 fps,无论您需要什么。

你可以像这样使用上面的代码:

AnimationTimerExt timer = new AnimationTimerExt(100) 

    @Override
    public void handle() 

        System.out.println( System.currentTimeMillis());

    
;

timer.start();

另外,一遍又一遍地加载图片并不是最好的选择。如果你想做动画,我建议你看看 Mike 的关于 Creating a Sprite Animation with JavaFX 的博客。

【讨论】:

【参考方案3】:

AnimationTimerhandle 方法在 FX 应用程序线程上为渲染的每一帧调用一次。你永远不应该阻塞那个线程,所以不要在这里调用Thread.sleep(...)

传递给handle(...) 方法的参数是一个时间戳,以纳秒为单位。因此,如果您想限制更新,使更新不会超过一次,例如 28 毫秒,您可以使用它来执行此操作:

private void initialize() 
 programButtonAnimation=new AnimationTimer()

            private long lastUpdate = 0 ;
            @Override
            public void handle(long now) 
                    if (now - lastUpdate >= 28_000_000) 
                        showClockAnimation();
                        lastUpdate = now ;
                    
            
        ;
 programButtonAnimation.start();


private void showClockAnimation()
    String imageName = "%s_"+"%05d"+".%s";
    String picturePath="t093760/diploma/view/styles/images/pink_frames/"+String.format( imageName,"pink" ,frameCount,"png");
    programButton.setStyle("-fx-background-image:url('"+picturePath+"')");
    frameCount++;
    if(frameCount>=120)
        programButtonAnimation.stop();
        frameCount=0;
    

【讨论】:

更多关于使用AnimationTimer的想法,我推荐阅读this和this 如果handle 以固定的速率被调用,比如每16ms左右,这种技术不会通过在16、32、16、16、32ms等处进行更新而引入抖动吗? @StevensMiller 是的,在这种情况下它会影响平滑度(轻微)。我之前评论中的链接详细讨论了可能的解决方案。目前还不清楚为什么 OP 想要将其限制在 JavaFX 工具包的“自然”帧渲染速率之外。 很抱歉,我看不出这些链接如何解决这个问题。这是 Java 的一个主要问题,AFAIK 实际上并不允许真正的 vsync。其他人说 AnimationTimer 提供了 vsync,但我的实验很容易证明这一点。同样,页面翻转(有助于防止撕裂)是另一种广泛提出的非解决方案,作为实现垂直同步的一种方式。诚然,OP 是在追求别的东西,但是很多这些问题归结为无法同步锁定到刷新。你知道方法吗? @PhilFreihofner 博客似乎不见了,但相关的代码仓库是 here

以上是关于如何改变 AnimationTimer 的速度?的主要内容,如果未能解决你的问题,请参考以下文章

如何生成矩形并更新它们AnimationTimer?

与仅使用自制线程相比,使用 AnimationTimer 是不是具有性能优势?

如何在动画 CABasicAnimation 时改变速度

如何在动画CABasicAnimation时改变速度

如何通过改变音高和速度 iOS 保存音频?

如何使用 ffmpeg 可靠地同时改变音高和速度