Javafx 插值 anticipate/overshoot

Javafx Interpolation anticipate/overshoot

尝试用预期和超调插入 javafx 时间轴,我找到了 Interpolator.SPLINE 方法,它应该

Create an Interpolator, which curve() is shaped using the spline control points defined by (x1, y1 ) and (x2, y2). The anchor points of the spline are implicitly defined as (0.0, 0.0) and (1.0, 1.0).

根据文档,但是使用小于 0.0 或大于 1.0 的值 Y 值调用会抛出 IllegalArgumentException,这意味着我无法做到 anticipate/overshoot。

我只是使用以下参数调用 Interpolator.SPLINE :

Interpolator anticipateOvershoot = Interpolator.SPLINE(0.68, -0.6, 0.32, 1.6);

我期待的是类似于 CSS 三次贝塞尔插值

但我得到的是一个 RuntimeException

Caused by: java.lang.IllegalArgumentException: Control point coordinates must all be in range [0,1]
    at com.sun.scenario.animation.SplineInterpolator.<init>(SplineInterpolator.java:98)
    at javafx.animation.Interpolator.SPLINE(Interpolator.java:199)

现在我的问题是为什么在 SplineInterpolator 中使用小于 0 或大于 1 的 Y 值是非法的,在 javafx 中制作 anticipate/overshoot 插值器的最方便的方法是什么?

添加 MRE

我正在尝试使用时间线在 StackPane 中的两个节点之间进行转换,但我需要用 anticipation/overshooting

对其进行插值
public class App extends Application {

    @Override
    public void start(Stage ps) throws Exception {
        //Setting up the scene
        VBox root = new VBox();
        root.setBackground(new Background(new BackgroundFill(Color.GRAY, null, null)));
        
        StackPane cards = new StackPane();
        cards.setMinSize(600, 600);
        
        StackPane card1 = new StackPane(new Label("Card 1"));
        card1.setMaxSize(400, 200);
        card1.setBackground(new Background(new BackgroundFill(Color.WHITE, new CornerRadii(10), null)));
        
        StackPane card2 = new StackPane(new Label("Card 2"));
        card2.setMaxSize(200, 400);
        card2.setBackground(card1.getBackground());
        
        cards.getChildren().addAll(card1, card2);
        
        HBox buttons = new HBox(10);
        buttons.setPadding(new Insets(10));
        buttons.setAlignment(Pos.CENTER);
        
        Button show1 = new Button("Card 1");
        Button show2 = new Button("Card 2");
        
        buttons.getChildren().addAll(show1, show2);
        
        root.getChildren().addAll(cards, buttons);
        
        //adding behaviour
        double duration = .3;
        Interpolator interpolator = Interpolator.EASE_BOTH;
        
        Timeline showCard1 = new Timeline(new KeyFrame(Duration.seconds(duration), 
                new KeyValue(card1.opacityProperty(), 1, interpolator), 
                new KeyValue(card1.scaleXProperty(), 1, interpolator), 
                new KeyValue(card1.scaleYProperty(), 1, interpolator), 
                new KeyValue(card1.translateYProperty(), 0, interpolator),
                
                
                new KeyValue(card2.opacityProperty(), 0, interpolator), 
                new KeyValue(card2.scaleXProperty(), .5, interpolator), 
                new KeyValue(card2.scaleYProperty(), .5, interpolator), 
                new KeyValue(card2.translateYProperty(), -100, interpolator)
            ));
        
        Timeline showCard2 = new Timeline(new KeyFrame(Duration.seconds(duration), 
                new KeyValue(card2.opacityProperty(), 1, interpolator), 
                new KeyValue(card2.scaleXProperty(), 1, interpolator), 
                new KeyValue(card2.scaleYProperty(), 1, interpolator), 
                new KeyValue(card2.translateYProperty(), 0, interpolator),
                
                
                new KeyValue(card1.opacityProperty(), 0, interpolator), 
                new KeyValue(card1.scaleXProperty(), .5, interpolator), 
                new KeyValue(card1.scaleYProperty(), .5, interpolator), 
                new KeyValue(card1.translateYProperty(), -100, interpolator)
            ));
        
        show1.setOnAction(e-> {
            showCard2.stop();
            showCard1.playFromStart();
        });
        
        show2.setOnAction(e-> {
            showCard1.stop();
            showCard2.playFromStart();
        });
        
        show1.fire();
        
        Scene scene = new Scene(root);
        ps.setScene(scene);
        ps.show();
    }

}

(该示例使用 ease_both 插值,因为尝试使用值超出范围的样条曲线会出现上述错误)

Why is it illegal to use Y values lower than 0 or greater than 1 in a SplineInterpolator,

我不知道。

and what's the most convenient way to make an anticipate/overshoot interpolator in javafx ?

创建您自己的实现。

扩展插值器,不要在您的实现中使用范围检查,对其进行单元测试以确保它执行您想要的功能。

Example implementation 您可能希望从。