javafx动画

在本文中,我将解释如何在JavaFX中编写自定义动画,以及如何使用这种方法为Sprite动画创建类。 (这对于我在第33次会议上的会议来说也是一种好习惯。我计划在短短一个小时内用JavaFX编写一个游戏。这将很有趣!)

java实现动画效果 javafx动画效果_spring


运动中的马

有很多非常好的文章描述了预定义的过渡(TranslateTransition,RotateTransition等)和时间表。 在大多数情况下,这些方法已足够,但在某些情况下,仅需要更大的灵活性。 这就是Transition类开始起作用的时候,可以扩展该类以定义自定义动画。
要通过扩展Transition编写自己的动画类,需要两个步骤:

  1. 指定一个周期的持续时间
  2. 实现interpolate()方法

一个周期的持续时间

您可以通过调用受保护的方法setCycleDuration()来设置周期的持续时间。 在大多数情况下,持续时间是固定的(如果动画仅使用一次)或可由用户配置。 JavaFX运行时中几乎所有预定义的转换都属于第二类。 它们通过duration属性公开其循环持续时间,您可能也想在您的课程中做到这一点。 在极少数情况下,循环的持续时间取决于其他值。 例如,SequentialTransition和ParallelTransition的持续时间取决于其子代的持续时间。
您可以随意更改循环持续时间,但是请注意,它不会影响当前正在运行的动画。 只有在动画停止并重新开始之后,才考虑新的循环持续时间。

interpolate()方法

interpolate()方法是抽象的,需要重写。 它定义了动画的实际行为。 播放动画时,运行时会在每帧中调用interpolate()方法。 传入值frac,0.0到1.0之间的双精度值(包括两端值),用于指定当前位置。 值0.0表示动画的开始,值1.0表示动画的结束。 之间的任何值都定义相对位置。 请注意,在计算frac的值时已经考虑了可能的内插器。

类SpriteAnimation

为了演示如何定义自定义过渡,我们将看一个允许我们制作Sprite动画的类。 它会拍摄具有几帧的图像,然后将视口随时间从一帧移到另一帧。 我们将通过Eadweard Muybridge着名的“运动中的马”来测试这一节课。 聊够了,这里是代码:

package sandboxfx;

import javafx.animation.Interpolator;
import javafx.animation.Transition;
import javafx.geometry.Rectangle2D;
import javafx.scene.image.ImageView;
import javafx.util.Duration;

public class SpriteAnimation extends Transition {

    private final ImageView imageView;
    private final int count;
    private final int columns;
    private final int offsetX;
    private final int offsetY;
    private final int width;
    private final int height;

    private int lastIndex;

    public SpriteAnimation(
            ImageView imageView, 
            Duration duration, 
            int count,   int columns,
            int offsetX, int offsetY,
            int width,   int height) {
        this.imageView = imageView;
        this.count     = count;
        this.columns   = columns;
        this.offsetX   = offsetX;
        this.offsetY   = offsetY;
        this.width     = width;
        this.height    = height;
        setCycleDuration(duration);
        setInterpolator(Interpolator.LINEAR);
    }

    protected void interpolate(double k) {
        final int index = Math.min((int) Math.floor(k * count), count - 1);
        if (index != lastIndex) {
            final int x = (index % columns) * width  + offsetX;
            final int y = (index / columns) * height + offsetY;
            imageView.setViewport(new Rectangle2D(x, y, width, height));
            lastIndex = index;
        }
    }
}



清单1:SpriteAnimation类



为了简单起见,此示例类仅接受构造函数中的所有参数,不允许以后更改它们。 在大多数情况下,这就足够了。
该类需要一个ImageView,一个周期的持续时间(即遍历所有帧应花费的时间),帧数,列数(图像中的一行中有多少帧),第一帧的偏移量以及所有帧的宽度和高度。 通过调用setCycleDuration()将整个周期的持续时间传递给超类,并存储所有其他值。 作为构造函数的最后一步,将内插器设置为线性。 默认情况下,将为所有过渡设置缓动插值器,因为通常这是最好的结果。 但是在我们的例子中,我们希望以相同的速度遍历所有帧,并且缓和插值器看起来很奇怪。
interpolate()方法采用传入的值并计算当前需要显示的帧。 如果自上次调用interpolate()以来它发生了变化,则将计算新帧的位置,并相应地设置ImageView的视口。 而已。

运动中的马

为了演示SpriteAnimation类,我们将对“运动中的马”进行动画处理。 这样做的代码很简单,大多数工作已经完成。 它创建一个将视口设置为第一帧的ImageView,并实例化SpriteAnimation类。 参数仅是估计值,您可能需要对其进行一些调整。

package sandboxfx;

import javafx.animation.Animation;
import javafx.application.Application;
import javafx.geometry.Rectangle2D;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.stage.Stage;
import javafx.util.Duration;

public class SandboxFX extends Application {

    private static final Image IMAGE = new Image("http://upload.wikimedia.org/wikipedia/commons/7/73/The_Horse_in_Motion.jpg");

    private static final int COLUMNS  =   4;
    private static final int COUNT    =  10;
    private static final int OFFSET_X =  18;
    private static final int OFFSET_Y =  25;
    private static final int WIDTH    = 374;
    private static final int HEIGHT   = 243;

    public static void main(String[] args) {
        launch(args);
    }

    public void start(Stage primaryStage) {
        primaryStage.setTitle("The Horse in Motion");

        final ImageView imageView = new ImageView(IMAGE);
        imageView.setViewport(new Rectangle2D(OFFSET_X, OFFSET_Y, WIDTH, HEIGHT));

        final Animation animation = new SpriteAnimation(
                imageView,
                Duration.millis(1000),
                COUNT, COLUMNS,
                OFFSET_X, OFFSET_Y,
                WIDTH, HEIGHT
        );
        animation.setCycleCount(Animation.INDEFINITE);
        animation.play();

        primaryStage.setScene(new Scene(new Group(imageView)));
        primaryStage.show();
    }
}



清单2:JavaFX推动发展



结论

通过扩展Transition类来定义自己的动画非常简单明了。 但是,这是一种非常强大的方法,因为以这种方式创建的动画具有常规动画具有的所有功能。 例如,您可以通过更改速率来越来越慢地播放它,甚至可以向后播放它。 您可以循环运行它,也可以在ParallelTransition和SequentialTransition中使用它来创建更复杂的动画。