JavaFX官方教程(十二)之树动画示例

翻译自  树动画示例

本章提供有关树动画示例的详细信息。您将了解场景中的所有元素是如何创建和动画的。

图4-1显示了带树的场景。

图4-1树动画


 

项目和要素

树动画项目由几个文件组成。每个元素,如树叶,草叶等,都是在不同的类中创建的。在TreeGenerator类创建的所有元素的树。本Animator类包含了除驻留在草地动画所有动画GrassWindAnimation类。

示例中的场景包含以下元素:

  • 与分支,叶子和花的树

每个元素都以自己的方式进行动画处理。一些动画并行运行,其他动画按顺序运行。树木生长动画仅运行一次,而季节变化动画设置为无限运行。

图4-2动画时间轴

季节变化动画包括以下部分:

  • 叶子和花朵出现在树上

  • 花瓣落下并消失

  • 叶子和草改变颜色

  • 叶子落到地上消失

本节介绍草的创建和动画。

创造草

在树动画示例中,如图4-3所示,草由单独的草叶片组成,每个草叶片都使用Path并添加到列表中。然后每个刀片弯曲并着色。算法用于随机化叶片的高度,曲线和颜色,并将叶片分布在“地面”上。您可以指定刀片的数量和草覆盖的“地面”的大小。

图4-3草


例4-1创建草刀片

public class Blade extends Path {public final Color SPRING_COLOR = Color.color(random() * 0.5, random() * 0.5+ 0.5, 0.).darker();public final Color AUTUMN_COLOR = Color.color(random() * 0.4 + 0.3, random()* 0.1 + 0.4, random() * 0.2);private final static double width = 3;private double x = RandomUtil.getRandom(170);private double y = RandomUtil.getRandom(20) + 20;private double h = (50 * 1.5 - y / 2) * RandomUtil.getRandom(0.3);public SimpleDoubleProperty phase = new SimpleDoubleProperty();public Blade() {getElements().add(new MoveTo(0, 0));final QuadCurveTo curve1;final QuadCurveTo curve2;getElements().add(curve1 = new QuadCurveTo(-10, h, h / 4, h));getElements().add(curve2 = new QuadCurveTo(-10, h, width, 0));setFill(AUTUMN_COLOR); //autumn color of bladesetStroke(null);getTransforms().addAll(Transform.translate(x, y));curve1.yProperty().bind(new DoubleBinding() {{super.bind(curve1.xProperty());}@Overrideprotected double computeValue() {final double xx0 = curve1.xProperty().get();return Math.sqrt(h * h - xx0 * xx0);}}); //path of top of blade is circle//code to bend bladecurve1.controlYProperty().bind(curve1.yProperty().add(-h / 4));curve2.controlYProperty().bind(curve1.yProperty().add(-h / 4));curve1.xProperty().bind(new DoubleBinding() {final double rand = RandomUtil.getRandom(PI / 4);{super.bind(phase);}@Overrideprotected double computeValue() {return (h / 4) + ((cos(phase.get() + (x + 400.) * PI / 1600 +rand) + 1) / 2.) * (-3. / 4) * h;}});}
}

为草运动创建时间轴动画

更改刀片顶部x坐标的时间轴动画用于创建草运动。

使用了几种算法使运动看起来很自然。例如,每个叶片的顶部以圆形而不是直线移动,并且叶片的侧面曲线使叶片看起来好像在风下弯曲。添加随机数以分离每个刀片运动。

例4-2草动画

class GrassWindAnimation extends Transition {final private Duration animationTime = Duration.seconds(3);final private DoubleProperty phase = new SimpleDoubleProperty(0);final private Timeline tl = new Timeline(Animation.INDEFINITE);public GrassWindAnimation(List<Blade> blades) {setCycleCount(Animation.INDEFINITE);setInterpolator(Interpolator.LINEAR);setCycleDuration(animationTime);for (Blade blade : blades) {blade.phase.bind(phase);}}@Overrideprotected void interpolate(double frac) {phase.set(frac * 2 * PI);}
}

本节介绍如何创建和动画图4-4中显示的树。

图4-4树

 

分行

树由树枝,树叶和花组成。叶子和花朵画在树顶的树枝上。每个分支生成包括从父分支绘制的三个分支(一个顶部分支和两个侧分支)。您可以使用NUMBER_OF_BRANCH_GENERATIONSMain类中TreeGenerator的构造函数中传递的代码指定代数。例4-3显示了TreeGenerator类中的代码,该代码创建了树的树干(或根分支),并为后续代添加了三个分支。

例4-3根分支

private List<Branch> generateBranches(Branch parentBranch, int depth) {List<Branch> branches = new ArrayList<>();if (parentBranch == null) { // add root branchbranches.add(new Branch());} else {if (parentBranch.length < 10) {return Collections.emptyList();}branches.add(new Branch(parentBranch, Type.LEFT, depth));branches.add(new Branch(parentBranch, Type.RIGHT, depth));branches.add(new Branch(parentBranch, Type.TOP, depth));}return branches;}

为了使树看起来更自然,每个子代生成分支以与父分支成一定角度生长,并且每个子分支小于其父分支。子角度使用随机值计算。例4-4提供了用于创建子分支的代码。

例4-4子分支

public Branch(Branch parentBranch, Type type, int depth) {this();SimpleDoubleProperty locAngle = new SimpleDoubleProperty(0);globalAngle.bind(locAngle.add(parentBranch.globalAngle.get()));double transY = 0;switch (type) {case TOP:transY = parentBranch.length;length = parentBranch.length * 0.8;locAngle.set(getRandom(10));break;case LEFT:case RIGHT:transY = parentBranch.length - getGaussianRandom(0,parentBranch.length, parentBranch.length / 10, parentBranch.length / 10);locAngle.set(getGaussianRandom(35, 10) * (Type.LEFT == type ? 1 :-1));if ((0 > globalAngle.get() || globalAngle.get() > 180) && depth <4) {length = parentBranch.length * getGaussianRandom(0.3, 0.1);} else {length = parentBranch.length * 0.6;}break;}setTranslateY(transY);getTransforms().add(new Rotate(locAngle.get(), 0, 0));globalH = getTranslateY() * cos(PI / 2 - parentBranch.globalAngle.get() *PI / 180) + parentBranch.globalH;setBranchStyle(depth);addChildToParent(parentBranch, this);}

叶子和花

叶子在顶部树枝上创建。因为叶子是与树的分支同时创建的,所以叶子被缩放为0 leaf.setScaleX(0)leaf.setScaleY(0)在树生长之前隐藏它们,如例4-5所示。当它们落下时,使用相同的技巧来隐藏树叶。为了营造更自然的外观,树叶的绿色色调略有不同。此外,叶子颜色根据叶子的位置而变化; 较深的色调应用于位于树冠中部下方的叶子。

例4-5叶形和放置

public class Leaf extends Ellipse {public final Color AUTUMN_COLOR;private final int N = 5;private List<Ellipse> petals = new ArrayList<>(2 * N + 1);public Leaf(Branch parentBranch) {super(0, parentBranch.length / 2., 2, parentBranch.length / 2.);setScaleX(0);setScaleY(0);double rand = random() * 0.5 + 0.3;AUTUMN_COLOR = Color.color(random() * 0.1 + 0.8, rand, rand / 2);Color color = new Color(random() * 0.5, random() * 0.5 + 0.5, 0, 1);if (parentBranch.globalH < 400 && random() < 0.8) { //bottom leaf is darkercolor = color.darker();}setFill(color);}
}

Flower在Flower类中创建,然后添加到TreeGenerator类中树的顶部分支。您可以指定花朵中的花瓣数量。花瓣是分布在圆圈中的椭圆,有些重叠。与草和叶子类似,花瓣以不同深浅的粉红色着色。

动画树元素

本节介绍树动画示例中用于为树和季节更改设置动画的技术。并行转换用于启动场景中的所有动画,如例4-6所示。

例4-6主要动画

final Transition all = new ParallelTransition(new GrassWindAnimation(grass),
treeWindAnimation, new SequentialTransition(branchGrowingAnimation,
seasonsAnimation(tree, grass)));all.play();

种树

树生长动画仅在树动画示例的开头运行一次。应用程序启动顺序转换动画,以便一个接一个地生成分支,如例4-7所示。最初长度设置为0.根分支大小和角度在TreeGenerator类中指定。目前,每一代都在两秒钟内生长。

例4-7开始分支增长动画的顺序转换

SequentialTransition branchGrowingAnimation = new SequentialTransition();

例4-8中的代码创建了树增长动画:

例4-8分支增长动画

private Animation animateBranchGrowing(List<Branch> branchGeneration) {ParallelTransition sameDepthBranchAnimation = new ParallelTransition();for (final Branch branch : branchGeneration) {Timeline branchGrowingAnimation = new Timeline(new KeyFrame(duration,new KeyValue(branch.base.endYProperty(), branch.length)));PauseTransition pauseTransition = new PauseTransition();pauseTransition.setOnFinished(t -> branch.base.setStrokeWidth(branch.length / 25));sameDepthBranchAnimation.getChildren().add(new SequentialTransition(pauseTransition,branchGrowingAnimation));}return sameDepthBranchAnimation;}

因为所有分支线都是同时计算和创建的,所以它们可以作为点出现在场景中。代码引入了一些技巧来隐藏线条之前的线条。在示例中,代码duration.one millisecond暂停转换的时间不明显。在例4-9中,base.setStrokeWidth(0)代码在每代生成动画动画之前将分支宽度设置为0。

例4-9树木生长动画优化

private void setBranchStyle(int depth) {base.setStroke(Color.color(0.4, 0.1, 0.1, 1));if (depth < 5) { base.setStrokeLineJoin(StrokeLineJoin.ROUND);base.setStrokeLineCap(StrokeLineCap.ROUND);}base.setStrokeWidth(0); }
}

创造树冠运动

在种植树的同时,风动画开始了。树枝,树叶和花朵在一起移动。

树风动画类似于草动画动画,但它更简单,因为只有树枝的角度改变。为了使树木运动看起来自然,不同分支世代的弯曲角度是不同的。分支的生成越高(分支越小),弯曲的越多。例4-10提供了风动画的代码。

例4-10风动画

private Animation animateTreeWind(List<Branch> branchGeneration, int depth) {ParallelTransition wind = new ParallelTransition();for (final Branch brunch : branchGeneration) {final Rotate rotation = new Rotate(0);brunch.getTransforms().add(rotation);Timeline windTimeline = new Timeline(new KeyFrame(WIND_CYCLE_DURATION, 
new KeyValue(rotation.angleProperty(), depth * 2)));windTimeline.setAutoReverse(true);windTimeline.setCycleCount(Animation.INDEFINITE);wind.getChildren().add(windTimeline);}return wind;
}

动画季节变化

季节变化动画实际上是在树长大后开始的,并且无限运行。示例4-11中的代码调用所有季节动画:

例4-11开始季节动画

private Transition seasonsAnimation(final Tree tree, final List<Blade> grass) {Transition spring = animateSpring(tree.leafage, grass);Transition flowers = animateFlowers(tree.flowers);Transition autumn = animateAutumn(tree.leafage, grass);SequentialTransition sequentialTransition = new SequentialTransition(spring, flowers, autumn);return sequentialTransition;}private Transition animateSpring(List<Leaf> leafage, List<Blade> grass) {ParallelTransition springAnimation = new ParallelTransition();for (final Blade blade : grass) {
springAnimation.getChildren().add(new FillTransition(GRASS_BECOME_GREEN_DURATION, blade,(Color) blade.getFill(), blade.SPRING_COLOR));}for (Leaf leaf : leafage) {ScaleTransition leafageAppear = new ScaleTransition(LEAF_APPEARING_DURATION, leaf);leafageAppear.setToX(1);leafageAppear.setToY(1);springAnimation.getChildren().add(leafageAppear);}return springAnimation;}

一旦所有树枝生长,叶片开始按照例4-12中的指示出现。

示例4-12启动Spring动画和显示叶子的并行转换

private Transition animateSpring(List<Leaf> leafage, List<Blade> grass) {ParallelTransition springAnimation = new ParallelTransition();for (final Blade blade : grass) {springAnimation.getChildren().add(new FillTransition(GRASS_BECOME_GREEN_DURATION, blade,(Color) blade.getFill(), blade.SPRING_COLOR));}for (Leaf leaf : leafage) {ScaleTransition leafageAppear = new ScaleTransition(LEAF_APPEARING_DURATION, leaf);leafageAppear.setToX(1);leafageAppear.setToY(1);springAnimation.getChildren().add(leafageAppear);}return springAnimation;}

当所有叶子都可见时,花开始出现,如例4-13所示。顺序过渡用于逐渐显示花朵。花外观的延迟在例4-13的顺序转换代码中设定。花只出现在树冠上。

例4-13显示鲜花

private Transition animateFlowers(List<Flower> flowers) {ParallelTransition flowersAppearAndFallDown = new ParallelTransition();for (int i = 0; i < flowers.size(); i++) {final Flower flower = flowers.get(i);for (Ellipse pental : flower.getPetals()) {FadeTransition flowerAppear = new FadeTransition(FLOWER_APPEARING_DURATION, petal);flowerAppear.setToValue(1);flowerAppear.setDelay(FLOWER_APPEARING_DURATION.divide(3).multiply(i + 1));flowersAppearAndFallDown.getChildren().add(new SequentialTransition(new SequentialTransition(flowerAppear,fakeFallDownAnimation(petal))));}}return flowersAppearAndFallDown;
}

一旦所有花朵出现在屏幕上,它们的花瓣就会开始下降。在示例4-14的代码中,鲜花被复制,并且隐藏了第一组鲜花以便稍后显示。

例4-14复制花瓣

private Ellipse copyEllipse(Ellipse petalOld, Color color) {Ellipse ellipse = new Ellipse();ellipse.setRadiusX(petalOld.getRadiusX());ellipse.setRadiusY(petalOld.getRadiusY());if (color == null) {ellipse.setFill(petalOld.getFill());} else {ellipse.setFill(color);}ellipse.setRotate(petalOld.getRotate());ellipse.setOpacity(0);return ellipse;}

复制的花瓣开始逐一落到地上,如例4-15所示。花瓣在地面上五秒后消失。花瓣的下落轨迹不是直线,而是计算出的正弦曲线,因此花瓣似乎在它们落下时旋转。

实施例4-15脱落花

Animation fakeLeafageDown = fakeFallDownEllipseAnimation(leaf, leaf.AUTUMN_COLOR,
node -> {node.setScaleX(0);node.setScaleY(0);
});

当所有的花朵从场景中消失时,下一季的变化就开始了。叶子和草变黄,叶子落下并消失。实施例4-15中使用的相同算法使花瓣落下用于显示落叶。例4-16中的代码启用了秋季动画。

例4-16动画秋季变化的动画

private Transition animateAutumn(List<Leaf> leafage, List<Blade> grass) {ParallelTransition autumn = new ParallelTransition();ParallelTransition yellowLeafage = new ParallelTransition();ParallelTransition dissappearLeafage = new ParallelTransition();for (final Leaf leaf : leafage) {final FillTransition toYellow =
new FillTransition(LEAF_BECOME_YELLOW_DURATION, leaf, null, leaf.AUTUMN_COLOR);Animation fakeLeafageDown = fakeFallDownEllipseAnimation(leaf, 
leaf.AUTUMN_COLOR,node -> {node.setScaleX(0);node.setScaleY(0);});dissappearLeafage.getChildren().add(fakeLeafageDown);}ParallelTransition grassBecomeYellowAnimation = new ParallelTransition();for (final Blade blade : grass) {final FillTransition toYellow =new
FillTransition(GRASS_BECOME_YELLOW_DURATION, blade, (Color) blade.getFill(),
blade.AUTUMN_COLOR);toYellow.setDelay(Duration.seconds(1 * random()));grassBecomeYellowAnimation.getChildren().add(toYellow);}autumn.getChildren().addAll(grassBecomeYellowAnimation, newSequentialTransition(yellowLeafage, dissappearLeafage));return autumn;
}

在所有叶子从地面消失之后,春天动画开始时将草绿色着色并显示叶子。

应用文件

NetBeans项目 

  • tree_animation.zip

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/325164.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

《走遍中国》珍藏版(一)

Console.WriteLine("上一本书已经看完&#xff0c;从今天开始看下一本"); System.out.println("《走遍中国》珍藏版"); System.out.println("这本书是关于地理的"); System.out.println("地理往往是和历史进行紧密联系的");北京、天津…

MySQL建表,DML,DDL,约束,外键策略

创建数据库表 CREATE TABLE student( sno int (6), sname VARCHAR(10), sex CHAR(1), age INT(2), enterdate date, classname VARCHAR(10), email VARCHAR(15) ); – 查看表的结构 desc student – 查看表的数据 SELECT * FROM student DML – 查询表的数据 SELECT * FRO…

ssl1197-质数和分解【dp练习】

Description   任何大于 1 的自然数 n&#xff0c;都可以写成若干个大于等于 2 &#xff0c;且小于等于 n 的质数之和表达式(包括只有一个数构成的和表达式的情况)&#xff0c;并且可能有不止一种质数和的形式。例如9 的质数和表达式就有四种本质不同的形式&#xff1a; 9 …

像素密度(衡量屏幕显示能力)

像素密度√[(长度像素数)^2(宽度像素数)^2]/屏幕尺寸 eg:

通过Roslyn构建自己的C#脚本(更新版)

之前写过文章介绍过如何通过Roslyn构建自己的C#脚本&#xff0c;但那篇文章是参考自Roslyn CTP版的&#xff0c;记得本来想等到Roslyn正式版出来重新更新一下文档的&#xff0c;不过记得后来Roslyn是跳票了的&#xff0c;Scripting API在正式版本中都一度被移除了&#xff0c;这…

JavaFX官方教程(十三)之应用效果

翻译自 Applying Effects 创建视觉效果包含以下主题&#xff1a; 混合效果 绽放效果 模糊效果 投影效果 内阴影效果 反射 照明效果 透视效果 创建一系列效果 介绍如何使用视觉效果来增强JavaFX应用程序的外观。 所有效果都位于javafx.scene.effect包中&#xff0c;…

《走遍中国》珍藏版(二)

Console.WriteLine("上一本书已经看完&#xff0c;从今天开始看下一本"); System.out.println("《走遍中国》珍藏版"); System.out.println("这本书是关于地理的"); System.out.println("地理往往是和历史进行紧密联系的");大门两侧傲…

单表查询

最简单的查询语句 select * from dept select * from emp; – 显示部分列 select empno,ename,sal ,comm,deptno from emp – 显示部分行 where select empno,ename,sal ,comm,deptno from emp where sal<2500 – 别名 select empno 编号,ename 姓名,sal 工资 ,comm 补助…

ssl1072-砝码称重【dp练习】

Description 设有1g、2g、3g、5g、10g、20g的砝码各若干枚&#xff08;其总重<1000&#xff09;&#xff0c; 要求&#xff1a; 输入方式&#xff1a;a1 a2 a3 a4 a5 a6 &#xff08;表示1g砝码有a1个&#xff0c;2g砝码有a2个&#xff0c;…&#xff0c;20g砝码有a6个&…

java中遍历树形菜单,你可能不知道还有这样的方法

版权声明&#xff1a;本文为CSDN博主「穆雄雄」的原创文章&#xff0c;遵循CC 4.0 BY-SA版权协议&#xff0c;转载请附上原文出处链接及本声明。原文链接&#xff1a;https://blog.csdn.net/qq_34137397/article/details/72654955穆雄雄开发工具&#xff1a;MyEclipse 10.5后台…

Docker-Compose 一键部署Ningx+.Net Core+Redis集群

在看该文章前&#xff0c;你需要对Docker有所了解。 1、创建WebApp应用程序 我使用的是.Net Core 1.0.1版本&#xff0c;创建一个MVC应用程序&#xff0c;并添加对Redis的引用。因为这些很基础&#xff0c;也很简单&#xff0c;这里就不详细说明了&#xff0c;特别提一下有关多…

JavaFX官方教程(十四)之转换,动画和视觉效果教程的源代码

翻译自 转换&#xff0c;动画和视觉效果教程的源代码 下表列出了本文档中的演示应用程序及其关联的源代码文件。 教程源代码NetBeans项目文件 转换概述 Xylophone.java transformations.zip 动画基础知识 animations.zip 树动画示例 tree_animation.zip 创建视觉效…

ssl2294-打包【dp练习】

Description 你现在拿到了许多的礼物&#xff0c;你要把这些礼物放进袋子里。你只有一个最多装下V 体积物品的袋子&#xff0c;你不能全部放进去。你也拿不动那么重的东西。你估计你能拿的最大重量为 G。现在你了解了每一个物品的完美值、重量和体积&#xff0c;你当然想让袋子…

《走遍中国》珍藏版(三)

Console.WriteLine("上一本书已经看完&#xff0c;从今天开始看下一本"); System.out.println("《走遍中国》珍藏版"); System.out.println("这本书是关于地理的"); System.out.println("地理往往是和历史进行紧密联系的");长廊的油漆…

Catalog Service - 解析微软微服务架构eShopOnContainers(三)

上一篇我们说了Identity Service&#xff0c;因为其基于IdentityServer4开发的&#xff0c;所以知识点不是很多&#xff0c;今天我们来看下Catalog Service&#xff0c;今后的讲解都会把不同的、重点的拿出来讲&#xff0c;希望大家明白。 源码分析 我们先看下它的目录结构&a…

JavaFX官方教程(十五)之A Xylophone.java

翻译自 Xylophone.java 一个 Xylophone.java 有关说明&#xff0c;请参阅转换类型和示例。 法律条款和版权声明 / **版权所有&#xff08;c&#xff09;2010,2014&#xff0c;Oracle和/或其附属公司。* 版权所有。使用须遵守许可条款。**此文件可通过以下许可证获得并获得许…

索引,事务,视图

索引 此时是要获取所有的数据&#xff0c;是否有索引作用不大 select * from emp – 因为数据库以及自动的给主键列empno创建了索引&#xff0c;查询条件中出现empno&#xff0c;自动使用索引进行查询 – 是否使用索引&#xff0c;语句没有区别 select * from emp where empno…

配置环境变量 path

原理 根据windows系统在查找可执行程序的原理&#xff0c;可以将java工具所在路径定义到path 环境变量中&#xff0c;让系统帮我们去找运行执行的程序。 配置方法 我的电脑–属性–高级系统设置–环境变量 编辑 path 环境变量&#xff0c;在变量值开始处加上java工具所在目录…

ssl2863-石子合并【dp练习】

Description 在一个操场上一排地摆放着N堆石子。现要将石子有次序地合并成一堆。规定每次只能选相邻的2堆石子合并成新的一堆&#xff0c;并将新的一堆石子数记为该次合并的得分。请设计一个程序&#xff0c;计算出将N堆石子合并成一堆的最小得分。 Input 每组数据第1行为一个…

《走遍中国》珍藏版(四)

Console.WriteLine("上一本书已经看完&#xff0c;从今天开始看下一本"); System.out.println("《走遍中国》珍藏版"); System.out.println("这本书是关于地理的"); System.out.println("地理往往是和历史进行紧密联系的");游走其中&a…