我正在寻找一些物理动画,并在一组DOM元素上执行动画。不是画布。 非常重要:不是画布。
我有它的工作,但即使考虑到DOM操作有多昂贵,性能也比我预期的要慢。如果您一次在页面上有多个组件,即使您将间隔调整为较低频率,它也会无法使用。
我想知道在保持Angular中的内容时是否有更简单或更高效的方式。也许是一种完全跳过区域角度渲染的方法?在不使用Angular绑定的情况下使用vanilla这样做方式更高效,所以我想知道我是否只是将Angular部分做错了,或者我是否应该将这些部分从Angular中删除。我认为Zones应该超越全球操纵但是......?
在屏幕上摆动某些东西的示例代码(真实的动画更复杂,但遵循这个确切的技术):
@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事件来kickstart非角度代码...
另外值得注意的是:我无法从A点开始并立即跳转到B点,以便让CSS过渡绘制动画。动画的可预测性不足以过渡/轻松。除非是一个非常聪明的解决方案,否则除了勾选每一步之外,我看不出它是如何动画的。
答案 0 :(得分: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次滴答,每次更新1次,而不是一次打5次更新。
现在,所有更改都会生成,然后在一个摘要/区域更新中执行。