当鼠标移动到Chrome网页元素上时,计时器会加速



我创建了一个延时图来显示一天中高速公路上的交通密度。数据保存在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);
    }
}

动画运行流畅,但它通常需要比我设置的5秒长得多,我将其归因于面板中数百条线的不断着色和生成。

为了验证我是正确的,它确实比它应该慢得多,我使用了谷歌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) {
      }
    }
}

这与time lapse类的行为完全相同,当将鼠标移动到Chrome元素上时,速度会大大加快。我认为原因是,在这种情况下,println是一个缓慢的方法,执行速度比计时器更新慢。还是那句话,有人能解释一下为什么Chrome会特别加快备份计时器的速度吗?

绘画方法仅用于绘画。

你不应该在绘画方法中改变类的属性。

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

例如,你不应该更新你的"index"变量或进度条值。相反,你的计时器应该调用一个方法来改变这些属性,然后该方法应该调用面板上的重绘。

只有当我移动鼠标

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

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

最新更新