2017-02-03 43 views
0

我正在寻找执行一些物理动画并在一组DOM元素上执行动画。不是画布。 非常重要:不是画布。Angular2物理动画

我有它的工作,但性能比我预期的要慢,即使考虑了昂贵的DOM操作是多么昂贵。即使您将时间间隔调整为不太频繁,如果您一次在页面上有多个组件,它将无法使用。

我想知道是否有一个更简单或更高性能的方式,同时保持在Angular中的东西。也许一种方法可以跳过区域的角度渲染?做它香草没有利用角绑定等方式更高性能,所以我想知道如果我只是做错误的角度部分,或者如果我应该打破这些部分免费的Angular。我认为区域应该超越全球操纵......虽然......?

示例代码扭动在屏幕上的东西(真正的动画都比较复杂,但遵循这个确切的技术):

@Component({ 
    selector: 'thingy,[thingy]', 
    template: `<div #container [ngStyle]="getStyle()"><ng-content></ng-content></div>` 
}) 


export class Thingy implements OnInit, OnDestroy { 
    private _x:number = 0; 
    private _y:number = 0; 
    private _interval:any; 
    private _style:CSSStyleDeclaration = { 
     left: 0, 
     top: 0, 
     position: absolute 
    }; 

    constructor(){} 

    ngOnInit() { 
     this._interval = setInterval(() => { 
      this._x++; 
      this._y = Math.sin(this._x); 

      this._style.left = this._x + "px"; 
      this._style.top = this._y + "px"; 
     }); 
    } 

    ngOnDestroy() { 
     clearInterval(this._interval); // because it continues ticking after destroy 
    } 

    getStyle():CSSStyleDeclaration { 
     return this._style; // in angular1 it was bad joojoo to return a new object each time so i avoid doing so here too 
    } 
} 

我已经优化了这种做法,就像我知道怎么办。我认为内置的动画元数据解决方案可以处理大多数场景,但是我没有尝试过,因为a)我无法想象如何添加更多抽象提高性能,以及b)这些动画不是状态转换,因此它看起来不像适当。

我也使用更多的这样的一个模板尝试,但它似乎并没有太大的不同:

<div [style.top.px]="_y" [style.left.px]="_x"><ng-content></ng-content></div> 

而且,我已经试过直接与ElementRef搞乱但肯定没帮助:

@ViewChild("container") container:ElementRef; 
this._container.nativeElement.styles.top = this._y + "px"; 

如果最好在Angular的控制之外做到这一点,那么有没有什么标准呢?我可以用一个Component绘制DOM元素,然后发送一个Window事件来启动非角度代码...

另外值得注意的是:我不能从A点开始并立即跳到B点以便让CSS转换画动画。动画不足以过渡/缓解。除非这是一个非常聪明的解决方案,否则我不会看到它是如何动画的,除了在每个步骤中滴答滴答。

回答

0

我通过使用全局代码而不是让每个组件负责它自己来解决性能问题。股票代码是一个简单的Observable,每个组件都订阅并取消订阅,否则它将开始并停止它自己的时间间隔。

import {Injectable} from '@angular/core'; 
import {Observer, Observable} from 'rxjs'; 

@Injectable() 
export class TickerService { 
    private _observer: Observer<number>; 
    private _timer:any; 
    public data$ = new Observable(observer => this._observer = observer).share(); 

    constructor() { 
     this.data$ = new Observable(observer => { 
      this._observer = observer; 
     }).share(); 
    } 

    public start():void { 
     if(this._timer) { // not required... just didn't want to have two going at once 
      this.stop(); 
     } 

     this._timer = setInterval(() => { 
      this.tick(); 
     }, 30); 
    } 

    public stop():void { 
     clearInterval(this._timer); 
     this._timer = 0; 
    } 

    private tick():void { 
     this._observer.next(new Date().getTime()); // the date part is irrelevant. i just wanted to use it to track the performance lag between each tick 
    } 
} 

项目可以将它们对其做出反应如此:

@Component({ 
    selector: 'thingy,[thingy]', 
    template: `<div #container [ngStyle]="getStyle()"><ng-content></ng-content></div>` 
}) 


export class Thingy implements OnInit, OnDestroy { 
    private _x:number = 0; 
    private _y:number = 0; 
    private _subscription:Subscription; 
    private _style:CSSStyleDeclaration = { 
     left: 0, 
     top: 0, 
     position: absolute 
    }; 

    constructor(private _ticker:TickerService){} 

    ngOnInit() { 
     this._subscription = this._ticker.data$.subscribe(() => { 
      this._x++; 
      this._y = Math.sin(this._x); 

      this._style.left = this._x + "px"; 
      this._style.top = this._y + "px"; 
     }); 
     this._ticker.start(); // even though every instance will call this, there's a guard in the TickerService to only allow one ticker to run 
    } 

    ngOnDestroy() { 
     this._subscription.unsubscribe(); 
    } 

    getStyle():CSSStyleDeclaration { 
     return this._style; 
    } 
} 

这个小变化了性能5个Thingys约5fps的约60fps的。我相信这是因为每个Thingy都会产生自己的股票,导致自己的(封锁)摘要/绘画与其他人不一致。这导致Angular单独消化每个更改,并建立起一个巨大的堆栈。而不是一个滴答5个更新执行,它是执行5个滴答,每个更新1次。

所以现在,所有的改变都是在一个摘要/区域更新中完成并执行的。

+0

出于好奇,你有没有尝试在Angular 2中使用内置的Renderer'setElementStyle',而不是通过ngStyle指令来应用样式? – jsfrocha

+0

我还没有试过渲染器。我会尝试(可能是几天),并更新我发现的。 – oooyaya