我正在为一款名为“反恐精英:全球攻势”的游戏创建一个模拟程序,我一直在研究如何在HBox中为某些图像制作动画。在游戏中,有武器箱包含各种稀有的皮肤。点击“打开”按钮后,可能获胜的项目应开始滚动浏览HBox。想象它几乎像幸运之轮,它开始快速并逐渐减慢直到它停在1名称,但在这种情况下,而不是名称有项目,而不是'轮',我有一个水平HBox与滚动的图像。以下是我正在尝试制作的一个很好的例子:http://cases.goaggro.com/。
目前,我有一个ImageView类的实例,该实例分配给应该包含在要滚动的整个项目组中的每个武器图像,以及一个用于保存所有这些图像的数组。在GUI上,我设置了HBox的最大宽度和高度,以便从左到右放置在HBox中的任何3个图像完美地填充其“音量”。我的第一个问题就在这里。如果我尝试做类似
的事情Hbox.getChildren().addAll(itemArray);
它会添加前3个很好,但会继续添加超出HBox边界的图像,直到到达主窗口边框。有没有办法在HBox中添加比最大显示图像数量更多的图像(由于我设置的当前HBox尺寸为3),同时防止它们超出HBox边界而只是隐藏在背面?
第二个问题是,在我的HBox内部动画每个图像的最佳方法是什么,以便它向左滚动但不在HBox边界之外或之外?我需要将最左边的图像向左滑动离开屏幕,同时中间的图像滑动到左侧位置,新的图像从右侧滑入以填充正确的位置,并以逐渐减慢到的速度重复此操作着陆点上的一个项目。
目前,将此代码添加到“打开”按钮的事件处理程序会将item1,item2和item3中的图像正确添加到HBox中。但是,如果我超过3个图像(例如将HBox .addAll()设置为itemArray而不是单独设置前3个项目),它将超过HBox边框并开始将它们放置在场景顶部,直到到达主窗口边框。 注意: ImageView类(item1-item15)共有15个实例,但我将代码缩短为4个,并将数组内容缩短为4个,因为无论何时发生问题任何超过3张图像都放入HBox。
public class Controller
{
@FXML
private HBox hBox;
public void openCaseAction(ActionEvent actionEvent)
{
final ImageView item1 = new ImageView(new Image(getClass().getResourceAsStream("../images/image1.png")));
final ImageView item2 = new ImageView(new Image(getClass().getResourceAsStream("../images/image2.png")));
final ImageView item3 = new ImageView(new Image(getClass().getResourceAsStream("../images/image3.png")));
final ImageView item4 = new ImageView(new Image(getClass().getResourceAsStream("../images/image4.png")));
final ImageView[] itemArray ={item1,item2,item3,item4};
hBox.getChildren().addAll(item1,item2,item3);
}
}
答案 0 :(得分:1)
您可能希望为此使用自定义布局而不是HBox。看一下Display Shelf示例:
它不会完全符合您的要求,但它会比尝试为HBox中的项目制作动画更接近起点。
Oracle DisplayShelf示例代码:
/*
* Copyright (c) 2008, 2014, 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 Corporation 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 ensemble.samples.graphics2d.displayshelf;
import javafx.animation.Interpolator;
import javafx.animation.KeyFrame;
import javafx.animation.KeyValue;
import javafx.animation.Timeline;
import javafx.beans.InvalidationListener;
import javafx.beans.Observable;
import javafx.collections.ObservableList;
import javafx.event.EventHandler;
import javafx.scene.Group;
import javafx.scene.control.ScrollBar;
import javafx.scene.image.Image;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.Region;
import javafx.scene.shape.Rectangle;
import javafx.util.Duration;
/**
* Simple 7 segment LED style digit. It supports the numbers 0 through 9.
*/
/**
* A ui control which displays a browse-able display shelf of images
*/
public class DisplayShelf extends Region {
private final Duration DURATION = Duration.millis(500);
private final Interpolator INTERPOLATOR = Interpolator.EASE_BOTH;
private final double SPACING = 50;
private final double LEFT_OFFSET = -110;
private final double RIGHT_OFFSET = 110;
private final double SCALE_SMALL = 0.7;
private PerspectiveImage[] items;
private Group centered = new Group();
private Group left = new Group();
private Group center = new Group();
private Group right = new Group();
private int centerIndex = 0;
private Timeline timeline;
private ScrollBar scrollBar = new ScrollBar();
private boolean localChange = false;
private Rectangle clip = new Rectangle();
public DisplayShelf(Image[] images) {
// set clip
setClip(clip);
// set ids for styling via CSS
setId("displayshelf");
scrollBar.setId("display-scrollbar");
// create items
items = new PerspectiveImage[images.length];
for (int i = 0; i < images.length; i++) {
final PerspectiveImage item =
items[i] = new PerspectiveImage(images[i]);
final double index = i;
item.setOnMouseClicked((MouseEvent me) -> {
localChange = true;
scrollBar.setValue(index);
localChange = false;
shiftToCenter(item);
});
}
// setup scroll bar
scrollBar.setMax(items.length - 1);
scrollBar.setVisibleAmount(1);
scrollBar.setUnitIncrement(1);
scrollBar.setBlockIncrement(1);
scrollBar.valueProperty().addListener((Observable ov) -> {
if (!localChange) {
shiftToCenter(items[(int) Math.round(scrollBar.getValue())]);
}
});
// create content
centered.getChildren().addAll(left, right, center);
getChildren().addAll(centered, scrollBar);
// listen for keyboard events
setFocusTraversable(true);
setOnKeyPressed((KeyEvent ke) -> {
if (ke.getCode() == KeyCode.LEFT) {
shift(1);
localChange = true;
scrollBar.setValue(centerIndex);
localChange = false;
} else if (ke.getCode() == KeyCode.RIGHT) {
shift(-1);
localChange = true;
scrollBar.setValue(centerIndex);
localChange = false;
}
});
// update
update();
}
@Override
protected void layoutChildren() {
// update clip to our size
clip.setWidth(getWidth());
clip.setHeight(getHeight());
// keep centered centered
centered.setLayoutY((getHeight() - PerspectiveImage.HEIGHT) / 2);
centered.setLayoutX((getWidth() - PerspectiveImage.WIDTH) / 2);
// position scroll bar at bottom
scrollBar.setLayoutX(10);
scrollBar.setLayoutY(getHeight() - 25);
scrollBar.resize(getWidth() - 20, 15);
}
private void update() {
// move items to new homes in groups
left.getChildren().clear();
center.getChildren().clear();
right.getChildren().clear();
for (int i = 0; i < centerIndex; i++) {
left.getChildren().add(items[i]);
}
center.getChildren().add(items[centerIndex]);
for (int i = items.length - 1; i > centerIndex; i--) {
right.getChildren().add(items[i]);
}
// stop old timeline if there is one running
if (timeline != null) {
timeline.stop();
}
// create timeline to animate to new positions
timeline = new Timeline();
// add keyframes for left items
final ObservableList<KeyFrame> keyFrames = timeline.getKeyFrames();
for (int i = 0; i < left.getChildren().size(); i++) {
final PerspectiveImage it = items[i];
double newX = -left.getChildren().size()
* SPACING + SPACING * i + LEFT_OFFSET;
keyFrames.add(new KeyFrame(DURATION,
new KeyValue(it.translateXProperty(), newX, INTERPOLATOR),
new KeyValue(it.scaleXProperty(), SCALE_SMALL, INTERPOLATOR),
new KeyValue(it.scaleYProperty(), SCALE_SMALL, INTERPOLATOR),
new KeyValue(it.angle, 45.0, INTERPOLATOR)));
}
// add keyframe for center item
final PerspectiveImage centerItem = items[centerIndex];
keyFrames.add(new KeyFrame(DURATION,
new KeyValue(centerItem.translateXProperty(), 0, INTERPOLATOR),
new KeyValue(centerItem.scaleXProperty(), 1.0, INTERPOLATOR),
new KeyValue(centerItem.scaleYProperty(), 1.0, INTERPOLATOR),
new KeyValue(centerItem.angle, 90.0, INTERPOLATOR)));
// add keyframes for right items
for (int i = 0; i < right.getChildren().size(); i++) {
final PerspectiveImage it = items[items.length - i - 1];
final double newX = right.getChildren().size()
* SPACING - SPACING * i + RIGHT_OFFSET;
keyFrames.add(new KeyFrame(DURATION,
new KeyValue(it.translateXProperty(), newX, INTERPOLATOR),
new KeyValue(it.scaleXProperty(), SCALE_SMALL, INTERPOLATOR),
new KeyValue(it.scaleYProperty(), SCALE_SMALL, INTERPOLATOR),
new KeyValue(it.angle, 135.0, INTERPOLATOR)));
}
// play animation
timeline.play();
}
private void shiftToCenter(PerspectiveImage item) {
for (int i = 0; i < left.getChildren().size(); i++) {
if (left.getChildren().get(i) == item) {
int shiftAmount = left.getChildren().size() - i;
shift(shiftAmount);
return;
}
}
if (center.getChildren().get(0) == item) {
return;
}
for (int i = 0; i < right.getChildren().size(); i++) {
if (right.getChildren().get(i) == item) {
int shiftAmount = -(right.getChildren().size() - i);
shift(shiftAmount);
return;
}
}
}
public void shift(int shiftAmount) {
if (centerIndex <= 0 && shiftAmount > 0) {
return;
}
if (centerIndex >= items.length - 1 && shiftAmount < 0) {
return;
}
centerIndex -= shiftAmount;
update();
}
}
/*
* Copyright (c) 2008, 2014, 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 Corporation 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 ensemble.samples.graphics2d.displayshelf;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.scene.Parent;
import javafx.scene.effect.PerspectiveTransform;
import javafx.scene.effect.Reflection;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
/**
* A Node that displays a image with some 2.5D perspective rotation around the Y
* axis.
*/
public class PerspectiveImage extends Parent {
private static final double REFLECTION_SIZE = 0.25;
public static final double WIDTH = 200;
public static final double HEIGHT = WIDTH + (WIDTH * REFLECTION_SIZE);
private static final double RADIUS_H = WIDTH / 2;
private static final double BACK = WIDTH / 10;
private PerspectiveTransform transform = new PerspectiveTransform();
/**
* Angle Property
*/
public final DoubleProperty angle = new SimpleDoubleProperty(45) {
@Override
protected void invalidated() {
// when angle changes calculate new transform
double lx = (RADIUS_H - Math.sin(Math.toRadians(angle.get())) * RADIUS_H - 1);
double rx = (RADIUS_H + Math.sin(Math.toRadians(angle.get())) * RADIUS_H + 1);
double uly = (-Math.cos(Math.toRadians(angle.get())) * BACK);
double ury = -uly;
transform.setUlx(lx);
transform.setUly(uly);
transform.setUrx(rx);
transform.setUry(ury);
transform.setLrx(rx);
transform.setLry(HEIGHT + uly);
transform.setLlx(lx);
transform.setLly(HEIGHT + ury);
}
};
public final double getAngle() {
return angle.getValue();
}
public final void setAngle(double value) {
angle.setValue(value);
}
public final DoubleProperty angleModel() {
return angle;
}
public PerspectiveImage(Image image) {
ImageView imageView = new ImageView(image);
Reflection reflection = new Reflection();
reflection.setFraction(REFLECTION_SIZE);
imageView.setEffect(reflection);
setEffect(transform);
getChildren().addAll(imageView);
}
}