Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/334.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 防止摆动动画跳跃_Java_Swing - Fatal编程技术网

Java 防止摆动动画跳跃

Java 防止摆动动画跳跃,java,swing,Java,Swing,我已经用一个JPanel和一个javax.swing.Timer制作了这个基本的例子,我希望动画相对平滑 如果我保持鼠标在窗口上移动,动画将平滑。如果我根本不与窗口交互,则动画开始跳跃 import javax.swing.*; import java.awt.*; public class SmoothSwing{ double x = 0; double y = 0; double theta = 0; public void step(){

我已经用一个JPanel和一个javax.swing.Timer制作了这个基本的例子,我希望动画相对平滑

如果我保持鼠标在窗口上移动,动画将平滑。如果我根本不与窗口交互,则动画开始跳跃

import javax.swing.*;
import java.awt.*;

public class SmoothSwing{
    double x = 0;
    double y = 0;
    double theta = 0;

    public void step(){
        x = 175 + 175*Math.sin( theta );
        y = 175 + 175*Math.cos( theta );
        theta += 0.02;
        if(theta > 6.28) theta = 0;
    }

    public void buildGui(){
        JFrame frame = new JFrame("smooth");
        JPanel panel = new JPanel(){
            Dimension dim = new Dimension(400, 400);
            @Override
            public void paintComponent(Graphics g){
                super.paintComponent(g);
                g.drawOval((int)x, (int)y, 50, 50);
            }
            @Override
            public Dimension getMinimumSize(){
                return dim;
            }
            @Override
            public Dimension getPreferredSize(){
                return dim;
            }
        };
        frame.add(panel, BorderLayout.CENTER);
        frame.setVisible(true);
        frame.pack();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        Timer timer = new Timer(30, evt->{
            step();
            panel.repaint();
        });
        timer.start();
    }
    public static void main(String[] args){
        EventQueue.invokeLater( new SmoothSwing()::buildGui );
    }

}
在我看来,重新粉刷的颜色在不断累积,并成团地重新粉刷。所以我注释掉了super.paintcomponent这行;这将导致面板无法清除

当我这样做时,我可以看到paintComponent正在运行,因为所有的圆都在绘制中,但显示仍然只有在绘制了多个圆之后才会更新

我在使用Linux的jdk-14+36、jdk-11.0.6和1.8.0_181上遇到了这个问题。Ubuntu 18.04

下面是比较差异的图表。我很难控制GIF的速度,但这说明了正在发生的事情

从左到右,如果我把鼠标移到窗口上,我会看到左边的行为,当我停下来后,中间会有点跳跃,然后右边会更跳跃

我可以做一件事来避免这个问题,那就是把我的计时器分成两个任务

Timer t1 = new Timer(30, evt->step());
Timer t2 = new Timer(3, evt->panel.repaint());
t1.start();
t2.start();

可以通过以下方式实现更平滑的动画: -使用更精细的θ步 -更新频率更高 -使用双值绘制:

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.geom.Ellipse2D;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;

public class SmoothSwing{
    double x = 0;
    double y = 0;
    double theta = 0;

    public void step(){
        x = 175 + 175*Math.sin( theta );
        y = 175 + 175*Math.cos( theta );
        theta += 0.002; //finer theta step
        if(theta > 6.28) {
            theta = 0;
        }
    }

    public void buildGui(){
        JFrame frame = new JFrame("smooth");
        JPanel panel = new JPanel(){
            Dimension dim = new Dimension(400, 400);
            @Override
            public void paintComponent(Graphics g){
                super.paintComponent(g);
                Graphics2D g2d = (Graphics2D) g;
                g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
                g2d.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE);
                g2d.draw(new Ellipse2D.Double(x, y, 50, 50));
            }
            @Override
            public Dimension getMinimumSize(){
                return dim;
            }
            @Override
            public Dimension getPreferredSize(){
                return dim;
            }
        };
        frame.add(panel, BorderLayout.CENTER);
        frame.setVisible(true);
        frame.pack();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        Timer timer = new Timer(3, evt->{ //higher frequency update
            step();
            panel.repaint();
        });
        timer.start();
    }
    public static void main(String[] args){
        EventQueue.invokeLater( new SmoothSwing()::buildGui );
    }   
}

可以通过以下方式实现更平滑的动画: -使用更精细的θ步 -更新频率更高 -使用双值绘制:

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.geom.Ellipse2D;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;

public class SmoothSwing{
    double x = 0;
    double y = 0;
    double theta = 0;

    public void step(){
        x = 175 + 175*Math.sin( theta );
        y = 175 + 175*Math.cos( theta );
        theta += 0.002; //finer theta step
        if(theta > 6.28) {
            theta = 0;
        }
    }

    public void buildGui(){
        JFrame frame = new JFrame("smooth");
        JPanel panel = new JPanel(){
            Dimension dim = new Dimension(400, 400);
            @Override
            public void paintComponent(Graphics g){
                super.paintComponent(g);
                Graphics2D g2d = (Graphics2D) g;
                g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
                g2d.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE);
                g2d.draw(new Ellipse2D.Double(x, y, 50, 50));
            }
            @Override
            public Dimension getMinimumSize(){
                return dim;
            }
            @Override
            public Dimension getPreferredSize(){
                return dim;
            }
        };
        frame.add(panel, BorderLayout.CENTER);
        frame.setVisible(true);
        frame.pack();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        Timer timer = new Timer(3, evt->{ //higher frequency update
            step();
            panel.repaint();
        });
        timer.start();
    }
    public static void main(String[] args){
        EventQueue.invokeLater( new SmoothSwing()::buildGui );
    }   
}

当我运行you示例或cOder示例时,我没有注意到运动随时间的变化。尽我所能,它保持不变

在我看来,重新粉刷的颜色在不断累积,并成团地重新粉刷

我现在要绕过Swing中的RepaitManager,唯一的方法就是使用paintInstance…方法。这会导致立即绘制零部件,而不会添加到EDT的末尾。不建议使用它,因为它会降低整体喷漆性能

任何更新类状态的方法都应该是类的一部分,而不是类的外部。所以,我修改了你的原始代码。这允许我在步骤方法中使用绘画逻辑:

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.geom.Ellipse2D;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;

public class SmoothSwing2 extends JPanel
{
    double x = 0;
    double y = 0;
    double theta = 0;

    Dimension dim = new Dimension(400, 400);

    @Override
    public void paintComponent(Graphics g)
    {
        super.paintComponent(g);
        Graphics2D g2d = (Graphics2D) g;
        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        g2d.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE);
        g2d.draw(new Ellipse2D.Double(x, y, 50, 50));
    }

    @Override
    public Dimension getMinimumSize()
    {
        return dim;
    }

    @Override
    public Dimension getPreferredSize()
    {
        return dim;
    }

    public void step()
    {
        x = 175 + 175*Math.sin( theta );
        y = 175 + 175*Math.cos( theta );
        theta += 0.008; //finer theta step
        if(theta > 6.28) {
            theta = 0;
        }

        repaint();
//      paintImmediately( getBounds() ); // repaints the entire panel
//      paintImmediately(x - 5, y - 5, 60, 60); // repaint only the circle

    }

    public void buildGui()
    {
        SmoothSwing panel = new SmoothSwing();

        JFrame frame = new JFrame("smooth");
        frame.add(panel, BorderLayout.CENTER);
        frame.setVisible(true);
        frame.pack();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        Timer timer = new Timer(3, evt-> panel.step)
        timer.start();
    }
    public static void main(String[] args){
        EventQueue.invokeLater( new SmoothSwing2()::buildGui );
    }
}
我没有注意到使用这三种方法中的任何一种都有任何区别

如果您继续注意到重绘的累积,那么可能是您的操作系统没有不断地为应用程序提供CPU


我在Windows 10上使用JDK 11。

当我运行you示例或cOder示例时,我没有注意到运动随时间的变化。尽我所能,它保持不变

在我看来,重新粉刷的颜色在不断累积,并成团地重新粉刷

我现在要绕过Swing中的RepaitManager,唯一的方法就是使用paintInstance…方法。这会导致立即绘制零部件,而不会添加到EDT的末尾。不建议使用它,因为它会降低整体喷漆性能

任何更新类状态的方法都应该是类的一部分,而不是类的外部。所以,我修改了你的原始代码。这允许我在步骤方法中使用绘画逻辑:

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.geom.Ellipse2D;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;

public class SmoothSwing2 extends JPanel
{
    double x = 0;
    double y = 0;
    double theta = 0;

    Dimension dim = new Dimension(400, 400);

    @Override
    public void paintComponent(Graphics g)
    {
        super.paintComponent(g);
        Graphics2D g2d = (Graphics2D) g;
        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        g2d.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE);
        g2d.draw(new Ellipse2D.Double(x, y, 50, 50));
    }

    @Override
    public Dimension getMinimumSize()
    {
        return dim;
    }

    @Override
    public Dimension getPreferredSize()
    {
        return dim;
    }

    public void step()
    {
        x = 175 + 175*Math.sin( theta );
        y = 175 + 175*Math.cos( theta );
        theta += 0.008; //finer theta step
        if(theta > 6.28) {
            theta = 0;
        }

        repaint();
//      paintImmediately( getBounds() ); // repaints the entire panel
//      paintImmediately(x - 5, y - 5, 60, 60); // repaint only the circle

    }

    public void buildGui()
    {
        SmoothSwing panel = new SmoothSwing();

        JFrame frame = new JFrame("smooth");
        frame.add(panel, BorderLayout.CENTER);
        frame.setVisible(true);
        frame.pack();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        Timer timer = new Timer(3, evt-> panel.step)
        timer.start();
    }
    public static void main(String[] args){
        EventQueue.invokeLater( new SmoothSwing2()::buildGui );
    }
}
我没有注意到使用这三种方法中的任何一种都有任何区别

如果您继续注意到重绘的累积,那么可能是您的操作系统没有不断地为应用程序提供CPU


我在Windows 10上使用JDK 11。

如果您提前完成所有计算,并将结果存储在ArrayList(可能是ArrayList)中,然后简单地在计时器中遍历一个或多个列表,会发生什么情况?这样会更平滑吗?Graphics2D g2d=Graphics2D g;g2d.setRenderingHintRenderingHints.KEY\u抗锯齿,RenderingHints.VALUE\u抗锯齿\u开启;g2d.SetRenderingHintrenderingHingHints.KEY\u STROKE\u控件,RenderingHints.VALUE\u STROKE\u PURE;g2d.drawnew Ellipse2D.Doublex,y,50,50@HovercraftFullOfEels我尝试制作一个点列表,但它没有改变任何东西。@c0我很惊讶渲染提示对帧速率有任何影响,但当我使用渲染提示时,它只会降低1级的速度。默认情况下,javax.swing.Timer合并事件。我在想,也许你之所以会有这种行为,是因为事件的结合。如果是这样,请确保在计时器上调用setCoalescefalse并重试。无论如何,我仍然无法重现错误。我只是提醒你试着用假的,甚至是真的。对于较小的延迟,跳跃可能会变得更加明显。如果您提前进行所有计算,并将结果存储在ArrayList(可能是ArrayList)中,然后简单地在计时器中的一个或多个列表中进行迭代,会发生什么情况?这样会更平滑吗?Graphics2D g2d=Graphics2D g;g2d.setRenderingHintRenderingHints.KEY\u抗锯齿,RenderingHints.VALUE\u抗锯齿\u开启;g2d.SetRenderingHintrenderingHingHints.KEY\u STROKE\u控件,RenderingHints.VALUE\u STROKE\u PURE;
g2d.drawnew Ellipse2D.Doublex,y,50,50@HovercraftFullOfEels我尝试制作一个点列表,但它没有改变任何东西。@c0我很惊讶渲染提示对帧速率有任何影响,但当我使用渲染提示时,它只会降低1级的速度。默认情况下,javax.swing.Timer合并事件。我在想,也许你之所以会有这种行为,是因为事件的结合。如果是这样,请确保在计时器上调用setCoalescefalse并重试。无论如何,我仍然无法重现错误。我只是提醒你试着用假的,甚至是真的。对于较小的延迟,跳跃可能会变得更加明显。问题不是θ太细,而是动画是分块绘制的。它开始时很好,但一旦没有输入,帧速率就会减慢,圆圈移动的步数也会变大。然后再等上一段时间,它的速度会减慢得更多,而且会走得更远。如果我将鼠标移到窗口上,动画将以预期的方式继续运行。您是否尝试了我发布的代码?这个解决方案的问题是,它增加了整体帧速率。目前我将帧速率设置为~30hz,您的解决方案将帧速率设置为~300hz。我很惊讶它解决了块中运动的问题,但是为什么我不能将帧速率设置为30hz呢?为什么它最终开始变慢。根据这个答案,我应该以我喜欢的速度运行我的动画,但只是用重绘来淹没摇摆。不是300Hz来完成它,而是更精细的θ。你可以期待粗糙的步伐和平稳的运动。不抱歉。我看不出来。我也不认为老鼠胡佛有效果。我可以看到,当x和y都改变时,圆会以小跳跃的方式移动。当x接近0时,运动更平滑圆在顶部或底部,当y左右接近0时。问题不是θ太细,动画是分块绘制的。它开始时很好,但一旦没有输入,帧速率就会减慢,圆圈移动的步数也会变大。然后再等上一段时间,它的速度会减慢得更多,而且会走得更远。如果我将鼠标移到窗口上,动画将以预期的方式继续运行。您是否尝试了我发布的代码?这个解决方案的问题是,它增加了整体帧速率。目前我将帧速率设置为~30hz,您的解决方案将帧速率设置为~300hz。我很惊讶它解决了块中运动的问题,但是为什么我不能将帧速率设置为30hz呢?为什么它最终开始变慢。根据这个答案,我应该以我喜欢的速度运行我的动画,但只是用重绘来淹没摇摆。不是300Hz来完成它,而是更精细的θ。你可以期待粗糙的步伐和平稳的运动。不抱歉。我看不出来。我也不认为老鼠胡佛有效果。我可以看到,当x和y都改变时,圆会以小跳跃的方式移动。当x接近0时,运动会更平滑。圆圈在顶部或底部,当y左右接近0时。我相信它也可能是操作系统。由于未调用super.paintComponent生成的图像绘制了所有的圆,我怀疑重新绘制会导致paintComponent以所需的速率启动,但操作系统只是没有显示更新的图形。不过,这对我来说是非常系统的。我停止移动指针,一段时间后帧速率下降。我相信这也可能是操作系统造成的。由于未调用super.paintComponent生成的图像绘制了所有的圆,我怀疑重新绘制会导致paintComponent以所需的速率启动,但操作系统只是没有显示更新的图形。不过,这对我来说是非常系统的。我停止移动指针,过了一会儿帧速率下降。