我有一个显示图像网格的Angular(不是js)应用程序。该网格非常大,因此会自动滚动用户浏览器窗口,并在一定时间间隔内随机选择并显示其中一张图像(使用灯箱)。在大多数情况下,这一切都很好。
但是,在某些交互过程中,例如当用户手动取消选择显示的图像时,或者当用户离开“视图”页面导航时,很明显,TimerObservable的运行自动滚动和自动选择不会被破坏。
这会导致2种有害的副作用;首先,即使在其他不适合滚动的页面上(这些其他组件甚至都没有自动滚动代码!),浏览器窗口仍会继续向上/向下滚动。其次,滚动速度变成了两倍,就像已经创建了两个TimerObservable一样,并且都在滚动文档窗口。
这是我的组件代码(删除了无关的内容):
您可以在此处看到我创建了2个Subscription变量来保存在StartAutoScroll()
和StartAutoSelector()
函数中创建的订阅。然后,我使用StopUI()
和StartUI()
函数停止/取消订阅/销毁这些订阅,但是正如我之前所说,这不会总是发生,nudge()
&即使我离开该组件(有时是有时)或自己手动取消选择灯箱,仍会调用focusTile()
函数。
import { Component, OnInit, ViewEncapsulation} from '@angular/core';
import { Router, ActivatedRoute, ParamMap } from '@angular/router';
import { switchMap } from 'rxjs/operators';
import { TimerObservable } from 'rxjs/observable/TimerObservable';
import { TileViewService } from '../services/tile-view.service';
import { TileView } from '../models/TileView';
import { Lightbox, LIGHTBOX_EVENT, LightboxEvent } from 'ngx-lightbox';
import { WindowRefService } from '../services/window-ref.service';
import { Subscription } from 'rxjs';
@Component({
selector: 'app-tile-view',
templateUrl: './tile-view.component.html',
styleUrls: ['./tile-view.component.scss'],
encapsulation: ViewEncapsulation.None
})
export class TileViewComponent implements OnInit {
// These are the subscriptions causing issues
public isAutoScrolling : Boolean = true;
private autoScroller: Subscription;
public isAutoSelecting : Boolean = true;
private autoSelector: Subscription;
tileView: TileView;
private tileAlbum: Array<any> = [];
public isFrozen : Boolean;
private nudgeValue = 1;
public focusTileDelay = 8000;
public focusTileDuration = 5000;
private lightboxSubscription : Subscription;
private albumIndicies: Array<number> = [];
constructor(
private route: ActivatedRoute,
private router: Router,
private tileService: TileViewService,
private windowRef: WindowRefService,
private lightbox: Lightbox,
private lightboxEvent: LightboxEvent
) { }
ngOnDestroy() {
console.info("tile-view destroyed!");
this.stopUI();
}
ngOnInit() {
var result = this.route.paramMap.pipe(
switchMap((params: ParamMap) =>
this.tileService.getTileById(params.get('id'))
)
).subscribe((response) => {
this.startUI();
});
}
public stopUI(){
this.stopAutoScroll();
this.stopTileSelector();
}
public startUI(){
if(this.isAutoScrolling){
this.startAutoScroll();
}
else{
this.stopAutoScroll();
}
if(this.isAutoSelecting){
this.startTileSelector();
}
else{
this.stopTileSelector();
}
}
private startAutoScroll() {
this.autoScroller = TimerObservable.create(0, 75).subscribe(x => {
this.nudge();
});
console.debug("STARTING document scroll", this.autoScroller);
}
private stopAutoScroll(){
this.autoScroller.unsubscribe();
console.debug("STOPPING document scroll", this.autoScroller);
}
private startTileSelector() {
this.autoSelector = TimerObservable.create(this.focusTileDelay, this.focusTileDuration).subscribe(x => {
this.focusNextTile();
});
console.debug("STARTING tile selection", this.autoSelector);
}
private stopTileSelector(){
this.autoSelector.unsubscribe();
console.info("STOPPING tile selection", this.autoSelector);
}
/**
* Destroys any existing instances of a lightbox display and shows
* a new lightbox element for the specific album item.
* @param index The item-position in the album to display
*/
private focusTile(index: number): void {
console.info("Focusing on tile #" + index);
// Stop scrolling / selecting while we focus this tile
this.stopUI();
// Subscribe to events that this lightbox may emit
this.lightboxSubscription = this.lightboxEvent.lightboxEvent$.subscribe(event => this.OnLightboxReceivedEvent(event));
// Actually display lightbox for selected focus
this.lightbox.open(this.tileAlbum, index, {centerVertically: true});
// If the tile isnt frozen, automatically de-select it after the standard viewing-duration
if(!this.isFrozen){
setTimeout(() =>{
this.unfocusCurrentTile();
},
this.focusTileDuration);
}
}
/**
* Removes any existing lightbox instances, re-starts UI
*/
private unfocusCurrentTile(){
this.stopUI();
var overlays = document.getElementsByClassName('lightboxOverlay');
var lightboxes = document.getElementsByClassName('lightbox');
for(var i = 0; i < overlays.length; i++){
overlays[i].remove();
}
for(var i = 0; i < lightboxes.length; i++){
lightboxes[i].remove();
}
this.startUI();
}
private nudge() {
if ((this.windowRef.nativeWindow.innerHeight + this.windowRef.nativeWindow.scrollY) >= document.body.offsetHeight) {
this.nudgeValue = -1;
}
else if (this.windowRef.nativeWindow.scrollY == 0) {
this.nudgeValue = 1;
}
this.windowRef.nativeWindow.scrollTo(
0,
this.windowRef.nativeWindow.scrollY + this.nudgeValue
);
}
}