简体   繁体   English

没有图形的Java GUI淡入淡出动画

[英]Java GUI fading animation without graphics

I'm making a game using Java GUIs, and I'm trying to do some fading animations. 我正在使用Java GUI进行游戏,并且尝试制作一些淡入淡出的动画。 I know that it is much easier using Java graphics, but for this, I'm using JLabels and Swing so I can't really do that. 我知道使用Java图形要容易得多,但是为此,我正在使用JLabels和Swing,所以我实际上不能做到这一点。 The fading animation I'm trying to do is where I change the background color of a JLabel to go from one color to the next in a gradual manner, so the way I tried to do it was to create an array of colors and have a TimerTask set the background of the JLabel to each color one by one. 我想做的褪色动画是我将JLabel的背景颜色逐渐从一种颜色变为另一种颜色,所以我尝试这样做的方法是创建一系列颜色并具有一个TimerTask将JLabel的背景一一设置为每种颜色。

TimerTask task = new TimerTask() {
    @Override
    public void run() {
        setBackground(c);
    }
};
tasks[i] = task;

This is my TimerTask. 这是我的TimerTask。 The color c is a color I put here as my example for a color that is partly between my original and final colors. 颜色c是我在此处作为示例使用的颜色,该颜色部分介于原始颜色和最终颜色之间。 For example, if I was trying to fade from black to white, the program would ideally first set the color to a dark grey, then a medium grey, then a lighter grey, then eventually the final white color. 例如,如果我尝试从黑色渐变为白色,则程序最好先将颜色设置为深灰色,然后将颜色设置为中灰,再将其设置为浅灰色,最后设置为最终的白色。 I tested out those TimerTasks, and they didn't seem to have any issues, so I try to execute them one by one using a Timer, but it doesn't seem to execute every time. 我测试了这些TimerTasks,它们似乎没有任何问题,因此我尝试使用Timer逐个执行它们,但似乎并非每次都执行。

Timer timer = new Timer();
for (int i = 0; i < f; i++) {
    timer.schedule(tasks[i], delay);
}

What usually ends up happening is that the colors fade partially, and the JLabel just ends up stuck on some color that is halfway between the original and final colors, as if not all tasks that were scheduled were completed. 通常最终会发生的是,颜色会部分褪色,而JLabel最终会停留在介于原始颜色和最终颜色之间的某种颜色上,好像并不是所有预定的任务都已完成。 What am I doing wrong, and is it even sensible trying to do animations with JLabels? 我在做什么错,尝试用JLabels制作动画甚至明智吗? Thanks in advance! 提前致谢!


EDIT: I'm sorry that I wasn't very clear with my description earlier. 编辑:对不起,我之前的描述不太清楚。 Perhaps it may be easier if I provided some parts of my original code, and I'll try to explain what I try to have it do bit by bit. 如果我提供了原始代码的某些部分,可能会更容易些,并且我将尝试解释我尝试使其逐步执行的操作。

/** Create array of colors **/
Color[] animationColors = new Color[f];      // f is the number of frames
// find difference between old and new values of r, g, b
int diffR = newColor.getRed() - oldColor.getRed();
int diffG = newColor.getGreen() - oldColor.getGreen();
int diffB = newColor.getBlue() - oldColor.getBlue();
// fill array with colors
for (int i = 0; i < f; i++) {
    int newR = (int)(diffR * (i + 1) / f) + oldColor.getRed();
    int newG = (int)(diffG * (i + 1) / f) + oldColor.getGreen();
    int newB = (int)(diffB * (i + 1) / f) + oldColor.getBlue();
    Color c = new Color(newR, newG, newB);
    animationColors[i] = c;
}

/** Set new background after each delay **/
int delay = (int)(t / f);         // t is the time that the animation will last in total
Timer timer = new Timer();
TimerTask task = new TimerTask() {
    @Override
    public void run() {
        animationCount++;
        if (animationCount == animationColors.length) {
            animationCount = 0;
            timer.cancel();
        } else {
            setBackground(animationColors[animationCount]);
        }
    }
};
timer.schedule(task, delay, delay);

First, I try to do the fading animation by creating an array of the colors that the JLabel will be set to after each short time interval. 首先,我尝试通过创建在每个短时间间隔后将JLabel设置为的颜色数组来进行淡入淡出动画。 I designated a number of frames that the animation will take place over, and at frame 1, the color of the JLabel will be set to animationColors at position 1, the color at frame 2 will be set to animationColors at position 2 etc. Each frame is a gradual fade from the first color to the final color, which for a fade from black to white will start with a darker grey, then a medium grey, a lighter grey, then eventually white. 我指定了要进行动画处理的多个帧,在第1帧,JLabel的颜色将在位置1处设置为animationColors,在第2帧的颜色将在位置2处设置为animationColors,以此类推。每帧是从第一种颜色到最终颜色的渐变,从黑色到白色的渐变将先从深灰色开始,然后是中灰,浅灰色,最后是白色。

The problem here is that quite often, the color of the tile does not fade completely. 这里的问题是,瓷砖的颜色通常不会完全褪色。 Sometimes, the color that it fades to ends up being the original color instead of what I set to be the final color. 有时,它淡出的颜色最终将变为原始颜色,而不是我设置为最终颜色的颜色。 Could the issue be with the way I'm writing the timer, or is using a timer in a GUI application a bad idea in general? 问题可能出在我编写计时器的方式上,还是在GUI应用程序中使用计时器通常是个坏主意? I read the responses to this question so far and I didn't quite understand a few of them so if you could explain them more that would be appreciated too. 到目前为止,我已经阅读了对该问题的回答,但我对其中的一些内容还不太了解,因此,如果您能进一步解释它们,也将不胜感激。

Many Swing components don't deal well with translucency, especially when it is changing. 许多Swing组件不能很好地处理半透明性,尤其是当它发生变化时。

Here is an example that draws the label as a BufferedImage that can then be used as the icon of another label. 这是一个将标签绘制为BufferedImage的示例,然后可以将其用作另一个标签的图标。 Using this approach loses a lot of the functionality of a standard label, but might suffice. 使用这种方法会失去标准标签的许多功能,但可能就足够了。

import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
import javax.swing.*;
import javax.swing.border.EmptyBorder;
import java.net.*;

public class FadingLabel {

    private JComponent ui = null;
    private BufferedImage fadeImage;
    private BufferedImage clearImage;
    private JLabel opaqueLabel;
    private JLabel fadeLabel;

    FadingLabel() {
        try {
            initUI();
        } catch (MalformedURLException ex) {
            ex.printStackTrace();
        }
    }

    public final void initUI() throws MalformedURLException {
        if (ui!=null) return;

        ui = new JPanel(new BorderLayout(4,4));
        ui.setBorder(new EmptyBorder(4,4,4,4));
        URL url = new URL("https://i.stack.imgur.com/F0JHK.png");
        initialiseImages(url, "Fade me!");
        fadeLabel = new JLabel(new ImageIcon(fadeImage));
        ui.add(fadeLabel);

        ActionListener fadeListener = new ActionListener() {

            float transparency;
            float difference = .1f;

            @Override
            public void actionPerformed(ActionEvent e) {
                transparency += difference;
                if (transparency>=1f) {
                    difference = -.01f;
                    transparency = 1f;
                }
                if (transparency<=0f) {
                    difference = .01f;
                    transparency = 0f;
                }
                fadeImage(transparency);
                fadeLabel.repaint();
            }
        };
        Timer timer = new Timer(30, fadeListener);
        timer.start();
    }

    private void fadeImage(float transparency) {
        Dimension d = opaqueLabel.getSize();
        fadeImage.setData(clearImage.getData());
        Graphics2D g = fadeImage.createGraphics();
        Composite composite = AlphaComposite.getInstance(AlphaComposite.SRC, transparency);
        g.setComposite(composite);
        opaqueLabel.paint(g);
    }

    private void initialiseImages(URL iconURL, String text) {
        ImageIcon ii = new ImageIcon(iconURL);
        opaqueLabel = new JLabel(text, ii, SwingConstants.LEADING);
        opaqueLabel.setForeground(Color.BLUE);
        opaqueLabel.setSize(opaqueLabel.getPreferredSize());
        Dimension d = opaqueLabel.getSize();
        clearImage = new BufferedImage(d.width, d.height, BufferedImage.TYPE_INT_ARGB);
        fadeImage = new BufferedImage(d.width, d.height, BufferedImage.TYPE_INT_ARGB);
        Graphics g = fadeImage.getGraphics();
        opaqueLabel.paint(g);
    }

    public JComponent getUI() {
        return ui;
    }

    public static void main(String[] args) {
        Runnable r = () -> {
            try {
                UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
            } catch (Exception useDefault) {
            }
            FadingLabel o = new FadingLabel();

            JFrame f = new JFrame(o.getClass().getSimpleName());
            f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            f.setLocationByPlatform(true);

            f.setContentPane(o.getUI());
            f.pack();
            f.setMinimumSize(f.getSize());

            f.setVisible(true);
        };
        SwingUtilities.invokeLater(r);
    }
}

@AndrewThompson, @TheUltimateMonkey said no Graphics, but It's a good answer, but going towards no Graphics, use this: @ AndrewThompson,@ TheUltimateMonkey表示没有图形,但这是一个很好的答案,但是对于没有图形的人,请使用以下命令:

try {

    JFrame frm = new JFrame("Fading Label");
    JLabel lbl = new JLabel("The background is changing!");

    lbl.setOpaque(true);
    lbl.setBackground(new Color(0, 0, 0));

    frm.add(lbl);
    frm.setDefaultCloseOperation(3);
    frm.pack();
    frm.setVisible(true);

    for(int a = 0; a < 256; a++) {

        lbl.setBackground(new Color(a, a, a));
        Thread.sleep(10);

    }

}
catch(Exception e) {}

But of you want to try java.util.Timer , then use this: 但是您想尝试java.util.Timer ,然后使用:

try {

    JFrame frm = new JFrame("Fading Label");
    JLabel lbl = new JLabel("The background is changing!");

    lbl.setOpaque(true);
    lbl.setBackground(new Color(0, 0, 0));

    frm.add(lbl);
    frm.setDefaultCloseOperation(3);
    frm.pack();
    frm.setVisible(true);

    Timer tmr = new Timer();
    tmr.schedule(new TimerTask() {

        @Override
        public void run() {

            int a = lbl.getBackground().getRed();
            lbl.setBackground(new Color(a + 1, a + 1, a + 1));
            if(a == 254) System.exit(0); //This prevents the app from crashing, you can do something else to fix this.

        }

    }, 10, 10);

}
catch(Exception e) {}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM