当前位置: 首页 > 知识库问答 >
问题:

如何在Java中保持graphic2D形状保持一段时间?

程志新
2023-03-14

我将为我的后期项目创建一个移动的圆圈,圆圈将继续移动,它的内部颜色将像颜色发射一样变化,变化的颜色将在5个级别中从小圆圈到大圆圈,所以如何保持每个颜色的变化保持一段时间,我希望这些代码与线程一起出现,所以我创建了两个线程,一个控制圆圈移动,另一个控制圆圈的内部颜色发射,这里是我的代码:

import java.awt.*;
import static java.awt.Color.black;
import static java.awt.Color.yellow;
import static java.awt.FlowLayout.RIGHT;
import java.awt.event.*;
import java.awt.geom.Arc2D;
import java.awt.geom.Ellipse2D;
import java.awt.geom.GeneralPath;
import java.awt.geom.Rectangle2D;
import static java.lang.Math.abs;
import java.util.Random;
import javax.swing.*;
import java.util.concurrent.ExecutorService;
class thepane extends JPanel implements Runnable{
   public float x,y,r;
   public float speedx,speedy;
    thepane(float lx,float ly,float lr, float sx,float sy){
        loadspeed(sx,sy);
        load(lx,ly,lr);
        for(int i=0;i<5;i++)
            fc[i]=new Color(nd.nextInt(255),nd.nextInt(255),nd.nextInt(255)); 
    }

    public void load(float lx,float ly,float lr){   
        x=lx;y=ly;r=lr;
    }
    public void loadspeed(float sx,float sy){
        speedx=sx;speedy=sy;
    }
    public void xmoving(){
        x+=speedx;
    }
    public void ymoving(){
        y-=speedy;
    }
    public void touchbond(){
        if(x>getWidth()-r||x<0)
         speedx*=-1;   
        if(y>getHeight()-r||y<0)
        speedy*=-1;          
        if(x>getWidth()-r)
            x=getWidth()-r;
        else if(x<0)
            x=0;
        if(y>getHeight()-r)
            y=getHeight()-r;
        else if(y<0)
            y=0;
    }         
    Random nd=new Random();   
    int colorcount=0;
    int emitcount=0;
    boolean emit=false;
    Color[] fc=new Color[5]; 
    Graphics2D  comp2D ;     
    Thread athread;

    @Override
    public void paintComponent(Graphics comp) {        
        comp2D = (Graphics2D) comp;
        //create rectangle background
        comp2D.setColor(Color.BLACK);
        comp2D.fillRect(0, 0, getWidth(), getHeight());
        //set information text
        comp2D.setFont( new Font("Arial", Font.BOLD, 12));
        comp2D.setColor(Color.WHITE);
        comp2D.drawString("Centre("+(x+r/2)+' '+(y+r/2)+"), xspeed: "+speedx+" yspeed: "+speedy, 10f,10f );
        comp2D.drawString("panel width "+getWidth()+" panel height "+getHeight()+" circle radius "
                +r, 10f, 22f);
    }
    //thread run()
    @Override
    public void run() {     
        x=100;y=100;
        System.out.println("thread in pane start!!!! (current colorcount = "+colorcount+')');       
        while(true){  
        circleEmit(fc[colorcount%5]);
        repaint();
       sleeping(1);
       // comp2D=(Graphics2D)this.getGraphics();
      //  colorEmit(comp2D);
         }
       }
    //wait method
   public void waiting(){
    try{wait();}
    catch(Exception e){}}
    public void waiting2D(int time){
    try{comp2D.wait(time);}
    catch(Exception e){}
    }
    public void waiting(int time){
    try{wait(time);}
    catch(Exception e){}
    }    
    //sleep method
    public void sleeping(int n){
      try{
          Thread.sleep(n);
          }catch(Exception f){
          System.out.print(f);
          }    
    }  
    Ellipse2D.Float[] e=new Ellipse2D.Float[5];
    public void loade(){
        float centrex=x+r/2,centrey=y+r/2;
        e[0]= new Ellipse2D.Float(centrex-r/10, centrey-r/10, r/5, r/5);
        e[1]= new Ellipse2D.Float(centrex-r/5, centrey-r/5, 2*r/5, 2*r/5);
        e[2]= new Ellipse2D.Float(centrex-3*r/10, centrey-3*r/10, 3*r/5, 3*r/5);
        e[3]= new Ellipse2D.Float(centrex-2*r/5, centrey-2*r/5, 4*r/5, 4*r/5);
        e[4]= new Ellipse2D.Float(centrex-r/2, centrey-r/2, r, r); 
    }   
    public Color ff;
    public synchronized void circleEmit(Color fc){
        comp2D=(Graphics2D)this.getGraphics();
        loade();
        comp2D.setColor(fc);
        comp2D.fill(e[emitcount%5]);
        waiting(5);
        emitcount++;
    }          
    public synchronized void callnotify(){
        this.notify();       
    }
       //iterative way to generate color emit        
    public  void colorEmit(Graphics2D comp2D){
         //create circle
        //set circle property    
        float centrex=x+r/2,centrey=y+r/2;//so x=centrex-r/2;y=centrey+r/2  
        Ellipse2D.Float e1 = new Ellipse2D.Float(centrex-r/10, centrey-r/10, r/5, r/5);
        Ellipse2D.Float e2 = new Ellipse2D.Float(centrex-r/5, centrey-r/5, 2*r/5, 2*r/5);
        Ellipse2D.Float e3 = new Ellipse2D.Float(centrex-3*r/10, centrey-3*r/10, 3*r/5, 3*r/5);
        Ellipse2D.Float e4 = new Ellipse2D.Float(centrex-2*r/5, centrey-2*r/5, 4*r/5, 4*r/5);
        Ellipse2D.Float e5 = new Ellipse2D.Float(centrex-r/2, centrey-r/2, r, r);  
            if(colorcount>=4)
                emit(comp2D,fc[(colorcount-4)%5],e5);
            waiting(1000);
            if(colorcount>=3)
                emit(comp2D,fc[(colorcount-3)%5],e4);   
            waiting(1000);
            if(colorcount>=2)
                emit(comp2D,fc[(colorcount-2)%5],e3);    
            waiting(1000);
            if(colorcount>=1)
                emit(comp2D,fc[(colorcount-1)%5],e2);    
            waiting(1000);
            emit(comp2D,fc[colorcount%5],e1);            
            waiting(1000);
            colorcount++;        
    }
    private void emit(Graphics2D comp,Color thecolor,Ellipse2D.Float f){
            comp.setColor(thecolor);
            comp.fill(f);    
    }
}

//------------------------------------------------------------------------------------
//main class
public class drawpanel extends Thread implements ActionListener{
       JFrame frame=new JFrame();
       thepane panel;    
       JButton FlyingBalls=new JButton("balls"),exit=new JButton("Exit"),stop=new JButton("Stop");
       JButton slow=new JButton("slow down"),resume=new JButton("resume");
       Float x,y,r;

    public void sleeping(int n){
      try{
          Thread.sleep(n);
          }catch(Exception f){
          System.out.print(f);
          }    
    }   
    Thread newthread,pthread;
    Thread[] five=new Thread[5];
    drawpanel(){
       frame.setTitle("FlyingBalls");
       frame.setLocation(100, 100);
       frame.setLayout(null);
       //x,y,r,speedx,speedy
       panel=new thepane(nd.nextInt(800),nd.nextInt(500),40,nd.nextFloat()*20+1,nd.nextFloat()*10+1);
       panel.setSize(800,500);
       frame.setSize(810,580);
       frame.add(panel);      
       FlyingBalls.setSize(80,30);exit.setSize(70,30);stop.setSize(70,30);slow.setSize(140,30);
       resume.setSize(100,30);
       FlyingBalls.addActionListener(this);
       exit.addActionListener(this);
       stop.addActionListener(this);slow.addActionListener(this);resume.addActionListener(this);
       frame.add(FlyingBalls);frame.add(exit); frame.add(stop);frame.add(slow);frame.add(resume);
       FlyingBalls.setLocation(20,500);exit.setLocation(190, 500);stop.setLocation(110,500);
       slow.setLocation(270,500);resume.setLocation(420,500);
       frame.setVisible(true);
       frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
       
       //control moving ball
       newthread=new Thread(this);
       //control color change
       for(int i=0;i<5;i++){
       five[i]=new Thread(panel);
       }
      // newthread.start();
           panel.colorcount++;
           five[0].start();
           panel.colorcount=2;
          // five[1].start();
           panel.waiting(5);
    }
    
   public static void main(String[] arg){
       drawpanel apanel=new drawpanel();     
    }
   int bw=800,bh=500;
   void setp(){
       x=panel.x;y=panel.y;
   }
   void touchbond(){
       System.out.println("width:"+panel.getWidth()+"Height:"+panel.getHeight());
       System.out.println("xposition:"+x+"yposition:"+y);
   if(x+r>panel.getWidth()){
       panel.speedx*=-1;
       x=bw-r;
   }
   else if(x-r<0){
       panel.speedx*=-1;
       x=r;
   }
   if(y-r<0){
       panel.speedy*=-1;
       y=r;
   }
   else if(y+r>panel.getHeight()){
       panel.speedy*=-1;
       y=bh-r;
   }
   panel.x=x;panel.y=y;
   }
   int T=10;
   Random nd=new Random();
       @Override
   public void run(){     
       r=panel.r;      
       panel.loadspeed(-6.33f,-3.4f);
       while(true){      
       if(stopcount==0){//button control variable
       panel.xmoving();panel.ymoving(); 
       panel.touchbond();
       sleeping(T);}
       panel.loade();
   //    panel.callnotify();
      // panel.colorEmit(panel.comp2D);
       panel.repaint();
       }
   }
       @Override
   public void start(){
       
   }
   int count=0,stopcount=0;
    @Override
    public void actionPerformed(ActionEvent e) {
        if(e.getSource()==exit){
            System.exit(0);
        }
        if(e.getSource()==FlyingBalls){
 
            //panel=new thepane();
        }
        if(e.getSource()==resume){
            stopcount=0;T=10;
            panel.emit=false;
        }
        if(e.getSource()==slow){
            if(count%2==0)
            T=500;
            else
                T=10;
            count++;
        }
        if(e.getSource()==stop){
        stopcount++;
        panel.emit=true;
        }
    }
}

共有1个答案

步联
2023-03-14

所以,有很多理论要讨论。

动画不容易,好的动画很难。

这意味着您不应该在事件分派线程的上下文中执行任何长时间运行或阻塞操作。

基于时间的动画也更有能力产生“地役”效果,但这是越来越深入,然后我们现在需要去。

好吧,但这和你的问题有什么关系?嗯,实际上,相当多。

我们需要的第一件事是某种“主循环”,从它可以驱动所有的动画。通常,我会寻找一个好的动画库,但如果做不到这一点,一个简单的Swing计时器就能很好地完成基本的功能。

private Timer timer;

//...

timer = new Timer(5, new ActionListener() {
    @Override
    public void actionPerformed(ActionEvent e) {
        // Update the state
        repaint();
    }
});

//...

timer.start();
public class AnimationDuration {
    private Duration duration;
    private Instant startedAt;

    public AnimationDuration(Duration duration) {
        this.duration = duration;
    }

    public Duration getDuration() {
        return duration;
    }
    
    public void start() {
        startedAt = Instant.now();
    }

    public void stop() {
        startedAt = null;
    }

    public boolean isRunning() {
        return startedAt != null;
    }
    
    public float getProgress() {
        Duration runningTime = Duration.between(startedAt, Instant.now());
        if (runningTime.compareTo(duration) > 0) {
            runningTime = duration;
        }
        long total = duration.toMillis();
        float progress = runningTime.toMillis() / (float) total;
        
        return progress;
    }
    
}

这基本上允许触发动画开始运行(定位时间点),然后在任何时间点获取动画的进度。这提供了一个从0-1开始的规范化概念,所以如果我们想让它变得更长或更短,我们所做的就是调整持续时间,其他一切都得到了照顾。

对于您的具体问题,我会考虑某种“时间线”或“关键帧”,它定义了在时间线上的某些时间点上应该发生的某些动作。

现在,下面是一个非常简单的概念,但它得到了工作。

public interface KeyFrame {
    public float getProgress();
}

public class TimeLine<K extends KeyFrame> {

    private List<K> keyFrames;

    public TimeLine() {
        keyFrames = new ArrayList<>(25);
    }

    // Returns the key frames between the current progression
    public K getKeyFrameAt(float progress) {
        for (int index = 0; index < keyFrames.size(); index++) {
            K keyFrame = keyFrames.get(index);
            if (progress >= keyFrame.getProgress()) {
                if (index + 1 < keyFrames.size()) {
                    K nextFrame = keyFrames.get(index + 1);
                    // But only if your between each other
                    if (progress < nextFrame.getProgress()) {
                        return keyFrame;
                    }
                } else {
                    // Nothing after me :D
                    return keyFrame;
                }
            }
        }
        return null;
    }

    public void add(K keyFrame) {
        keyFrames.add(keyFrame);
        Collections.sort(keyFrames, new Comparator<KeyFrame>() {
            @Override
            public int compare(KeyFrame lhs, KeyFrame rhs) {
                if (lhs.getProgress() > rhs.getProgress()) {
                    return 1;
                } else if (lhs.getProgress() < rhs.getProgress()) {
                    return -1;
                }

                return 0;
            }
        });
    }
}
public class CirclePropertiesKeyFrame implements KeyFrame {

    private float progress;

    private double radius;
    private Color color;

    public CirclePropertiesKeyFrame(float progress, double radius, Color color) {
        this.progress = progress;
        this.radius = radius;
        this.color = color;
    }

    @Override
    public float getProgress() {
        return progress;
    }

    public Color getColor() {
        return color;
    }

    public double getRadius() {
        return radius;
    }

    @Override
    public String toString() {
        return "KeyFrame progress = " + getProgress() + "; raidus= " + radius + "; color = " + color;
    }

}
public class TestPane extends JPanel {

    private AnimationDuration timelineDuration;
    private TimeLine<CirclePropertiesKeyFrame> timeLine;
    private Timer timer;

    private CirclePropertiesKeyFrame circleProperties;

    public TestPane() {
        timelineDuration = new AnimationDuration(Duration.ofSeconds(10));
        timeLine = new TimeLine<>();
        timeLine.add(new CirclePropertiesKeyFrame(0, 5, Color.CYAN));
        timeLine.add(new CirclePropertiesKeyFrame(0.2f, 10, Color.BLUE));
        timeLine.add(new CirclePropertiesKeyFrame(0.4f, 15, Color.GREEN));
        timeLine.add(new CirclePropertiesKeyFrame(0.6f, 20, Color.YELLOW));
        timeLine.add(new CirclePropertiesKeyFrame(0.8f, 25, Color.MAGENTA));
        timer = new Timer(5, new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                if (timelineDuration.isRunning()) {
                    float progress = timelineDuration.getProgress();
                    if (progress >= 1.0) {
                        timelineDuration.stop();
                    }
                    CirclePropertiesKeyFrame keyFrame = timeLine.getKeyFrameAt(progress);
                    circleProperties = keyFrame;
                }
                repaint();
            }
        });
    }

    @Override
    public void addNotify() {
        super.addNotify();
        timelineDuration.start();
        timer.start();
    }

    @Override
    public void removeNotify() {
        super.removeNotify();
        timer.stop();
        timelineDuration.stop();
    }

    @Override
    public Dimension getPreferredSize() {
        return new Dimension(200, 200);
    }

    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        Graphics2D g2d = (Graphics2D) g.create();
        if (circleProperties != null) {
            double radius = circleProperties.radius;
            double xPos = (getWidth() / 2) - radius;
            double yPos = (getHeight() / 2) - radius;

            g2d.setColor(circleProperties.color);
            g2d.fill(new Ellipse2D.Double(xPos, yPos, radius * 2, radius * 2));
        }
        g2d.dispose();
    }

}

然后我们就得到了类似于...

现在,这是一个10秒的序列,所以每2秒它就会更新一次。尝试更改AnimationDuration的持续时间,看看会发生什么。

注意这是一个不重复的动画(它没有循环)。您可以让它循环,但是这样做的计算变得更加复杂,因为您需要考虑超出预期的持续时间的时间,然后将其应用到下一个循环中,所以看起来很顺利

 类似资料:
  • 问题内容: 我正在使用POST方法。我需要创建一次,并且应该使用Keep Alive Connection。但是我认为,它每次都会建立一个新的连接。 因此,我需要使用 保持活动 连接。 这是我的代码段,很多帮助将不胜感激。 而且logcat日志是: 问题答案: 10:07:29.746:D / org.apache.http.headers(1529):>>连接:保持活动 您正在要求保持活动状态。

  • 视图(在本例中,ViewPager)有自己的保存和恢复状态的实现,我依靠它在设备旋转后将ViewPager保持在同一页面上。 当ViewPager在活动中时,它工作得非常好。我滚动到一个页面,旋转设备,它仍然在那个页面上。在SaveInstanceState中不需要任何代码。 我已经把那个活动转化成了一个片段。现在,ViewPager无法保持其状态!如果我滚动到一个页面并旋转设备,它会返回到第1页

  • HTTP协议是无状态的:每次请求都是一次新的请求,不会记得之前通信的状态 客户端与服务器端的一次通信,就是一次会话 实现状态保持的方式:在客户端或服务器端存储与会话有关的数据 存储方式包括cookie、session,会话一般指session对象 使用cookie,所有数据存储在客户端,注意不要存储敏感信息 推荐使用sesison方式,所有数据存储在服务器端,在客户端cookie中存储sessio

  • 问题内容: Redux文档的第一个原理是: 整个应用程序的状态存储在单个存储中的对象树中。 我实际上以为我很好地理解了所有原则。但是我现在对应用程序的含义感到困惑。 我知道,如果应用程序只是网站中复杂的小部分之一,并且仅用一页即可工作。但是,如果申请意味着整个网站怎么办?我应该使用LocalStorage还是cookie或其他保留状态树的东西?但是如果浏览器不支持LocalStorage怎么办?

  • 问题内容: 当我从一个视图控制器移动到另一个视图控制器时,第一个控制器上的开关会重置自身,并且不会保持其状态。查看其他控制器后,如何恢复状态?以及在关闭应用程序后如何使其保存状态。我看过各种stackOverflow问题和响应以及Apple文档,但似乎没有任何效果。 这是我的带有开关的View Controller的类。 我是Swift和Xcode的初学者。预先感谢您的时间和帮助:) 问题答案:

  • 所以,我有一个按钮,在黑暗和光明模式之间切换(我的网站默认是黑暗的),它可以工作,但现在我需要它保持在任何切换状态,在多个页面选择。我怀疑这和会话存储有关。也不想使用jQuery。我可以在代码中添加什么来实现这一点呢? 我有五个页面都链接到了id为“dark”的styles.css,然后在JS中我引用了第二个样式表light.css或JS中的“light”,所以我要切换样式表。所有五个页面的页脚都