JavaFX 时间轴动画
JavaFX教程 - JavaFX时间轴动画
通过更改节点的属性(如大小,位置和颜色等)创建动画。
时间轴动画会随着时间的推移更新属性值。
JavaFX支持关键帧动画。动画状态场景在某些时间由开始和结束关键帧声明。
什么是关键值?
JavaFX允许我们创建可以内插的定时事件预定义的值来生成动画。
例如,为了产生淡出效果,我们将目标为节点的不透明属性来内插其值,从完全不透明的1开始到在一段时间内是透明的0。
以下代码定义了一个KeyValue,它以从1开始并以0结尾的矩形的不透明度属性为目标。
Rectangle rectangle = new Rectangle(0, 0, 50, 50); KeyValue keyValue = new KeyValue(rectangle.opacityProperty(), 0);
KeyValue对象实际上不内插值。它定义属性的开始和结束值。
默认情况下,KeyValue对象将有一个线性插值器。
KeyValue可以用不同的方式定义类型的内插器,如线性,ease in, 或ease out。
以下代码定义了一个键值,它将使用Interpolator.EASE_OUT内插器从左到右将矩形动画化100像素。渐弱将减慢结束键值之前的渐变。
Rectangle rectangle = new Rectangle(0, 0, 50, 50); KeyValue keyValue = new KeyValue(rectangle.xProperty(), 100, Interpolator.EASE_OUT);
什么是关键帧?
当动画发生时,每个步骤称为由KeyFrame对象定义的关键帧。
关键帧在javafx.util.Duration中定义的时间段内插入在KeyValue对象中定义的键值。
当创建KeyFrame对象时,构造函数需要一个定时持续时间(Duration)。KeyFrame构造函数接受一个或多个键值。
假设我们要从左上角到右下角移动一个矩形,我们定义一个具有1000毫秒持续时间的关键帧和两个表示矩形的x和y属性的关键值。
以下代码定义关键帧,以一秒或1000毫秒将矩形的左上角(0,0)移动到点(100,100)。
Rectangle rectangle = new Rectangle(0, 0, 50, 50); KeyValue xValue = new KeyValue(rectangle.xProperty(), 100); KeyValue yValue = new KeyValue(rectangle.yProperty(), 100); KeyFrame keyFrame = new KeyFrame(Duration.millis(1000), xValue, yValue);
什么是时间轴?
时间轴是由许多KeyFrame对象组成的一个动画序列。每个KeyFrame对象按顺序运行。
时间轴是javafx.animation.Animation类的子类,它具有标准属性,如循环计数和自动反转。
循环计数是播放动画的次数。要无限期地播放动画,请使用值Timeline.INDEFINITE。
auto-reverse
属性是一个布尔标志,表示动画可以向后播放时间轴。
默认情况下,周期计数设置为1,自动反转设置为false。
要向时间轴对象添加关键帧,请使用getKeyFrames().addAll()方法。
以下代码演示了自动反转设置为true时无限期播放的时间轴。
Timeline timeline = new Timeline(); timeline.setCycleCount(Timeline.INDEFINITE); timeline.setAutoReverse(true); timeline.getKeyFrames().addAll(keyFrame1, keyFrame2); timeline.play();
例子
/* * Copyright (c) 2011, 2012 Oracle and/or its affiliates. * All rights reserved. Use is subject to license terms. * * This file is available and licensed under the following license: * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the distribution. * - Neither the name of Oracle nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ //package colorfulcircles; import javafx.animation.KeyFrame; import javafx.animation.KeyValue; import javafx.animation.Timeline; import javafx.application.Application; import javafx.scene.Group; import javafx.scene.Node; import javafx.scene.Scene; import javafx.scene.effect.BlendMode; import javafx.scene.effect.BoxBlur; import javafx.scene.paint.Color; import javafx.scene.paint.CycleMethod; import javafx.scene.paint.LinearGradient; import javafx.scene.paint.Stop; import javafx.scene.shape.Circle; import javafx.scene.shape.Rectangle; import javafx.scene.shape.StrokeType; import javafx.stage.Stage; import javafx.util.Duration; import static java.lang.Math.random; public class Main extends Application { public static void main(String[] args) { launch(args); } @Override public void start(Stage primaryStage) { Group root = new Group(); Scene scene = new Scene(root, 800, 600, Color.BLACK); primaryStage.setScene(scene); Group circles = new Group(); for (int i = 0; i < 30; i++) { Circle circle = new Circle(150, Color.web("white", 0.05)); circle.setStrokeType(StrokeType.OUTSIDE); circle.setStroke(Color.web("white", 0.16)); circle.setStrokeWidth(4); circles.getChildren().add(circle); } Rectangle colors = new Rectangle(scene.getWidth(), scene.getHeight(), new LinearGradient(0f, 1f, 1f, 0f, true, CycleMethod.NO_CYCLE, new Stop[]{ new Stop(0, Color.web("#f8bd55")), new Stop(0.14, Color.web("#c0fe56")), new Stop(0.28, Color.web("#5dfbc1")), new Stop(0.43, Color.web("#64c2f8")), new Stop(0.57, Color.web("#be4af7")), new Stop(0.71, Color.web("#ed5fc2")), new Stop(0.85, Color.web("#ef504c")), new Stop(1, Color.web("#f2660f")),})); Group blendModeGroup = new Group(new Group(new Rectangle(scene.getWidth(), scene.getHeight(), Color.BLACK), circles), colors); colors.setBlendMode(BlendMode.OVERLAY); root.getChildren().add(blendModeGroup); circles.setEffect(new BoxBlur(10, 10, 3)); Timeline timeline = new Timeline(); for (Node circle : circles.getChildren()) { timeline.getKeyFrames().addAll( new KeyFrame(Duration.ZERO, // set start position at 0 new KeyValue(circle.translateXProperty(), random() * 800), new KeyValue(circle.translateYProperty(), random() * 600)), new KeyFrame(new Duration(40000), // set end position at 40s new KeyValue(circle.translateXProperty(), random() * 800), new KeyValue(circle.translateYProperty(), random() * 600))); } // play 40s of animation timeline.play(); primaryStage.show(); } }
上面的代码生成以下结果。
时间轴事件
/* * Copyright (c) 2011, Pro JavaFX Authors * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * 3. Neither the name of JFXtras nor the names of its contributors may be used * to endorse or promote products derived from this software without * specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * * Metronome1Main.fx - A simple example of animation using a Timeline * * Developed 2011 by James L. Weaver jim.weaver [at] javafxpert.com * as a JavaFX SDK 2.0 example for the Pro JavaFX book. */ import javafx.animation.AnimationTimer; import javafx.animation.KeyFrame; import javafx.animation.KeyValue; import javafx.animation.Timeline; import javafx.application.Application; import javafx.event.ActionEvent; import javafx.event.EventHandler; import javafx.scene.Group; import javafx.scene.Scene; import javafx.scene.effect.Lighting; import javafx.scene.layout.StackPane; import javafx.scene.paint.Color; import javafx.scene.shape.Circle; import javafx.scene.text.Text; import javafx.stage.Stage; import javafx.util.Duration; public class Main extends Application { //main timeline private Timeline timeline; private AnimationTimer timer; //variable for storing actual frame private Integer i=0; @Override public void start(Stage stage) { Group p = new Group(); Scene scene = new Scene(p); stage.setScene(scene); stage.setWidth(500); stage.setHeight(500); p.setTranslateX(80); p.setTranslateY(80); //create a circle with effect final Circle circle = new Circle(20, Color.rgb(156,216,255)); circle.setEffect(new Lighting()); //create a text inside a circle final Text text = new Text (i.toString()); text.setStroke(Color.BLACK); //create a layout for circle with text inside final StackPane stack = new StackPane(); stack.getChildren().addAll(circle, text); stack.setLayoutX(30); stack.setLayoutY(30); p.getChildren().add(stack); stage.show(); //create a timeline for moving the circle timeline = new Timeline(); timeline.setCycleCount(Timeline.INDEFINITE); timeline.setAutoReverse(true); //You can add a specific action when each frame is started. timer = new AnimationTimer() { @Override public void handle(long l) { text.setText(i.toString()); i++; } }; //create a keyValue with factory: scaling the circle 2times KeyValue keyValueX = new KeyValue(stack.scaleXProperty(), 2); KeyValue keyValueY = new KeyValue(stack.scaleYProperty(), 2); //create a keyFrame, the keyValue is reached at time 2s Duration duration = Duration.millis(2000); //one can add a specific action when the keyframe is reached EventHandler onFinished = new EventHandler<ActionEvent>() { public void handle(ActionEvent t) { stack.setTranslateX(java.lang.Math.random()*200-100); //reset counter i = 0; } }; KeyFrame keyFrame = new KeyFrame(duration, onFinished , keyValueX, keyValueY); //add the keyframe to the timeline timeline.getKeyFrames().add(keyFrame); timeline.play(); timer.start(); } public static void main(String[] args) { Application.launch(args); } }
上面的代码生成以下结果。
更多建议: