Как я могу реализовать функции ослабления с помощью streamа

Я пытаюсь найти эффективный, нормальный или простой способ реализации easing-функций в моей java-программе. У меня есть функция облегчения работы, но я чувствую, что есть более эффективный способ сделать это; тот, который я не вижу, возможно, из-за туннельного зрения. вот код, который у меня есть; может кто-нибудь показать мне, что я должен делать по-другому или указать мне в направлении, которое мне нужно, чтобы исследовать

public class slide extends JPanel implements Runnable { Thread ease = new Thread(this); float total = 0; float dur; slide() { ease.start(); setLayout(null); } public float calc(float t, float b, float c, float d) { return c * t / d + b; } public void run() { while (true) { try { if (total < 50) { total += 1; } else { ease.stop(); } setBounds(400, Math.round(200 * total / 50 + 0), 250, 150); repaint(); System.out.println(total + " " + dur); ease.sleep(10); } catch (Exception e) { } } } } 

Я попытался реализовать метод calc () для функции линейного ослабления, которую я нашел в Интернете, но это практически бесполезно, потому что я был вынужден заставить ее работать, если не вставить уравнение непосредственно в

Итак, анимация – довольно сложный и углубленный предмет, который я не собираюсь здесь освещать, он также включает в себя множество математических упражнений, которые я действительно не понимаю, поэтому мы не собираемся входить в огромную сумму глубина или детали, есть лучшие люди, которые меня могут объяснить, вы можете прочитать об этом в Интернете

Поэтому для начала мы делаем некоторые предположения …

Анимация – это изменение со временем, где время переменное. Освобождение – это изменение скорости (в данном случае) за время. Это означает, что скорость анимации является переменной для любого заданного момента времени.

В принципе, мы хотим «нормализовать» все. То есть, в начале анимации время равно 0, а в конце – 1, все остальное между ними составляет долю между этими двумя значениями.

Если вы можете так думать, все становится намного проще. Поэтому, основываясь на заданной точке на временной шкале, вы можете принимать решения о том, что нужно делать. Например, в 50% случаев вы должны быть на полпути между начальным и конечным точками

Хорошо, но как все это помогает нам? Если бы нам пришлось графически отображать анимацию легкости и облегчения, она выглядела бы как-то …

BellCurve

Где ось x – это время, а ось y – скорость (между 0 и 1 по обеим сторонам). Поэтому в любой заданной точке вдоль x (по времени) мы должны иметь возможность рассчитать скорость.

Теперь мы можем сделать это, используя некоторые математические данные с кривой / кривой Безиера и вычислить скорость объекта в данной точке на временной шкале.

Теперь я позаимствовал большую часть кода непосредственно из Timing Framework, но если вам действительно интересно, вы также можете посмотреть на Bézier Curves для своих игр: Учебное пособие

(nb: Я действительно написал что-то вроде этого, а через 2 дня обнаружил, что Timing Framework уже реализована … было веселое упражнение …)

Теперь важно отметить, что эта реализация не вернет вам скорость объекта, но она вернет прогрессию времени по временной шкале (0-1), хорошо, это звучит странно, но что это позволяет вам рассчитать текущую позицию между начальной и конечной точками (startValue + ((endValue - startValue) * progress)) вдоль временной линии

Я не буду подробно останавливаться на этом, так как я действительно не понимаю математику, я просто знаю, как ее применять, но в основном мы вычисляем точки (x / y) вдоль кривой, мы затем нормализуйте эти значения (0-1), чтобы упростить поиск.

Метод interpolate использует двоичный поиск, чтобы найти ближайшую точку совпадения для заданной доли времени, а затем вычисляет положение скорости / y этой точки

 public class SplineInterpolator { private final double points[]; private final List normalisedCurve; public SplineInterpolator(double x1, double y1, double x2, double y2) { points = new double[]{ x1, y1, x2, y2 }; final List baseLengths = new ArrayList<>(); double prevX = 0; double prevY = 0; double cumulativeLength = 0; for (double t = 0; t <= 1; t += 0.01) { Point2D xy = getXY(t); double length = cumulativeLength + Math.sqrt((xy.getX() - prevX) * (xy.getX() - prevX) + (xy.getY() - prevY) * (xy.getY() - prevY)); baseLengths.add(length); cumulativeLength = length; prevX = xy.getX(); prevY = xy.getY(); } normalisedCurve = new ArrayList<>(baseLengths.size()); int index = 0; for (double t = 0; t <= 1; t += 0.01) { double length = baseLengths.get(index++); double normalLength = length / cumulativeLength; normalisedCurve.add(new PointUnit(t, normalLength)); } } public double interpolate(double fraction) { int low = 1; int high = normalisedCurve.size() - 1; int mid = 0; while (low <= high) { mid = (low + high) / 2; if (fraction > normalisedCurve.get(mid).getPoint()) { low = mid + 1; } else if (mid > 0 && fraction < normalisedCurve.get(mid - 1).getPoint()) { high = mid - 1; } else { break; } } /* * The answer lies between the "mid" item and its predecessor. */ final PointUnit prevItem = normalisedCurve.get(mid - 1); final double prevFraction = prevItem.getPoint(); final double prevT = prevItem.getDistance(); final PointUnit item = normalisedCurve.get(mid); final double proportion = (fraction - prevFraction) / (item.getPoint() - prevFraction); final double interpolatedT = prevT + (proportion * (item.getDistance() - prevT)); return getY(interpolatedT); } protected Point2D getXY(double t) { final double invT = 1 - t; final double b1 = 3 * t * invT * invT; final double b2 = 3 * t * t * invT; final double b3 = t * t * t; final Point2D xy = new Point2D.Double((b1 * points[0]) + (b2 * points[2]) + b3, (b1 * points[1]) + (b2 * points[3]) + b3); return xy; } protected double getY(double t) { final double invT = 1 - t; final double b1 = 3 * t * invT * invT; final double b2 = 3 * t * t * invT; final double b3 = t * t * t; return (b1 * points[2]) + (b2 * points[3]) + b3; } public class PointUnit { private final double distance; private final double point; public PointUnit(double distance, double point) { this.distance = distance; this.point = point; } public double getDistance() { return distance; } public double getPoint() { return point; } } } 

Если мы сделаем что-то вроде ...

 SplineInterpolator si = new SplineInterpolator(1, 0, 0, 1); for (double t = 0; t <= 1; t += 0.1) { System.out.println(si.interpolate(t)); } 

У нас есть что-то вроде ...

 0.0 0.011111693284790492 0.057295031944523504 0.16510933001160544 0.3208510585798438 0.4852971690762217 0.6499037832761319 0.8090819765428142 0.9286158775101805 0.9839043020410436 0.999702 

Хорошо, теперь вы, вероятно, думаете: «Подожди минутку, это линейная прогрессия!», Но это не так, если бы вы написали это, вы обнаружите, что первые три и три последних значения очень близки друг к другу, а остальные распространяются в разной степени, это наша «прогрессивная» ценность, насколько далеко по шкале времени мы должны быть

Итак, теперь ваша голова должна вот-вот взорваться (моя есть) - вот почему я говорю, использую frameworks!

Но как бы вы его использовали ?! Это интересная часть, теперь помните, все переменное, продолжительность анимации, скорость объекта с течением времени, количество тиков или обновлений, все переменные ...

Это важно, поскольку в этом есть сила чего-то подобного! Если, например, анимация застопорилась из-за некоторого внешнего фактора, эта реализация способна просто пропускать эти «кадры», а не становиться узким и ошеломляющим. Это может показаться плохим, но поверьте мне, это все обманывает глаз, чтобы «думать», что-то меняется;)

(Ниже приведен пример 8fps, поэтому он довольно дерьмовый)

оживлять

 import java.awt.Color; import java.awt.Dimension; import java.awt.EventQueue; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.geom.Point2D; import java.util.ArrayList; import java.util.List; import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.Timer; import javax.swing.UIManager; import javax.swing.UnsupportedLookAndFeelException; public class Test { public static void main(String[] args) { new Test(); } public Test() { EventQueue.invokeLater(new Runnable() { @Override public void run() { try { UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) { ex.printStackTrace(); } JFrame frame = new JFrame("Testing"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.add(new TestPane()); frame.pack(); frame.setLocationRelativeTo(null); frame.setVisible(true); } }); } public class TestPane extends JPanel { private int startAt = 0; private int endAt; private int x = startAt; private Timer timer; private SplineInterpolator splineInterpolator; private long startTime = -1; private long playTime = 5000; // 5 seconds public TestPane() { splineInterpolator = new SplineInterpolator(1, 0, 0, 1); timer = new Timer(5, new ActionListener() { @Override public void actionPerformed(ActionEvent e) { if (startTime < 0) { startTime = System.currentTimeMillis(); } long now = System.currentTimeMillis(); long duration = now - startTime; double t = (double) duration / (double) playTime; if (duration >= playTime) { t = 1; } double progress = splineInterpolator.interpolate(t); x = startAt + ((int) Math.round((endAt - startAt) * progress)); repaint(); } }); timer.setInitialDelay(0); addMouseListener(new MouseAdapter() { @Override public void mouseClicked(MouseEvent e) { if (!timer.isRunning()) { startTime = -1; startAt = 0; endAt = getWidth() - 10; timer.start(); } } }); } @Override public Dimension getPreferredSize() { return new Dimension(200, 200); } @Override protected void paintComponent(Graphics g) { super.paintComponent(g); Graphics2D g2d = (Graphics2D) g.create(); g2d.setColor(Color.RED); g2d.fillRect(x, (getHeight() / 2) - 5, 10, 10); g2d.dispose(); } } public static class SplineInterpolator { private final double points[]; private final List normalisedCurve; public SplineInterpolator(double x1, double y1, double x2, double y2) { points = new double[]{x1, y1, x2, y2}; final List baseLengths = new ArrayList<>(); double prevX = 0; double prevY = 0; double cumulativeLength = 0; for (double t = 0; t <= 1; t += 0.01) { Point2D xy = getXY(t); double length = cumulativeLength + Math.sqrt((xy.getX() - prevX) * (xy.getX() - prevX) + (xy.getY() - prevY) * (xy.getY() - prevY)); baseLengths.add(length); cumulativeLength = length; prevX = xy.getX(); prevY = xy.getY(); } normalisedCurve = new ArrayList<>(baseLengths.size()); int index = 0; for (double t = 0; t <= 1; t += 0.01) { double length = baseLengths.get(index++); double normalLength = length / cumulativeLength; normalisedCurve.add(new PointUnit(t, normalLength)); } } public double interpolate(double fraction) { int low = 1; int high = normalisedCurve.size() - 1; int mid = 0; while (low <= high) { mid = (low + high) / 2; if (fraction > normalisedCurve.get(mid).getPoint()) { low = mid + 1; } else if (mid > 0 && fraction < normalisedCurve.get(mid - 1).getPoint()) { high = mid - 1; } else { break; } } /* * The answer lies between the "mid" item and its predecessor. */ final PointUnit prevItem = normalisedCurve.get(mid - 1); final double prevFraction = prevItem.getPoint(); final double prevT = prevItem.getDistance(); final PointUnit item = normalisedCurve.get(mid); final double proportion = (fraction - prevFraction) / (item.getPoint() - prevFraction); final double interpolatedT = prevT + (proportion * (item.getDistance() - prevT)); return getY(interpolatedT); } protected Point2D getXY(double t) { final double invT = 1 - t; final double b1 = 3 * t * invT * invT; final double b2 = 3 * t * t * invT; final double b3 = t * t * t; final Point2D xy = new Point2D.Double((b1 * points[0]) + (b2 * points[2]) + b3, (b1 * points[1]) + (b2 * points[3]) + b3); return xy; } protected double getY(double t) { final double invT = 1 - t; final double b1 = 3 * t * invT * invT; final double b2 = 3 * t * t * invT; final double b3 = t * t * t; return (b1 * points[2]) + (b2 * points[3]) + b3; } public class PointUnit { private final double distance; private final double point; public PointUnit(double distance, double point) { this.distance = distance; this.point = point; } public double getDistance() { return distance; } public double getPoint() { return point; } } } } 

Таким образом, помимо SplineInterpolator , магия происходит внутри ActionListener для javax.swing.Timer (и некоторых в обработчике событий mouseClicked )

В основном, это вычисляет количество времени ( duration ), которое играла анимация, это становится нашим нормированным временем t или значением fraction (0-1) по временной линии, затем мы используем это для вычисления нашей «прогрессии» по временной шкале с помощью SplineInterpolator и обновить положение нашего объекта на основе разницы между его начальным и конечным положениями, умноженными на текущую «прогрессию»,

 if (startTime < 0) { startTime = System.currentTimeMillis(); } long now = System.currentTimeMillis(); long duration = now - startTime; double t = (double) duration / (double) playTime; if (duration >= playTime) { t = 1; } double progress = splineInterpolator.interpolate(t); x = startAt + ((int) Math.round((endAt - startAt) * progress)); repaint(); 

И вуаля, у нас есть легкость и легкость!

Теперь используйте анимационную структуру! Это просто SOOOO намного проще: P

  • Для «быстрого входа / замедления» вы можете использовать 0, 0, 1, 1
  • Для «медленного входа / выхода» вы можете использовать 0, 1, 0, 0
  • Для «медленного входа» вы можете использовать 1, 0, 1, 1
  • Для «замедления» вы можете использовать 0, 0, 0, 1

(или, по крайней мере, это значения, которые я использую)

Поэкспериментируйте и посмотрите, что вы получаете

  • Как я могу вырезать ArrayList из ArrayList в Java?
  • Что делает Maven Update Project в Eclipse?
  • У кого-нибудь есть тесты (код и результаты), сравнивающие производительность приложений Android, написанных на Xamarin C # и Java?
  • Тестирование частного метода с использованием mockito
  • o отобразить изображение
  • Как преобразовать текущую дату в строку в java?
  • Тестирование безопасности инициализации конечных полей
  • Что такое символ обратной косой черты (\\)?
  • java.util.Date для XMLGregorianCalendar
  • Использование открытого ключа из authorized_keys с защитой Java
  • Поддерживает ли поддержка OkHttp самоподписанные сертификаты SSL?
  • Давайте будем гением компьютера.