feiyu02
2025-03-26 da0d06cb06ef3fc55d88cb4a9a52505ac35c03e6
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
import moment from 'moment';
 
/**
 * 帧动画控制工具
 *
 */
function FrameAnimation() {
  // 动画帧数
  this.fps = 12;
  // 帧间隔(ms)
  this.timeout = 1000 / this.fps;
  this.speed = 1;
  this.animationTask = [];
  // 单次插入最多任务数
  this.maxTasks = 10;
  this.onNextTask;
 
  this.isPause = false;
  this.isRunning = false;
  this.isStop = true;
  // 是否开启动态绘制速度
  this.dynamicSpeed = false;
  // 每个任务耗时(秒)
  this.taskPeriod = 4;
  // 数据最新时间
  this.lastestTime;
}
 
FrameAnimation.prototype = {
  start: function () {
    // 不存在动画任务时重新开始,否则继续
    if (this.intervalFlag == undefined) {
      this._doTask();
    }
    this.isPause = false;
    this.isStop = false;
    this.isRunning = true;
  },
  /**
   * 动态动画速度
   * 当页面失去焦点时,循环任务js会暂停,此时后续的轨迹动画就会被延后;
   * 同时,由于浏览器性能问题,当运行时间逐渐变长时,轨迹动画与实际时间的差值也会逐渐拉大;
   * 因此需要动态调整动画运行倍速,与实际时间保持相近
   */
  dynamicAdjustment: function (time) {
    if (this.dynamicSpeed && this.lastestTime) {
      // 以两个坐标点(一次task)为最小单位,根据当前绘制数据时间和最新数据时间的差值,调整绘制速度
      var t1 = moment(time);
      var t2 = moment(this.lastestTime);
      var sec = Math.abs(t1.diff(t2, 'seconds')); // 获取时间差
 
      // 当时间差超过单任务耗时时,需要适当加快动画速度
      if (sec > this.taskPeriod) {
        this.speed = Math.round(sec / this.taskPeriod);
        if (this.speed > 16) {
          // this.speed = 16
        }
      } else {
        this.speed = 1;
      }
    }
  },
  changeSpeed: function (speed) {
    if (typeof speed === 'number') {
      this.speed = speed;
    }
  },
  pause: function () {
    this.isPause = true;
    this.isRunning = false;
  },
  stop: function () {
    if (this.isStop) return;
 
    if (this.intervalFlag != undefined) {
      clearInterval(this.intervalFlag);
      this.intervalFlag = undefined;
      this.animationTask = [];
    }
 
    // 当前任务列表结束后,首先判断是否有下一段任务
    if (this.onNextTask) {
      this.onNextTask();
      return;
    }
 
    if (typeof this.onStopCallback === 'function') {
      this.onStopCallback();
    }
 
    this.isRunning = false;
    this.isPause = false;
    this.isStop = true;
    this.lastestTime = undefined;
    this.speed = 1;
  },
  setOnStopCallback: function (callback) {
    this.onStopCallback = callback;
  },
  addTask: function (count, data, callback) {
    var task = {
      data: data,
      count: count,
      task: callback
    };
    this.animationTask.push(task);
    // console.log('FrameAnimation: addTask ' + count);
  },
  // 任务采取分段执行方式,即当当前任务列表执行完成后,继续开始处理下一段任务
  addOnNextTasks: function (callback) {
    this.onNextTask = callback;
  },
  runStatus: function () {
    return this.isRunning;
  },
  _doTask: function () {
    if (this.animationTask.length > 0) {
      var t = this.animationTask[0];
 
      // 动态调整速度
      this.dynamicAdjustment(t.data.times[t.data.times.length - 1]);
 
      var index = 0;
      this.intervalFlag = setInterval(() => {
        if (this.isPause) {
          return;
        }
        // 绘制3D图形时,最少需要2个点才可绘制图形
        // 因此此处索引只到倒数第二个点就结束
        if (index >= t.count - 1) {
          this._endTask(this.intervalFlag);
          return;
        }
        t.task(t.data, index, t.count);
 
        index += this.speed;
      }, this.timeout);
    }
  },
  _endTask: function (intervalFlag) {
    clearInterval(intervalFlag);
    this.animationTask.splice(0, 1);
    if (this.animationTask.length > 0) {
      this._doTask();
    } else {
      this.stop();
    }
  }
};
 
export { FrameAnimation };