2015-07-11 119 views
0

我是JavaFX中的動畫新手,我對它的工作原理有了基本的瞭解;你需要一個起始關鍵幀並結束一個,然後你播放它。文本移動動畫

我想要做的是有一個字符串列表,例如「0,1,2,3,4,5 ... 10」,以及一個變量index。然後我想要做的就是在屏幕上對他們有3處這樣的內3個不同的Text對象時間:

0 1 2 

在上面的例子中,index = 0。如果index = 1,它應該是這樣的:

1 2 3 

你的想法,所以我想要做的,就是每一次index增量(它必須是一個Property),會有的動畫數字交易空間。

所以像這樣(符號數字代表衰落):

index = 0 : 0 1 2 
index = 1 :) 1 2# 
frame --- :) 1 2 # 
frame --- :)1 2 # 
frame --- : 1 2 3 

所以理論上,存儲這些數據的列表可以是無限的,所以我不能(也不應該)有一個單獨的Text對象每個號碼。我無法弄清楚應該怎麼做,因爲移動對象本身的關鍵幀本身使事情變得複雜。

回答

1

下面是一個示例。

app

import javafx.animation.*; 
import javafx.application.Application; 
import javafx.beans.binding.StringBinding; 
import javafx.beans.property.*; 
import javafx.geometry.*; 
import javafx.scene.Scene; 
import javafx.scene.control.*; 
import javafx.scene.layout.*; 
import javafx.scene.paint.Color; 
import javafx.stage.Stage; 
import javafx.util.Duration; 

import java.util.List; 
import java.util.stream.*; 

public class NumberSwitcher extends Application { 

    private static final int NUM_NUMS_DISPLAYED = 3; 
    private static final Duration TRANSITION_TIME = Duration.seconds(0.5); 

    private final IntegerProperty idx = new SimpleIntegerProperty(0); 
    private final IntegerProperty toIdx = new SimpleIntegerProperty(0); 

    private final List<Label> labels = 
      IntStream.range(0, NUM_NUMS_DISPLAYED) 
        .mapToObj(
          this::createLabel 
        ) 
        .collect(Collectors.toList()); 

    private final Button incrementButton = new Button("Increment"); 

    @Override 
    public void start(Stage stage) { 
     stage.setScene(
       new Scene(
         createLayout(), 
         Color.PALEGREEN 
       ) 
     ); 
     stage.show(); 

     enableTransitions(); 
    } 

    private StackPane createLayout() { 
     incrementButton.setStyle("-fx-base: darkslateblue; -fx-text-fill: papayawhip; -fx-font-size: 20px;"); 

     HBox numbers = new HBox(10); 
     numbers.getChildren().addAll(labels); 
     numbers.setPadding(new Insets(15)); 
     numbers.setMaxWidth(HBox.USE_PREF_SIZE); 

     VBox layout = new VBox(
       15, 
       numbers, 
       incrementButton 
     ); 
     layout.setAlignment(Pos.CENTER); 
     layout.setPadding(new Insets(15)); 
     layout.setStyle("-fx-background-color: null;"); 

     StackPane centered = new StackPane(layout); 
     centered.setStyle("-fx-background-color: null;"); 
     return centered; 
    } 

    private Label createLabel(final int i) { 
     Label label = new Label(); 
     label.setStyle(
       "-fx-font-size: 50px; -fx-font-family: monospace; -fx-text-fill: midnightblue;" 
     ); 

     label.textProperty().bind(new StringBinding() { 
      { 
       super.bind(idx); 
      } 

      @Override 
      protected String computeValue() { 
       return "" + ((idx.get() + i) % 10); 
      } 
     }); 

     return label; 
    } 

    private void enableTransitions() { 
     ParallelTransition changeNumbers = new ParallelTransition(
       createFadeFirst() 
     ); 

     IntStream.range(1, labels.size()) 
       .mapToObj(this::createMoveLeft) 
       .forEachOrdered(
         moveLeft -> changeNumbers.getChildren().add(moveLeft) 
       ); 

     changeNumbers.setOnFinished(e -> idx.set(toIdx.get())); 

     toIdx.addListener((observable, oldValue, newValue) -> 
       changeNumbers.play() 
     ); 

     // You can disable incrementing while the transition is running, 
     // but that is kind of annoying, so I chose not to. 
//  incrementButton.disableProperty().bind(
//    changeNumbers.statusProperty().isEqualTo(
//      PauseTransition.Status.RUNNING 
//    ) 
//  ); 

     incrementButton.setOnAction(e -> { 
      if (idx.get() != toIdx.get()) { 
       idx.set(toIdx.get()); 
      } 

      toIdx.set((toIdx.get() + 1) % 10); 
     }); 
    } 

    private FadeTransition createFadeFirst() { 
     FadeTransition fadeFirst = new FadeTransition(
       TRANSITION_TIME, 
       labels.get(0) 
     ); 
     fadeFirst.setFromValue(1); 
     fadeFirst.setToValue(0); 
     fadeFirst.setOnFinished(e -> labels.get(0).setOpacity(1)); 
     return fadeFirst; 
    } 

    private TranslateTransition createMoveLeft(int i) { 
     TranslateTransition moveLeft = new TranslateTransition(
       TRANSITION_TIME, 
       labels.get(i) 
     ); 
     double dx = 
       labels.get(i).getBoundsInParent().getMinX() 
         - labels.get(i-1).getBoundsInParent().getMinX(); 

     moveLeft.setFromX(0); 
     moveLeft.setToX(-dx); 
     moveLeft.setOnFinished(e -> labels.get(i).setTranslateX(0)); 

     return moveLeft; 
    } 

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

} 

回答更多的問題

我看你所翻譯的節點,但是,在什麼時候,你更改HBox中的位置?

此樣本的HBox中的位置(例如layoutX和layoutY)在HBox中初始佈局後決不會更改。 HBox是一個佈局管理者,它會自動設置HBox中的佈局X和佈局Y座標。 HBox佈局算法將從左到右依次將每個項目放置在其子項列表中(默認情況下),並將每個項目的大小設置爲它的首選大小。 HBox基於髒標誌工作,所以它只會在需要時重新佈置組件內部(例如組件更改或組件更改的CSS樣式更改或HBox更改的可用區域),這些都不會在此發生樣品。

你只是翻譯他們,切換文本,然後重置翻譯?

是。

你能簡單介紹一下它是如何工作的嗎?

將三個標籤添加到HBox中。每個標籤使用等寬字體和相同數量的字符,因此每個標籤的寬度相等。記錄兩個指數。一個是顯示字符的文本區域中的當前索引,另一個是要移動到文本數組中的下一個索引的toIdx。提供一個增量按鈕,它將增加toIdx。更改偵聽器放置在toIdx上,以便當它更改時,它會啓動一組動畫,第一個動畫會淡化第一個標籤,其他動畫會將剩餘標籤移動到下一個位置。當動畫完成時,每個標籤的翻譯被設置爲0,因此每個移動的標籤都會移回原始位置。另外,一旦動畫完成,當前索引被設置爲toIdx。綁定用於在toIdx更新時自動更新每個標籤中的文本。

效果是,當按下增加按鈕時,第一個標籤中的數字消失,右側標籤中的數字向左移動,一旦返回原來的位置,但使用它們值設置爲下一個最高數字。這提供了問題中要求的效果。

+0

我不明白這一點,我看你正在翻譯節點,但是,你在什麼時候改變HBox的位置?你只是翻譯他們,切換文本,然後重新設置翻譯?你能簡單介紹一下它的工作原理嗎?那太好了! –

+0

感謝您的解釋! –