js动画补间Tween

博客 动态
0 231
羽尘
羽尘 2022-05-28 14:59:46
悬赏:0 积分 收藏

js 动画补间 Tween

  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 }
完整代码
 
 
 
TweenValue (从 原点 以规定的时间到达  终点)

parameter: origin, end, time, onUpdate, onEnd;

attribute:
    origin: Object; //原点(起点)
    end: Object; //终点
    time: Number; //origin 到 end 花费的时间
    onUpdate: Function; //更新回调; 一个回调参数 origin; 默认null;
    onEnd: Function; //结束回调; 没有回调参数; 默认null; (如果返回的是true将不从队列删除, 你可以在onEnd中更新.end不间断的继续补间)

method:
    reset(origin, end: Object): undefined; //更换 .origin, .end; 它会清除其它对象的缓存属性
    reverse(): undefined; //this.end 复制 this.origin 的原始值
    update(): undefined; //Tween 通过此方法统一更新 TweenValue

demo:
    //init Tween:
    const tween = new Tween(),
    animate = function (){
        requestAnimationFrame(animate);
        tween.update();
    }

    //init TweenValue:
    const v1 = new Tween.Value({x:0, y:0}, {x:5, y:10}, 1000, v => console.log(v));
   
    animate();
    tween.start(v1);
 
 
 
 
 
TweenAlone (相对于 TweenValue 此类可以独立补间, 不需要 Tween)

demo:
    const v1 = new TweenAlone({x:0, y:0}, {x:5, y:10}, 1000, v => console.log(v)),
    animate = function (){
        requestAnimationFrame(animate);
        v1.update();
    }

    animate();
    v1.start();
 
 
 
 
 
Tween 动画补间 (TweenValue 的root, 可以管理多个TweenValue)

parameter:
attribute:
method:
    start(value: TweenValue): undefined;
    stop(value: TweenValue): undefined;

static:
    Value: TweenValue;

demo:
    //init Tween:
    const tween = new Tween(),
    animate = function (){
        requestAnimationFrame(animate);
        tween.update();
    }

    //init TweenValue:
    const v2 = new Tween.Value({x:0, y:0}, {x:5, y:10}, 1000, v => console.log(v), v => {
        v2.reverse(); //v2.end 复制起始值
        return true; //返回true表示不删除队列, 需要继续补间
    });
   
    animate();
    tween.start(v2);
 
 
 
 
 
TweenTargetChange 朝着轴插值(有效的跟踪动态目标)
parameter:  
    v1 = {x: 0},
    v2 = {x: 100},
    distance = 1,       //每次移动的距离
    onUpdate = null,    //
    onEnd = null

attribute:
    v1: Object;             //起点
    v2: Object;             //终点
    onUpdate: Function;     //
    onEnd: Function;        //

method:
    update(): undefined;                        //一般在动画循环里执行此方法
    updateAxis(): undefined;                    //更新v1至v2的方向轴 (初始化时构造器自动调用一次)
    setDistance(distance: Number): undefined;   //设置每次移动的距离 (初始化时构造器自动调用一次)

demo:
    const ttc = new TweenTargetChange({x:0, y:0}, {x:100, y:100}, 10),

    //计时器模拟动画循环函数, 每秒执行一次.update()
    timer = new Timer(() => {
        ttc.update();
        console.log('update: ', ttc.v1);

    }, 1000, Infinity);

    ttc.onEnd = v => {
        timer.stop();
        console.log('end: ', v);
    }

    timer.start();
    console.log(ttc);
posted @ 2022-05-28 14:44 鸡儿er 阅读(0) 评论(0) 编辑 收藏 举报
回帖
    羽尘

    羽尘 (王者 段位)

    2335 积分 (2)粉丝 (11)源码

     

    温馨提示

    亦奇源码

    最新会员