

1 /* RunningList (触发过程中可以安全的删除自己) 2 如果触发过程中删除(回调函数中删除正在遍历的数组), 不仅 len 没有变(遍历前定义的len没有变, 真实的len随之减少), 而且还会漏掉一个key; 3 4 */ 5 class RunningList{ 6 7 static getProxy(runName){ 8 9 return new Proxy(new RunningList(runName), { 10 11 get(tar, key){ 12 13 }, 14 15 set(tar, key, val){ 16 17 } 18 19 }); 20 21 } 22 23 constructor(runName = 'update'){ 24 this._running = false; 25 this._list = []; 26 this._delList = []; 27 this._runName = runName; 28 29 } 30 31 get length(){ 32 return this._list.length; 33 } 34 35 push(v){ 36 this._list.push(v); 37 38 } 39 40 splice(v){ 41 if(this._running === true) this._delList.push(v); 42 43 else{ 44 const i = this._list.indexOf(v); 45 if(i !== -1) this._list.splice(i, 1); 46 } 47 48 } 49 50 update(){ 51 var k, len = this._list.length; 52 53 this._running = true; 54 for(k = 0; k < len; k++) this._list[k][this._runName](); 55 this._running = false; 56 57 len = this._delList.length; 58 for(k = 0; k < len; k++) this.splice(this._delList[k]); 59 this._delList.length = 0; 60 61 } 62 63 } 64 65 66 67 68 /* TweenValue (从 原点 以规定的时间到达 终点) 69 70 parameter: origin, end, time, onUpdate, onEnd; 71 72 attribute: 73 origin: Object; //原点(起点) 74 end: Object; //终点 75 time: Number; //origin 到 end 花费的时间 76 onUpdate: Function; //更新回调; 一个回调参数 origin; 默认null; 77 onEnd: Function; //结束回调; 没有回调参数; 默认null; (如果返回的是true将不从队列删除, 你可以在onEnd中更新.end不间断的继续补间) 78 79 method: 80 reset(origin, end: Object): undefined; //更换 .origin, .end; 它会清除其它对象的缓存属性 81 reverse(): undefined; //this.end 复制 this.origin 的原始值 82 update(): undefined; //Tween 通过此方法统一更新 TweenValue 83 84 demo: 85 //init Tween: 86 const tween = new Tween(), 87 animate = function (){ 88 requestAnimationFrame(animate); 89 tween.update(); 90 } 91 92 //init TweenValue: 93 const v1 = new Tween.Value({x:0, y:0}, {x:5, y:10}, 1000, v => console.log(v)); 94 95 animate(); 96 tween.start(v1); 97 98 */ 99 class TweenValue{100 101 constructor(origin = {}, end = {}, time = 500, onUpdate = null, onEnd = null, onStart = null){102 this.origin = origin;103 this.end = end;104 this.time = time;105 106 this.onUpdate = onUpdate;107 this.onEnd = onEnd;108 this.onStart = onStart;109 110 //以下属性不能直接设置111 this._r = null;112 this._t = 0;113 this._v = Object.create(null);114 115 }116 117 _start(){118 var v = "";119 for(v in this.origin) this._v[v] = this.origin[v];120 121 this._t = Date.now();122 //this.update();123 124 }125 126 reset(origin, end){127 this.origin = origin;128 this.end = end;129 this._v = Object.create(null);130 131 }132 133 reverse(){134 var n = "";135 for(n in this.origin) this.end[n] = this._v[n];136 137 }138 139 update(){140 if(this["_r"] !== null){141 142 var ted = Date["now"]() - this["_t"];143 144 if(ted >= this["time"]){145 146 for(ted in this["origin"]) this["origin"][ted] = this["end"][ted];147 if(this["onUpdate"] !== null) this["onUpdate"](this["origin"]);148 149 if(this["onEnd"] !== null){150 151 if(this["onEnd"]() !== true){152 if(this["_r"] !== null) this["_r"]["stop"](this);153 }154 155 else this["_start"]();156 157 }158 159 else this["_r"]["stop"](this);160 161 }162 163 else{164 let n = "";165 ted = ted / this["time"];166 for(n in this["origin"]) this["origin"][n] = ted * (this["end"][n] - this["_v"][n]) + this["_v"][n];167 if(this["onUpdate"] !== null) this["onUpdate"](this["origin"]);168 169 }170 171 }172 173 }174 175 }176 177 Object.defineProperties(TweenValue.prototype, {178 179 isTweenValue: {180 configurable: false,181 enumerable: false,182 writable: false,183 value: true,184 }185 186 });187 188 189 190 191 /* TweenAlone (相对于 TweenValue 此类可以独立补间, 不需要 Tween)192 193 demo:194 const v1 = new TweenAlone({x:0, y:0}, {x:5, y:10}, 1000, v => console.log(v)),195 animate = function (){196 requestAnimationFrame(animate);197 v1.update();198 }199 200 animate();201 v1.start();202 203 */204 class TweenAlone extends TweenValue{205 206 constructor(origin, end, time, onUpdate, onEnd, onStart){207 super(origin, end, time, onUpdate, onEnd, onStart);208 209 }210 211 start(){212 if(this.onStart !== null) this.onStart();213 this._r = this;214 this._start();215 216 }217 218 stop(){219 this._r = null;220 221 }222 223 }224 225 226 227 228 229 /* Tween 动画补间 (TweenValue 的root, 可以管理多个TweenValue)230 231 parameter:232 attribute:233 method:234 start(value: TweenValue): undefined;235 stop(value: TweenValue): undefined;236 237 static:238 Value: TweenValue;239 240 demo:241 //init Tween:242 const tween = new Tween(),243 animate = function (){244 requestAnimationFrame(animate);245 tween.update();246 }247 248 //init TweenValue:249 const v2 = new Tween.Value({x:0, y:0}, {x:5, y:10}, 1000, v => console.log(v), v => {250 v2.reverse(); //v2.end 复制起始值251 return true; //返回true表示不删除队列, 需要继续补间252 });253 254 animate();255 tween.start(v2);256 257 */258 class Tween extends RunningList{259 260 static Value = TweenValue;261 262 constructor(){263 super();264 265 }266 267 start(value){268 if(value.onStart !== null) value.onStart();269 if(value._r === null) this.push(value);270 value._r = this;271 value._start(this);272 273 }274 275 stop(value){276 if(value._r !== null) this.splice(value);277 value._r = null;278 279 }280 281 }282 283 284 285 286 /* TweenTargetChange 朝着轴插值(有效的跟踪动态目标)287 parameter: 288 v1 = {x: 0}, 289 v2 = {x: 100}, 290 distance = 1, //每次移动的距离291 onUpdate = null, //292 onEnd = null293 294 attribute:295 v1: Object; //起点296 v2: Object; //终点297 onUpdate: Function; //298 onEnd: Function; //299 300 method:301 update(): undefined; //一般在动画循环里执行此方法302 updateAxis(): undefined; //更新v1至v2的方向轴 (初始化时构造器自动调用一次)303 setDistance(distance: Number): undefined; //设置每次移动的距离 (初始化时构造器自动调用一次)304 305 demo:306 const ttc = new TweenTargetChange({x:0, y:0}, {x:100, y:100}, 10),307 308 //计时器模拟动画循环函数, 每秒执行一次.update()309 timer = new Timer(() => {310 ttc.update();311 console.log('update: ', ttc.v1);312 313 }, 1000, Infinity);314 315 ttc.onEnd = v => {316 timer.stop();317 console.log('end: ', v);318 }319 320 timer.start();321 console.log(ttc);322 323 */324 class TweenTargetChange{325 326 #distance = 1;327 #distancePow2 = 1;328 #axis = {};329 330 constructor(v1 = {x: 0}, v2 = {x: 100}, distance, onUpdate = null, onEnd = null){331 this.v1 = v1;332 this.v2 = v2;333 this.onUpdate = onUpdate;334 this.onEnd = onEnd;335 336 this.setDistance(distance);337 this.updateAxis();338 }339 340 setDistance(v = 1){341 this.#distance = v;342 this.#distancePow2 = Math.pow(v, 2);343 344 }345 346 updateAxis(){ //更新轴347 var n, v, len = 0;348 349 for(n in this.v1){350 v = this.v2[n] - this.v1[n];351 len += v * v;352 this.#axis[n] = v;353 354 }355 356 len = Math.sqrt(len);357 358 if(len !== 0){359 360 for(n in this.v1) this.#axis[n] *= 1 / len;361 362 }363 364 }365 366 update(){367 var n, len = 0;368 369 for(n in this.v1) len += Math.pow(this.v1[n] - this.v2[n], 2);370 371 if(len > this.#distancePow2){372 373 for(n in this.v1) this.v1[n] += this.#axis[n] * this.#distance;374 if(this.onUpdate !== null) this.onUpdate(this.v1);375 376 }377 378 else{379 for(n in this.v1) this.v1[n] = this.v2[n];380 if(this.onEnd !== null) this.onEnd(this.v1);381 382 }383 384 }385 386 }