【问题标题】:How to get my Buffered Image class to display in my GUI?如何让我的缓冲图像类显示在我的 GUI 中?
【发布时间】:2018-09-04 08:27:34
【问题描述】:

我有一个使用定时器切换图像来制作动画的程序。当程序在其最后一个图像上时,我使用一个类来创建该图像的缓冲图像,上面有文本。当显示动画的最后一个图像时,我想将显示的图像更改为缓冲图像。我无法让它工作。代码原样播放,就好像粗体部分不存在一样。如果我删除它上面的行,它会显示带有文本的图像,而没有其他内容。我应该对我的代码进行哪些修改来解决这个问题?

执行动画的类

**import java.awt.event.*;
  import java.awt.Graphics;
  import java.awt.Color;
  import java.awt.Font;
  import java.awt.image.*;

  import java.io.*;
  import java.io.File;

  import java.awt.*;
  import java.awt.image.BufferedImage;

  import java.net.URL;

  import javax.swing.*;
  import javax.swing.*;

  import javax.imageio.ImageIO;

  /**
   * Write a description of class Reveal here.
   *
   * @author (your name)
   * @version (a version number or a date)
   */
  public class Reveal extends JPanel
  {
      private JPanel panel = new JPanel();       //a panel to house the label
      private JLabel label = new JLabel();       //a label to house the image
      private String[] image = {"Jack in the Box 1.png","Jack in the Box 2.png","Jack in the Box 3.png","Jack in the Box 4.png","Jack in the Box 5.png","Jack in the Box 6.png","Jack in the Box 7.png"}; //an array to hold the frames of the animation
      private ImageIcon[] icon = new ImageIcon[7]; //an array of icons to be the images
      private JFrame f;

private TextOverlay TO;

private Timer timer;
private Timer timer2;
int x = 0;
int y = 4;
int counter = 0;
/**
 * Constructor for objects of class Reveal
 */
public Reveal(String name, int number) 
{ 
    TO = new TextOverlay("Jack in the Box 7.png", name, number);

    for (int h = 0; h < 7; h++){
      icon[h] = new ImageIcon(image[h]);
      icon[h].getImage();
    }

    JFrame f = new JFrame();
    f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    f.setVisible(true);

    //Sets the size of the window
    f.setSize(800,850);
    panel = new JPanel();
    label = new JLabel();
    label.setIcon( icon[x] );
    panel.add(label);


    setVisible(true);

    f.add(panel);
    display(name, number);
    **f.add(TO);**

}

public void display(String name, int number){
    timer = new Timer(150, new ActionListener(){
        public void actionPerformed(ActionEvent e) {
            if (counter > 27){
            timer.stop();
            timer2.start(); //starts the second half of the animation
          }else{

            if (x != 3){
                x++;
            }else{
                x = 0;
            }
            label.setIcon( icon[x] );
            counter++;
          } //ends if-else
        } //ends action method
    }); //ends timer

    timer2 = new Timer(250, new ActionListener(){
        public void actionPerformed(ActionEvent e){ 
          if (y > 6) {   
            timer2.stop();
          }else{
            label.setIcon( icon[y] );
            y++;
          } //ends if-else
        } //ends action method
    }); //ends timer2

    timer.start();
    }

}
**

将文本放在图像上的类

import java.io.*;
import java.awt.*;
import javax.swing.*;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;

/**
 * @see     https://stackoverflow.com/questions/2658663
 */
public class TextOverlay extends JPanel {

private BufferedImage image;
private String name;
private String fileX;
private int number;
public TextOverlay(String f, String s, int n) {
    name = s;
    number = n;
    fileX = f;

    try {
        image = ImageIO.read(new File(fileX));
    } catch (IOException e) {
        e.printStackTrace();
    }
    image = process(image, name, number);
}



@Override
protected void paintComponent(Graphics g) {
    super.paintComponent(g);
    g.drawImage(image, 0, 0, null);
}

private BufferedImage process(BufferedImage old, String name, int number) {
    int w = old.getWidth();
    int h = old.getHeight();
    BufferedImage img = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
    Graphics2D g2d = img.createGraphics();
    g2d.drawImage(old, 0, 0, w, h, this);
    g2d.setPaint(Color.black);
    g2d.setFont(new Font("Franklin Gothic Demi Cond", Font.PLAIN, 30));
    String s1 = name;
    String s2 = Integer.toString(number);;
    FontMetrics fm = g2d.getFontMetrics();
    g2d.drawString(s1, 40, 90);
    g2d.drawString(s2, 40, 140);
    g2d.dispose();
    return img;
}

}

【问题讨论】:

    标签: java graphics timer bufferedimage


    【解决方案1】:

    所以,您似乎对 Swing 的工作原理有误解,您可能会找到 How to Use Swing TimersConcurrency in Swing 的一些帮助。

    基本上,当您 startTimer 时,它不会在此时阻塞,直到计时器结束(即使它阻塞了,您也不会按照您想要的方式工作)。相反,会创建一个新线程,并在指定时间段后向事件调度线程发出请求以执行提供的Runnable

    这意味着当你做类似...

    f.add(panel);
    display(name, number);
    f.add(TO);
    

    您实际上是将TO 组件添加到JLabel 上(因为框架使用BorderLayoutCENTRE 位置是默认位置。

    相反,在您的第二个计时器完成时,您需要删除标签并添加 TO 组件...

    timer2 = new Timer(250, new ActionListener() {
        public void actionPerformed(ActionEvent e) {
            if (y > 6) {
                timer2.stop();
                Container parent = label.getParent();
                parent.remove(label);
                parent.add(TO);
                parent.revalidate();
            } else {
                label.setIcon(icon[y]);
                y++;
            } //ends if-else
        } //ends action method
    }); //ends timer2
    

    可运行示例...

    import java.awt.event.*;
    import java.awt.Graphics;
    import java.awt.Color;
    import java.awt.Font;
    import java.awt.*;
    import java.awt.image.BufferedImage;
    import javax.swing.*;
    import javax.swing.border.LineBorder;
    
    public class Reveal extends JPanel {
    
        public static void main(String[] args) {
            EventQueue.invokeLater(new Runnable() {
                @Override
                public void run() {
                    try {
                        UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                    } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                        ex.printStackTrace();
                    }
                    new Reveal("Test", 5);
                }
            });
        }
    
        private JPanel panel = new JPanel();       //a panel to house the label
        private JLabel label = new JLabel();       //a label to house the image
        private ImageIcon[] icon = new ImageIcon[7]; //an array of icons to be the images
        private JFrame f;
    
        private TextOverlay TO;
    
        private Timer timer;
        private Timer timer2;
        int x = 0;
        int y = 4;
        int counter = 0;
    
        /**
         * Constructor for objects of class Reveal
         */
        public Reveal(String name, int number) {
            TO = new TextOverlay("Jack in the Box 7.png", name, number);
    
            for (int h = 0; h < 7; h++) {
                icon[h] = new ImageIcon(makeImage(h));
                icon[h].getImage();
            }
    
            JFrame f = new JFrame();
            f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            f.setVisible(true);
    
            //Sets the size of the window
            f.setSize(800, 850);
            panel = new JPanel(new GridBagLayout());
            label = new JLabel();
            label.setIcon(icon[x]);
            label.setBorder(new LineBorder(Color.RED));
            panel.add(label);
    
            f.add(panel);
            display(name, number);
    //      f.add(TO);
    
            setVisible(true);
        }
    
        public void display(String name, int number) {
            timer = new Timer(150, new ActionListener() {
                public void actionPerformed(ActionEvent e) {
                    if (counter > 27) {
                        timer.stop();
                        timer2.start(); //starts the second half of the animation
                    } else {
    
                        if (x != 3) {
                            x++;
                        } else {
                            x = 0;
                        }
                        label.setIcon(icon[x]);
                        counter++;
                    } //ends if-else
                } //ends action method
            }); //ends timer
    
            timer2 = new Timer(250, new ActionListener() {
                public void actionPerformed(ActionEvent e) {
                    if (y > 6) {
                        timer2.stop();
                        Container parent = label.getParent();
                        parent.remove(label);
                        parent.add(TO);
                        parent.revalidate();
                    } else {
                        label.setIcon(icon[y]);
                        y++;
                    } //ends if-else
                } //ends action method
            }); //ends timer2
    
            timer.start();
        }
    
        protected BufferedImage makeImage(int h) {
            BufferedImage img = new BufferedImage(100, 100, BufferedImage.TYPE_INT_ARGB);
            Graphics2D g2d = img.createGraphics();
            FontMetrics fm = g2d.getFontMetrics();
            String text = Integer.toString(h);
            int x = (100 - fm.stringWidth(text)) / 2;
            int y = ((100 - fm.getHeight()) / 2) + fm.getAscent();
            g2d.setColor(Color.BLUE);
            g2d.fillRect(0, 0, 100, 100);
            g2d.setColor(Color.BLACK);
            g2d.drawString(text, x, y);
            g2d.dispose();
            return img;
        }
    
        public class TextOverlay extends JPanel {
    
            private BufferedImage image;
            private String name;
            private String fileX;
            private int number;
    
            public TextOverlay(String f, String s, int n) {
                name = s;
                number = n;
                fileX = f;
    
                image = makeImage(n);
    
                image = process(image, name, number);
            }
    
            @Override
            protected void paintComponent(Graphics g) {
                super.paintComponent(g);
                g.drawImage(image, 0, 0, this);
            }
    
            @Override
            public Dimension getPreferredSize() {
                return new Dimension(100, 100);
            }
    
            private BufferedImage process(BufferedImage old, String name, int number) {
                int w = old.getWidth();
                int h = old.getHeight();
                BufferedImage img = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
                Graphics2D g2d = img.createGraphics();
                g2d.drawImage(old, 0, 0, w, h, this);
                g2d.setPaint(Color.black);
                g2d.setFont(new Font("Franklin Gothic Demi Cond", Font.PLAIN, 30));
                String s1 = name;
                String s2 = Integer.toString(number);;
                FontMetrics fm = g2d.getFontMetrics();
                g2d.drawString(s1, 40, 90);
                g2d.drawString(s2, 40, 140);
                g2d.dispose();
                return img;
            }
        }
    
    }
    

    一种“稍微”不同的方法...

    动画其实是一门很复杂的学科,不好实现。

    这就是为什么在面对此类问题时,我更喜欢查看已经实施的库来帮助解决这些问题。我建议看看:

    作为一些起点。

    虽然我更喜欢使用库,但有时这是不可能的,或者库不符合我的整体需求……我喜欢涉足……这是一种爱好。

    根据我从您的代码中可以理解的内容,您尝试从快速动画开始,然后将其放慢直到到达最后一帧。在动画理论中,这通常称为地役权,更具体地说,是“减慢/缓出”。

    以下内容借鉴了我一直在玩的一堆 sn-ps(以设计一个更可重用的库),它们基本上(随机)在 4 秒内显示图像,动画速度减慢,最后,出示“幸运”号码

    nb gif 动画其实很慢,你需要运行它才能看到区别

    import java.awt.Color;
    import java.awt.Dimension;
    import java.awt.EventQueue;
    import java.awt.Font;
    import java.awt.FontMetrics;
    import java.awt.Graphics2D;
    import java.awt.GridBagLayout;
    import java.awt.event.ActionEvent;
    import java.awt.event.ActionListener;
    import java.awt.geom.Point2D;
    import java.awt.image.BufferedImage;
    import java.time.Duration;
    import java.time.LocalDateTime;
    import java.util.ArrayList;
    import java.util.Collections;
    import java.util.Iterator;
    import java.util.List;
    import javax.swing.*;
    
    public class Reveal extends JPanel {
    
        public static void main(String[] args) {
            new Reveal();
        }
    
        public Reveal() {
            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 IntAnimatable animatable;
    
            private List<ImageIcon> icons = new ArrayList<>(25);
            private JLabel label = new JLabel();
    
            public TestPane() {
                setLayout(new GridBagLayout());
                IntRange range = new IntRange(0, 111);
                animatable = new IntAnimatable(range, Duration.ofSeconds(4), Easement.SLOWOUT, new AnimatableListener<Integer>() {
                    @Override
                    public void animationChanged(Animatable<Integer> animator) {
                        int value = animator.getValue();
                        int index = value % 7;
                        ImageIcon icon = icons.get(index);
                        if (label.getIcon() != icon) {
                            label.setIcon(icon);
                        }
                    }
                }, new AnimatableLifeCycleAdapter<Integer>() {
                    @Override
                    public void animationCompleted(Animatable<Integer> animator) {
                        BufferedImage img = makeImage(3);
                        writeTextOverImage("Lucky number", img);
                        ImageIcon luckNumber = new ImageIcon(img);
                        label.setIcon(luckNumber);
                    }
                });
    
                for (int index = 0; index < 7; index++) {
                    icons.add(new ImageIcon(makeImage(index)));
                }
                Collections.shuffle(icons);
    
                add(label);
    
                Animator.INSTANCE.add(animatable);
            }
    
            @Override
            public Dimension getPreferredSize() {
                return new Dimension(200, 200);
            }
    
        }
    
        protected void writeTextOverImage(String text, BufferedImage img) {
            Graphics2D g2d = img.createGraphics();
            Font font = g2d.getFont();
            font = font.deriveFont(Font.BOLD, font.getSize2D() + 2);
            g2d.setFont(font);
            FontMetrics fm = g2d.getFontMetrics();
            int width = img.getWidth();
            int height = img.getWidth();
            int x = (width - fm.stringWidth(text)) / 2;
            int y = fm.getAscent();
            g2d.setColor(Color.YELLOW);
            g2d.drawString(text, x, y);
            g2d.dispose();
        }
    
        protected BufferedImage makeImage(int h) {
            BufferedImage img = new BufferedImage(100, 100, BufferedImage.TYPE_INT_ARGB);
            Graphics2D g2d = img.createGraphics();
            FontMetrics fm = g2d.getFontMetrics();
            String text = Integer.toString(h);
            int x = (100 - fm.stringWidth(text)) / 2;
            int y = ((100 - fm.getHeight()) / 2) + fm.getAscent();
            g2d.setColor(Color.BLUE);
            g2d.fillRect(0, 0, 100, 100);
            g2d.setColor(Color.WHITE);
            g2d.drawString(text, x, y);
            g2d.dispose();
            return img;
        }
    
        /**** Range ****/
        /* 
        A lot of animation is done from one point to another, this just
        provides a self contained concept of a range which can be used to 
        calculate the value based on the current progression over time
        */
    
        public abstract class Range<T> {
    
            private T from;
            private T to;
    
            public Range(T from, T to) {
                this.from = from;
                this.to = to;
            }
    
            public T getFrom() {
                return from;
            }
    
            public T getTo() {
                return to;
            }
    
            @Override
            public String toString() {
                return "From " + getFrom() + " to " + getTo();
            }
    
            public abstract T valueAt(double progress);
    
        }
    
        public class IntRange extends Range<Integer> {
    
            public IntRange(Integer from, Integer to) {
                super(from, to);
            }
    
            public Integer getDistance() {
                return getTo() - getFrom();
            }
    
            @Override
            public Integer valueAt(double progress) {
                int distance = getDistance();
                int value = (int) Math.round((double) distance * progress);
                value += getFrom();
    
                return value;
            }
        }
    
        /**** Animatable ****/
        /*
        The core concept of something that is animatable.  This basic wraps up the 
        logic for calculating the progression of the animation over a period of time
        and then use that to calculate the value of the range and then the observers
        are notified so they can do stuff
        */
    
        public class IntAnimatable extends AbstractAnimatableRange<Integer> {
    
            public IntAnimatable(IntRange animationRange, Duration duration, Easement easement, AnimatableListener<Integer> listener, AnimatableLifeCycleListener<Integer> lifeCycleListener) {
                super(animationRange, duration, easement, listener, lifeCycleListener);
            }
    
        }
    
        public interface AnimatableListener<T> {
    
            public void animationChanged(Animatable<T> animator);
        }
    
        public interface AnimatableLifeCycleListener<T> {
    
            public void animationStopped(Animatable<T> animator);
    
            public void animationCompleted(Animatable<T> animator);
    
            public void animationStarted(Animatable<T> animator);
    
            public void animationPaused(Animatable<T> animator);
        }
    
        public interface Animatable<T> {
    
            public T getValue();
    
            public void tick();
    
            public Duration getDuration();
    
            public Easement getEasement();
    
            // Wondering if these should be part of a secondary interface
            // Provide a "self managed" unit of work
            public void start();
    
            public void stop();
    
            public void pause();
        }
    
        public class AnimatableLifeCycleAdapter<T> implements AnimatableLifeCycleListener<T> {
    
            @Override
            public void animationStopped(Animatable<T> animator) {
            }
    
            @Override
            public void animationCompleted(Animatable<T> animator) {
            }
    
            @Override
            public void animationStarted(Animatable<T> animator) {
            }
    
            @Override
            public void animationPaused(Animatable<T> animator) {
            }
    
        }
    
        public abstract class AbstractAnimatable<T> implements Animatable<T> {
    
            private LocalDateTime startTime;
            private Duration duration = Duration.ofSeconds(5);
            private AnimatableListener<T> animatableListener;
            private AnimatableLifeCycleListener<T> lifeCycleListener;
            private Easement easement;
            private double rawOffset;
    
            public AbstractAnimatable(Duration duration, AnimatableListener<T> listener) {
                this.animatableListener = listener;
                this.duration = duration;
            }
    
            public AbstractAnimatable(Duration duration, AnimatableListener<T> listener, AnimatableLifeCycleListener<T> lifeCycleListener) {
                this(duration, listener);
                this.lifeCycleListener = lifeCycleListener;
            }
    
            public AbstractAnimatable(Duration duration, Easement easement, AnimatableListener<T> listener) {
                this(duration, listener);
                this.easement = easement;
            }
    
            public AbstractAnimatable(Duration duration, Easement easement, AnimatableListener<T> listener, AnimatableLifeCycleListener<T> lifeCycleListener) {
                this(duration, easement, listener);
                this.lifeCycleListener = lifeCycleListener;
            }
    
            public void setEasement(Easement easement) {
                this.easement = easement;
            }
    
            @Override
            public Easement getEasement() {
                return easement;
            }
    
            public Duration getDuration() {
                return duration;
            }
    
            protected void setDuration(Duration duration) {
                this.duration = duration;
            }
    
            public double getCurrentProgress(double rawProgress) {
                Easement easement = getEasement();
                double progress = Math.min(1.0, Math.max(0.0, getRawProgress()));
                if (easement != null) {
                    progress = easement.interpolate(progress);
                }
                return Math.min(1.0, Math.max(0.0, progress));
            }
    
            public double getRawProgress() {
                if (startTime == null) {
                    return 0.0;
                }
                Duration duration = getDuration();
                Duration runningTime = Duration.between(startTime, LocalDateTime.now());
                double progress = rawOffset + (runningTime.toMillis() / (double) duration.toMillis());
    
                return Math.min(1.0, Math.max(0.0, progress));
            }
    
            @Override
            public void tick() {
                if (startTime == null) {
                    startTime = LocalDateTime.now();
                    fireAnimationStarted();
                }
                double rawProgress = getRawProgress();
                double progress = getCurrentProgress(rawProgress);
                if (rawProgress >= 1.0) {
                    progress = 1.0;
                }
    
                tick(progress);
    
                fireAnimationChanged();
                if (rawProgress >= 1.0) {
                    fireAnimationCompleted();
                }
            }
    
            protected abstract void tick(double progress);
    
            @Override
            public void start() {
                if (startTime != null) {
                    // Restart?
                    return;
                }
                Animator.INSTANCE.add(this);
            }
    
            @Override
            public void stop() {
                stopWithNotitifcation(true);
            }
    
            @Override
            public void pause() {
                rawOffset += getRawProgress();
                stopWithNotitifcation(false);
    
                double remainingProgress = 1.0 - rawOffset;
                Duration remainingTime = getDuration().minusMillis((long) remainingProgress);
                setDuration(remainingTime);
    
                lifeCycleListener.animationStopped(this);
            }
    
            protected void fireAnimationChanged() {
                if (animatableListener == null) {
                    return;
                }
                animatableListener.animationChanged(this);
            }
    
            protected void fireAnimationCompleted() {
                stopWithNotitifcation(false);
                if (lifeCycleListener == null) {
                    return;
                }
                lifeCycleListener.animationCompleted(this);
            }
    
            protected void fireAnimationStarted() {
                if (lifeCycleListener == null) {
                    return;
                }
                lifeCycleListener.animationStarted(this);
            }
    
            protected void fireAnimationPaused() {
                if (lifeCycleListener == null) {
                    return;
                }
                lifeCycleListener.animationPaused(this);
            }
    
            protected void stopWithNotitifcation(boolean notify) {
                Animator.INSTANCE.remove(this);
                startTime = null;
                if (notify) {
                    if (lifeCycleListener == null) {
                        return;
                    }
                    lifeCycleListener.animationStopped(this);
                }
            }
    
        }
    
        public abstract class AbstractAnimatableRange<T> extends AbstractAnimatable<T> {
    
            private Range<T> range;
            private T value;
    
            public AbstractAnimatableRange(Range<T> range, Duration duration, AnimatableListener<T> listener) {
                super(duration, listener);
                this.range = range;
            }
    
            public AbstractAnimatableRange(Range<T> range, Duration duration, AnimatableListener<T> listener, AnimatableLifeCycleListener<T> lifeCycleListener) {
                super(duration, listener, lifeCycleListener);
                this.range = range;
            }
    
            public AbstractAnimatableRange(Range<T> range, Duration duration, Easement easement, AnimatableListener<T> listener) {
                super(duration, easement, listener);
                this.range = range;
            }
    
            public AbstractAnimatableRange(Range<T> range, Duration duration, Easement easement, AnimatableListener<T> listener, AnimatableLifeCycleListener<T> lifeCycleListener) {
                super(duration, easement, listener, lifeCycleListener);
                this.range = range;
            }
    
            protected void tick(double progress) {
                setValue(range.valueAt(progress));
            }
    
            protected void setValue(T value) {
                this.value = value;
            }
    
            @Override
            public T getValue() {
                return value;
            }
    
        }
    
        /*
        Easement, complicated, but fun
        */
        public enum Easement {
            SLOWINSLOWOUT(1d, 0d, 0d, 1d), FASTINSLOWOUT(0d, 0d, 1d, 1d), SLOWINFASTOUT(0d, 1d, 0d, 0d), SLOWIN(1d, 0d, 1d, 1d), SLOWOUT(0d, 0d, 0d, 1d);
            private final double[] points;
            private final List<PointUnit> normalisedCurve;
    
            private Easement(double x1, double y1, double x2, double y2) {
                points = new double[]{x1, y1, x2, y2};
                final List<Double> 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;
            }
    
            protected 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;
                }
            }
    
        }
    
        /**** Core Animation Engine ****/
    
        public enum Animator {
            INSTANCE;
            private Timer timer;
            private List<Animatable> properies;
    
            private Animator() {
                properies = new ArrayList<>(5);
                timer = new Timer(5, new ActionListener() {
                    @Override
                    public void actionPerformed(ActionEvent e) {
                        List<Animatable> copy = new ArrayList<>(properies);
                        Iterator<Animatable> it = copy.iterator();
                        while (it.hasNext()) {
                            Animatable ap = it.next();
                            ap.tick();
                        }
                        if (properies.isEmpty()) {
                            timer.stop();
                        }
                    }
                });
            }
    
            public void add(Animatable ap) {
                properies.add(ap);
                timer.start();
            }
    
            protected void removeAll(List<Animatable> completed) {
                properies.removeAll(completed);
            }
    
            public void remove(Animatable ap) {
                properies.remove(ap);
                if (properies.isEmpty()) {
                    timer.stop();
                }
            }
    
        }
    
    }
    

    【讨论】:

    • 我将您的答案合并到我的程序中,现在它按预期运行。非常感谢您的详细而翔实的回答。
    猜你喜欢
    • 1970-01-01
    • 2018-06-22
    • 2018-04-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多