2

我开始对用 Java 制作动画(幻灯片、背景等)感兴趣。我知道 JavaFX 做这件事要好得多,但我只是固执地打扰切换。

这是我到目前为止得到的。

import java.awt.Color;
import java.awt.GradientPaint;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.awt.image.BufferedImageOp;
import java.awt.image.ConvolveOp;
import java.awt.image.Kernel;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Random;

import javax.swing.JFrame;
import javax.swing.JPanel;

public class BlurredLightCells extends JPanel {

    private static final long serialVersionUID = 4610174943257637060L;

    private Random random = new Random();

    private ArrayList<LightCell> lightcells;

    private float[] blurData = new float[500];

    public static void main(String[] args) {
        JFrame frame = new JFrame("Swing animated bubbles");
        frame.setSize(1000, 750);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        frame.add(new BlurredLightCells(60));

        frame.setVisible(true);
    }

    public BlurredLightCells(int amtOfBCells) {
        setSize(1000, 750);
        /**
         * Below we initiate all the cells that are going to be drawn on screen
         */
        Arrays.fill(blurData, 1f / 20f);
        lightcells = new ArrayList<LightCell>(amtOfBCells);
        for (int i = 0; i < amtOfBCells; i++) {

            /**
             * Below we generate all the values for each cell(SHOULD be random for each one)
             */
            int baseSpeed = random(0, 3);
            int xSpeed = (int) Math.floor((Math.random() * (baseSpeed - -baseSpeed + baseSpeed)) + -baseSpeed);
            int ySpeed = (int) Math.round((Math.random() * baseSpeed) + 0.5);
            int radius = random(25, 100);
            int x = (int) Math.floor(Math.random() * getWidth());
            int y = (int) Math.floor(Math.random() * getHeight());
            int blurrAmount = (int) (Math.floor(Math.random() * 10) + 5);
            int alpha = (int) ((Math.random() * 15) + 3);

            /**
             * Now we draw a image, and apply transparency and a slight blur to it
             */
            Kernel kernel = new Kernel(blurrAmount, blurrAmount, blurData);
            BufferedImageOp op = new ConvolveOp(kernel);
            BufferedImage circle = new BufferedImage(150, 150, BufferedImage.TYPE_INT_ARGB);
            Graphics2D circlegfx = circle.createGraphics();
            circlegfx.setColor(new Color(255, 255, 255, alpha));
            circlegfx.fillOval(20, 20, radius, radius);
            circle = op.filter(circle, null);
            LightCell bubble = new LightCell(x, y, xSpeed, ySpeed, radius, getDirection(random.nextInt(3)), circle);
            lightcells.add(bubble);
        }
    }

    public int random(int min, int max) {
        final int n = Math.abs(max - min);
        return Math.min(min, max) + (n == 0 ? 0 : random.nextInt(n));
    }

    @Override
    public void paint(Graphics g) {
        int w = getWidth();
        int h = getHeight();
        final Graphics2D g2 = (Graphics2D) g;
        GradientPaint gp = new GradientPaint(-w, -h, Color.LIGHT_GRAY, w, h, Color.DARK_GRAY);
        g2.setPaint(gp);
        g2.fillRect(0, 0, w, h);
        long start = System.currentTimeMillis();
        for (int i = 0; i < lightcells.size(); i++) {
            LightCell cell = lightcells.get(i);
            cell.process(g2);
        }
        System.out.println("Took " + (System.currentTimeMillis() - start) + " milliseconds to draw ALL cells.");
        repaint();
    }

    public String getDirection(int i) {
        switch (i) {
            case 0:
                return "right";
            case 1:
                return "left";
            case 2:
                return "up";
            case 3:
                return "down";
        }
        return "";
    }

    private class LightCell {

        private int x, y, xSpeed, ySpeed, radius;
        private String direction;
        private BufferedImage image;

        public LightCell(int x, int y, int xSpeed, int ySpeed, int radius, String direction, BufferedImage image) {
            this.x = x;
            this.y = y;
            this.xSpeed = xSpeed;
            this.ySpeed = ySpeed;
            this.radius = radius;
            this.direction = direction;
            this.image = image;
        }

        public void process(Graphics g) {
            switch (direction) {
                case "right":
                    moveRight();
                    break;
                case "left":
                    moveLeft();
                    break;
                case "up":
                    moveUp();
                    break;
                case "down":
                    moveDown();
                    break;
            }
            g.drawImage(image, x, y, null);
        }

        private void moveUp() {
            x += xSpeed;
            y -= ySpeed;
            if (y + (radius / 2) < 0) {
                y = getHeight() + (radius / 2);
                x = (int) Math.floor(Math.random() * getWidth());
            }

            if ((x + radius / 2) < 0 || (x - radius / 2) > getWidth()) {
                y = radius + (radius / 2);
                x = (int) Math.floor(Math.random() * getWidth());
            }
        }

        private void moveDown() {
            x += xSpeed;
            y += ySpeed;
            if (y - (radius / 2) > getHeight()) {
                y = 0 - (radius / 2);
                x = (int) Math.floor(Math.random() * getWidth());
            }

            if ((x + radius / 2) < 0 || (x - radius / 2) > getWidth()) {
                y = getHeight() + (radius / 2);
                x = (int) Math.floor(Math.random() * getWidth());
            }
        }

        private void moveRight() {
            x += ySpeed;
            y += xSpeed;
            if (y - (radius / 2) > getHeight() || y + (radius / 2) < 0) {
                x = 0 - (radius / 2);
                y = (int) Math.floor(Math.random() * getHeight());
            }

            if ((x - radius / 2) > getWidth()) {
                x = 0 - (radius / 2);
                y = (int) Math.floor(Math.random() * getWidth());
            }
        }

        private void moveLeft() {
            x -= ySpeed;
            y -= xSpeed;
            if (y - (radius / 2) > getHeight() || y + (radius / 2) < 0) {
                x = getWidth() + (radius / 2);
                y = (int) Math.floor(Math.random() * getHeight());
            }

            if ((x + radius / 2) < 0) {
                x = getWidth() + (radius / 2);
                y = (int) Math.floor(Math.random() * getWidth());
            }
        }
    }
}

如果你运行它,你会看到单元格以非常高的速度移动,如果你查看代码,你会看到我调用repaint()paint我覆盖的方法。我知道这样做不好。但我的问题是,他们是否有任何其他方式可以在repaint()我现在拥有的循环之外绘制每个单元格,因为当我在 JFrame 中与其他组件一起使用它时,这会导致其他组件闪烁/闪烁。

仅供参考:最终我想实现类似的目标:单击此处

谢谢!

4

1 回答 1

6

闪烁的问题与顶级容器没有双缓冲这一事实有关。而不是从JFrame(或其他顶级容器)扩展,您应该考虑使用更像JPanel并覆盖它的paintComponent.

nb-在我的脑海中,OP 是从JFrame...延伸出来的。

有两个问题可能导致闪烁。第一个是覆盖paint,第二个是不调用super.paint(g)和更新之间的时间。更好的解决方案是覆盖paintComponent并确保您正在调用super.paintComponent. 还使用类似 ajavax.swing.Timer来安排更新和定期间隔也会有所帮助......

repaint在您想鼓励RepaintManager更新您的组件时调用。不要repaint从任何paintXxx方法中调用,这将导致绘制请求的无休止循环调度到事件队列中,最终消耗你的 CPU

我会避免在你的方法中做任何paintXxx可能需要时间来执行的事情,这会减慢渲染过程。相反,我将使用 ajavax.swing.Timer进行简单更新或更复杂的处理,Thread可用于在模型呈现到屏幕之前更新模型。

这是我所做的一些简单优化过程的示例,将 500 个对象的动画处理为 4500 个对象,整体性能仅略有下降。

更新

我稍微改变了你的代码,它工作正常......

在此处输入图像描述

我将您的paint方法更改为paintComponent并添加super.paintComponent(g)

@Override
protected void paintComponent(Graphics g) {
    super.paintComponent(g);
    int w = getWidth();
    int h = getHeight();
    final Graphics2D g2 = (Graphics2D) g;
    GradientPaint gp = new GradientPaint(-w, -h, Color.LIGHT_GRAY, w, h, Color.DARK_GRAY);
    g2.setPaint(gp);
    g2.fillRect(0, 0, w, h);
    for (int i = 0; i < lightcells.size(); i++) {
        LightCell cell = lightcells.get(i);
        cell.process(g2);
    }
}

在你的构造函数的最后,我添加了......

Timer timer = new Timer(40, new ActionListener() {
    @Override
    public void actionPerformed(ActionEvent e) {
        repaint();
    }
});
timer.start();

定期更新用户界面...

于 2013-09-16T00:11:01.683 回答