Статьи

Анимированные эффекты с помощью JavaFX Callouts

В этой статье вы узнаете, как использовать API анимации JavaFX для создания выносок. Вы можете увидеть демонстрацию этих выносок в действии на моем канале Youtube по адресу https://www.youtube.com/watch?v=XTHbB0LRdT4 .

Анимация JavaFX

Что такое выноска?

Я уверен, что вы видели рекламу или научно-фантастические фильмы с использованием выносок, появляющихся поверх изображения для обозначения объектов в сцене. В этой статье вы узнаете, как использовать API анимации JavaFX для создания анимированного эффекта выноски. Используя такие эффекты, вы можете поместить текст в сцену и соединить текст с интересующим вас предметом через линию или стрелку.

На рисунке 1 показаны фрагменты, которые объединяются для создания выноски в контексте этой статьи.

Анимация JavaFX

Рисунок 1. Различные части типичного выноски

Ниже приведены определения различных частей, показанных на рисунке 1:

  • Голова — точка (круг), обозначающая предмет на изображении
  • Линия лидера — линия, проведенная от головы до другой точки, обычно образующей диагональ
  • Конец линии лидера — Конечная часть сегмента линии лидера (горизонтальная линия)
  • Основной заголовок — основной текст заголовка. Основной текст начинает прокручиваться в конечной точке строки лидера
  • Subtitle Rectangle — маленький анимированный прямоугольник, идущий снизу от основного заголовка
  • Подзаголовок — текст подзаголовка. Текст подзаголовка будет находиться под основным текстом заголовка

Не все выноски одинаковы. Большинство, тем не менее, будет иметь большинство из этих элементов.

В то время как выноска обычно указывает на вещи в книгах или журналах статическим способом, гораздо лучше в видео, если мы можем оживить выноску. Хороший, анимированный эффект — начать с рисования точки (головы), затем обвести линию (линию лидера), затем прокрутить основной заголовок и, наконец, прокрутить субтитры. Когда анимация заканчивается, она может остановиться на мгновение (достаточно, чтобы зритель мог прочитать текст заголовка), прежде чем полностью изменить процесс как способ выхода из сцены.

Анимация выноски

Теперь, когда вы знаете, что составляет выноску, мы можем перейти к некоторому коду JavaFX, чтобы анимировать каждую из частей (1-6), показанных на рисунке 1.

Поскольку каждый фрагмент выноски анимирован в последовательности, вы начнете с создания экземпляра javafx.animation.SequentialTransition. SequentialTransition содержит ноль для многих объектов Animation . Вот как это сделать:

SequentialTransition calloutAnimation = new SequentialTransition ();

Как следует из названия, анимация будет происходить в последовательности. Дополнительные вещи, которые вы хотите установить в calloutAnimation — это счетчик циклов и свойства автореверса. Например:

1
2
3
// Allow animation to go in reverse
   calloutAnimation.setCycleCount(2);
   calloutAnimation.setAutoReverse(true);

Приведенные выше настройки позволяют анимации полностью изменить последовательность после того, как читатель успел просмотреть и воспринять выноску. Чтобы добавить анимацию, вы вызываете метод .getChildren (). Add () для объекта анимации с последовательным переходом. Ниже приведен код для добавления анимации головы к объекту анимации последовательного перехода. Метод buildHeadAnim () возвращает экземпляр javafx.animation.Animation .

1
2
3
4
// Animation of head
   Circle head = new Circle(600, 550, 5); // center x, y, radius
   head.setFill(Color.WHITE);
   calloutAnimation.getChildren().add(buildHeadAnim(head));

Анимация головы

При создании анимации для головы вы можете использовать любую форму, например, прямоугольник или круг. В следующем примере голова представляет собой форму круга JavaFX. Анимация начинается с нулевого радиуса и масштабируется до большего радиуса. Показанный метод создает и возвращает объект javafx.animation.Timeline .

01
02
03
04
05
06
07
08
09
10
11
protected Animation buildHeadAnim(Node head) {
   Circle headCircle = (Circle) head;
   return new Timeline(
      new KeyFrame(Duration.millis(1),
         new KeyValue(headCircle.visibleProperty(), true),
         new KeyValue(headCircle.radiusProperty(), 0)
      ), // start value
      new KeyFrame(Duration.millis(300),
        new KeyValue(headCircle.radiusProperty(), 5.0d)) // end value
   );
}

Временная шкала состоит из исходного ключевого кадра, который устанавливает для свойства видимого окружности головы значение true, а для свойства радиуса — ноль. Затем конечный ключевой кадр задается значениями ключей для интерполяции свойства radius от нуля до 5,0 в течение 300 миллисекунд.

Лидер Линия Анимация

При анимации лидерной линии, линия будет выглядеть так, как будто она рисуется или рисуется карандашом. В приведенном ниже коде координаты конечной точки интерполируются (линейно). Линия проводится от центра головы (600, 550) к координате lineToPoint (400, 300). Я жестко закодировал значения, чтобы сделать код более кратким. Фактические значения ключа для endX / endY параметризуются как getLeaderLineToPoint (). GetX () и getLeaderLineToPoint (). GetY () соответственно.

01
02
03
04
05
06
07
08
09
10
protected Animation buildBeginLeaderLineAnim(Line leaderLine) {
   return new Timeline(
      new KeyFrame(Duration.millis(1),
         new KeyValue(leaderLine.visibleProperty(), true)),// show
      new KeyFrame(Duration.millis(300),
         new KeyValue(leaderLine.endXProperty(), 400),
         new KeyValue(firstLeaderLine.endYProperty(), 300)
      )
   );
}

Анимация конца строки лидера

Поскольку код для анимации конечной части выносной линии вы заметите, он очень похож на предыдущую анимацию, поэтому я опущу код. Чтобы увидеть полный список, пожалуйста, посетите: https://github.com/carldea/callouts/tree/master/src/com/carlfx/callouts .

Основное название Текст Анимация

Основная анимация текста заголовка состоит из HBox, содержащего узел Text, который будет прокручиваться влево или вправо в зависимости от направления конечной точки выносной линии. Например, если направление конечной точки выносной линии указывает на право, то текст основного заголовка будет отображаться для прокрутки вправо.

Ниже приведен метод buildMainTitleAnim (), отвечающий за анимацию основного текста заголовка. Поскольку этот метод является наиболее сложной частью анимации выноски, я хотел поделиться некоторыми советами и хитростями, с которыми я столкнулся на своем пути.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
protected Animation buildMainTitleAnim(HBox mainTitleBackground) {
     // main title box
    // Calculate main title width and height upfront
    Rectangle2D mainTitleBounds = getBoundsUpfront(mainTitleBackground);
 
    double mainTitleWidth = mainTitleBounds.getWidth();
    double mainTitleHeight = mainTitleBounds.getHeight();
 
    // Position mainTitleText background beside the end part of the leader line.
    Point2D endPointLLine = calcEndPointOfLeaderLine();
    double x = endPointLLine.getX();
    double y = endPointLLine.getY();
 
    // Viewport to make main title appear to scroll
    Rectangle mainTitleViewPort = new Rectangle();
    mainTitleViewPort.setWidth(0);
    mainTitleViewPort.setHeight(mainTitleHeight);
 
    mainTitleBackground.setClip(mainTitleViewPort);
    mainTitleBackground.setLayoutX(x);
    mainTitleBackground.setLayoutY(y - (mainTitleHeight/2));
 
    // Animate main title from end point to the left.
    if (LEFT == getEndLeaderLineDirection()) {
        // animate layout x and width
        return new Timeline(
           new KeyFrame(Duration.millis(1),
              new KeyValue(mainTitleBackground.visibleProperty(), true),
              new KeyValue(mainTitleBackground.layoutXProperty(), x)
           ), // show
           new KeyFrame(Duration.millis(200),
              new KeyValue(mainTitleBackground.layoutXProperty(), x - mainTitleWidth),
              new KeyValue(mainTitleViewPort.widthProperty(), mainTitleWidth)
           )
        );
    }
 
    // Animate main title from end point to the right
    return new Timeline(
       new KeyFrame(Duration.millis(1),
          new KeyValue(mainTitleBackground.visibleProperty(), true)), // show
       new KeyFrame(Duration.millis(200),
             new KeyValue(mainTitleViewPort.widthProperty(), mainTitleWidth)
       )
    );
}

В поисках основного названия (Bounds) Upfront

Ваш код должен знать заранее размер ограничивающей области при размещении основного текста заголовка. Ниже приведен метод, который принимает HBox, содержащий узел Text, а затем вычисляет ширину и высоту HBox без отображения поля на главном графе сцены.

1
2
3
4
5
6
7
8
9
protected Rectangle2D getBoundsUpfront(Region node) {
    // Calculate main title width and height
    Group titleRoot = new Group();
    new Scene(titleRoot);
    titleRoot.getChildren().add(node);
    titleRoot.applyCss();
    titleRoot.layout();
    return new Rectangle2D(0, 0, node.getWidth(), node.getHeight());
}

В этом примере вы можете увидеть, как методы applyCss () и layout () из класса javafx.scene.Node отвечают за определение ширины и высоты после применения стиля CSS. Выше вы заметите, что сцена временно создана.

Анимация субтитров

Для краткости я опустил анимацию субтитров. Надеюсь, вы посмотрите полный список кодов, который я упоминал ранее на Github. Все анимации субтитров следуют тому же шаблону, что и анимации титров, с точки зрения начального начала анимаций и интерполяции до конечных значений ключа.

Воспроизведение анимации выноски

Предполагая, что ваши узлы, составляющие выноску, добавлены в узел макета JavaFX Pane, вам нужно остановить последовательную анимацию calloutAnimation . Затем вам нужно будет инициализировать все узлы, чтобы они не отображались (свойство visible имеет значение false). Наконец, вам нужно вызвать метод play () .

01
02
03
04
05
06
07
08
09
10
getChildren().addAll(head,
      firstLeaderLine,
      secondLeaderLine,
      mainTitle,
      subTitleRect,
      subTitle);
 
   calloutAnimation.stop();
   getChildren().forEach(node -> node.setVisible(false));
   calloutAnimation.play();

Вывод

Используя API анимации JavaFX и простые формы, было довольно легко создавать анимированные выноски. В этой статье вы узнали, как использовать объект SequentialTransition, ответственный за последовательный запуск анимации меньшего размера (временной шкалы).

При построении каждого шага выноски каждая анимация временной шкалы будет использовать ключевые кадры, которые интерполируются по ключевым значениям на основе свойств формы, таких как радиус круга. После того, как вы узнали о том, как анимировать лидерскую линию, вы также узнали об одном хитром приеме, который помог определить размер основного текста заголовка с помощью методов applyCss () и layout () . Из-за стилей и размеров шрифта трудно узнать размер компонента пользовательского интерфейса, пока он не будет визуализирован в основной граф сцены.

Теперь, когда вы знаете, как реализовать анимированные выноски, я надеюсь, вы сделаете приложения более привлекательными. Удачного кодирования!

Смотрите оригинальную статью здесь: Анимированные эффекты с помощью JavaFX Callouts

Мнения, высказанные участниками Java Code Geeks, являются их собственными.