将鼠标移到Chrome网页元素上时,计时器会加快速度

时间:2016-08-24 15:39:41

标签: java swing google-chrome animation

我创造了一段时间,以显示一天中高速公路上的密度。数据保存在double[][]数组data中,其中data.length为2880(每个索引代表30秒间隔),data[0].length约为450(表示三次插值)穿过高速公路路段的长度。)

我的时间间隔代码如下:

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.ObjectInputStream;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.Timer;

public class TimeLapse extends JPanel {
    static double[][] data;
    int index = 0;
    double max;
    double lineWidth;
    Timer timer;
    final JProgressBar progressBar = new JProgressBar(0, 2879);
    BufferedImage[] images;
    Color colors[][];

    public static void main(String[] args) {
      data=getData(); //arbitrary method to get interpolated data
      new TimeLapse(data, 90);
    }

    public TimeLapse(double[][] data1, double max) {
      data = data1;
      this.max = max;
      JFrame frame = new JFrame("Timelapse");
      frame.setPreferredSize(new Dimension(800, 600));
      frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      frame.setLayout(new GridBagLayout());
      GridBagConstraints c = new GridBagConstraints();
      c.fill = GridBagConstraints.BOTH;
      c.anchor = GridBagConstraints.NORTH;
      c.gridwidth = 1;
      c.gridx = 0;
      c.gridheight = 1;
      c.weightx = 1;
      c.weighty = .01;
      c.gridy = 0;
      frame.add(progressBar, c);
      c.anchor = GridBagConstraints.SOUTH;
      c.gridy = 1;
      c.gridheight = 9;
      c.weighty = 1;
      frame.add(this, c);
      frame.pack();
      getColorArray();
      frame.setVisible(true);
      int dataLength;
      dataLength = data.length;
      // Make the animation 5 seconds long
      int delay = (int) (5000d / dataLength);
      timer = new Timer(delay, new ActionListener() {
        @Override
        public void actionPerformed(ActionEvent e) {
          updateIndex();
          repaint();
          Toolkit.getDefaultToolkit().sync();
        }
      });
      timer.start();
    }

    private void getColorArray() {
      double cutOff = max / 2;
      colors = new Color[data.length][data[0].length];
      for (int index = 0; index < data.length; index++) {
        for (double x = 0; x < data[0].length; x++) {
          colors[index][(int) x] =
            getColor(data[index][(int) x], cutOff);
        }
      }
    }

    private void updateIndex() {
      index = index < data.length - 1 ? index + 1 : 0;
      progressBar.setValue(2879 * index / data.length);
    }

    @Override
    public void paintComponent(Graphics g) {
      super.paintComponent(g);
      Graphics2D g2 = (Graphics2D) g;
      int panelHeight = getHeight();
      lineWidth = ((double) getWidth()) / ((double) (data[0].length));
      // guaranteed counter, as doing it in timer's ActionListener could overlap with rendering

      for (double x = 0; x < data[0].length; x++) {
        g2.setColor(colors[index][(int) x]);
        double rectHeight = panelHeight * data[index][(int) x] / max;
        g2.fillRect((int) (x * lineWidth),
            (int) (panelHeight - rectHeight),
            (int) (lineWidth + 1), (int) rectHeight + 1);
      }
      g2.dispose();
    }

    private Color getColor(double value, double cutOff) {
      int hueR, hueG, hueB = 0;
      if (value < cutOff) {
        hueG = 255;
        hueR = (int) (255 * value / (cutOff));
      } else if (max != cutOff) {
        hueR = 255;
        hueG = (int) (255 - (255 * (value - cutOff) / (max - cutOff)));
      } else {
        hueR = 255;
        hueG = 0;
      }

      hueR = (hueR < 0) ? 0 : ((hueR > 255) ? 255 : hueR);
      hueG = (hueG < 0) ? 0 : ((hueG > 255) ? 255 : hueG);
      hueB = (hueB < 0) ? 0 : ((hueB > 255) ? 255 : hueB);
      return new Color(hueR, hueG, hueB);
    }
}

动画功能很流畅,但它通常需要的时间比我设置的五秒钟要长很多,我可以在面板中对数百行进行恒定着色和生成。

为了验证我是否正确并且它确实比它应该慢得多,我使用了Google“小秒表”计时时出现的Google Chrome小部件。这样做,我发现当我运行秒表时,动画加速,以及每当我将鼠标移动到某些元素上时(超链接,顶部的标签,以及看似任何其他可以对鼠标悬停的视觉响应) 。这只发生在我移动鼠标或运行秒表时;保持鼠标仍然不会加快速度,并且它似乎只有在将鼠标悬停在Chrome上时才有这种行为(即任何其他应用程序都可以)。任何人都可以解释这种奇怪的行为吗?

编辑:重新加载标签时也会发生这种情况,但重新加载后却没有。

编辑2:我现在肯定知道计时器正在加速。我创建了一个带有计时器的小类,该计时器每毫秒打印一个增加的索引:

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.Timer;

public class TimerTest {
    static int index = 0;

    public static void main(String[] args) {
      Timer t = new Timer(1, new ActionListener() {
        @Override
        public void actionPerformed(ActionEvent e) {
        System.out.println(index++);
        }
      });
      t.start();

      while (true) {
      }
    }
}

这与时间推移类完全相同,当将鼠标移动到Chrome的元素上时,速度会大大加快。我相信原因是,在这种情况下,println是一种慢速方法,执行速度比定时器更新慢。同样,有人可以解释为什么Chrome专门加速备份计时器吗?

1 个答案:

答案 0 :(得分:1)

绘画方法仅适用于绘画。

您不应该在绘画方法中更改类的属性。

当Swing确定需要重新绘制组件时,您无法控制。因此可能会有一些系统调用导致组件重新绘制,因此比您想象的更频繁地更改属性。

例如,您不应该更新“索引”变量或进度条值。相反,你的Timer应该调用一个方法来改变这些属性,然后该方法应该调用面板上的重绘。

  

仅当我移动鼠标

时才会发生这种情况

也许你在面板上有工具提示会导致它被重新绘制。

这很容易测试,只需将一个System.out.println(...)语句添加到paintComponent()方法,看它是否比计时器的5秒更频繁地显示。