? ? ? ?Angular cdk Scrolling包給我們提供了一些指令和Service來應對滑動事件。推薦大家盡量去看官網(wǎng)上的介紹https://material.angular.io/cdk/scrolling/overview
? ? ? ?想要在我們項目中使用cdk Scrolling功能,需要在我們模塊里面 import {ScrollDispatchModule} from '@angular/cdk/scrolling';
一 cdkScrollable and ScrollDispatcher
? ? ? ?cdkScrollable指令單獨使用的時候沒有特別的效果,cdkScrollable指令一般用來配合ScrollDispatcher Service使用。任何添加了cdkScrollable指令的視圖元素都會注冊到ScrollDispatcher里面去。然后可以在ScrollDispatcher里面去監(jiān)聽元素的滑動事件。
1.1 ScrollDispatcher常用方法介紹
export declare class ScrollDispatcher implements OnDestroy {
/**
* 訂閱全局`scroll`和`resize`
*/
_globalSubscription: Subscription | null;
/**
* 注冊到ScrollDispatcher里面的元素(添加了CdkScrollable指令的元素)
*/
scrollContainers: Map<CdkScrollable, Subscription>;
/**
* 注冊CdkScrollable(我們不用管,我們在某個視圖元素上添加CdkScrollable指令會自動注冊的)
*/
register(scrollable: CdkScrollable): void;
/**
* 取消注冊
*/
deregister(scrollable: CdkScrollable): void;
/**
* 可以去訂閱任務一個注冊CdkScrollable的scroll事件
*
* **Note:** 為了避免每次滾動都發(fā)送狀態(tài)變化檢測,內(nèi)部采用的是NgZone.runOutsideAngular。所以如果你想每次都檢測變化就采用NgZone.run的放
* 有疑問可以去搜下NgZone的使用
*/
scrolled(auditTimeInMs?: number): Observable<CdkScrollable | void>;
/**
* 監(jiān)聽elementRef視圖元素的祖先元素有滾動事件,當然了對應的祖先元素需要添加CdkScrollable指令
* auditTimeInMs參數(shù)是為了防止事件發(fā)送過快,可以設置多長時間收一次事件
*/
ancestorScrolled(elementRef: ElementRef, auditTimeInMs?: number): Observable<CdkScrollable | void>;
/**
* elementRef添加了CdkScrollable指令的祖先元素
*/
getAncestorScrollContainers(elementRef: ElementRef): CdkScrollable[];
}
1.2 cdkScrollable and ScrollDispatcher使用
import {AfterViewInit, Component, ElementRef, OnInit, ViewChild} from '@angular/core';
import {CdkScrollable, ScrollDispatcher} from '@angular/cdk/overlay';
@Component({
selector: 'app-cdk-scrolling',
template: `
<!-- 通過cdkScrollable指令配合ScrollDispatcher來監(jiān)聽節(jié)點的scrolling -->
<div cdkScrollable class="scrolling-parent">
<div #scrollingParent class="scrolling-item">item 1</div>
<div class="scrolling-item">item 2</div>
<div class="scrolling-item">item 3</div>
</div>
<!-- 這個div沒有添加cdkScrollable指令,所以這個div的scrolling事件ScrollDispatcher獲取不到 -->
<div class="scrolling-parent">
<div class="scrolling-item">item 1</div>
<div class="scrolling-item">item 2</div>
<div class="scrolling-item">item 3</div>
</div>
`,
styles: [`
.scrolling-parent {
height: 100px;
width: 200px;
border: 1px solid black;
padding: 8px;
overflow-y: auto;
}
.scrolling-item {
height: 50px;
}
`]
})
export class CdkScrollingComponent implements OnInit, AfterViewInit {
@ViewChild('scrollingParent')
childDiv: ElementRef;
constructor(private scrollDispatcher: ScrollDispatcher) {
}
ngOnInit() {
/**
* 監(jiān)聽所有ScrollDispatcher里面注冊的CdkScrollable的scroll事件
*/
this.scrollDispatcher.scrolled().subscribe((scrollable: CdkScrollable) => {
if (scrollable) {
console.log('發(fā)生scroll了,來源于:');
console.log(scrollable.getElementRef().nativeElement);
}
});
}
ngAfterViewInit(): void {
/**
* 第二個參數(shù)auditTimeInMs表示事件延時多少秒發(fā)生
* 當祖先設置了cdkScrollable指令,在孩子里面也能抓到scrolling事件
*/
this.scrollDispatcher.ancestorScrolled(this.childDiv).subscribe((scrollable: CdkScrollable) => {
if (scrollable) {
console.log('祖先發(fā)生scroll了,來源于:');
console.log(scrollable.getElementRef().nativeElement);
}
});
// 獲取ScrollDispatcher里面所有注冊了scrolling的組件信息
console.log(this.scrollDispatcher.scrollContainers);
}
}
二 ViewportRuler
? ? ? ?ViewportRuler也是cdk Scrolling里面提供的一個Service。用來測量瀏覽器的視圖窗口信息。
2.1 ViewportRuler常用方法
export declare class ViewportRuler implements OnDestroy {
/**
* 獲取瀏覽器窗口的寬度和高度
*/
getViewportSize(): Readonly<{
width: number;
height: number;
}>;
/**
* 獲取瀏覽器窗口的rect信息(left、top、right、bottom)
*/
getViewportRect(): ClientRect;
/**
* 獲取瀏覽器窗口的滑動位置(top、left)
*/
getViewportScrollPosition(): ViewportScrollPosition;
/**
* 監(jiān)聽瀏覽器窗口大小變化
*/
change(throttleTime?: number): Observable<Event>;
}
2.2 ViewportRuler使用實例
import {Component, OnInit} from '@angular/core';
import {ViewportRuler} from '@angular/cdk/overlay';
@Component({
selector: 'app-cdk-scrolling',
template: `
`
})
export class CdkScrollingComponent implements OnInit {
constructor(private viewPortRuler: ViewportRuler) {
}
ngOnInit() {
/**
* ViewportRuler 用來監(jiān)聽窗口的大小
*/
// { width, height }
console.log(this.viewPortRuler.getViewportSize());
// { bottom, height, left, right, top, width }
console.log(this.viewPortRuler.getViewportRect());
// { top, left }
console.log(this.viewPortRuler.getViewportScrollPosition());
// native resize event object
this.viewPortRuler.change().subscribe(resizeEvent => console.log(resizeEvent));
}
}
三 CdkVirtualScrollViewport
? ? ? ?angular cdk 里面給提供了一個scrolling的組件CdkVirtualScrollViewport - cdk-virtual-scroll-viewport。CdkVirtualScrollViewport組件在list展示的時候非常有用,比如有我們list里面的item有1000項的時候。CdkVirtualScrollViewport 并不會直接給我們加載出1000項。只會加載部分項。一邊滑動一邊加載。如果有做過android的話,應該會知道ListView里面的回收機制。CdkVirtualScrollViewport組件做的也是類似的事情。
3.1 CdkVirtualScrollViewport組件主要方法介紹
export class CdkVirtualScrollViewport extends CdkScrollable implements OnInit, OnDestroy {
/**
* 這個是CdkFixedSizeVirtualScroll里面的屬性,也是可以設置在CdkVirtualScrollViewport組件上的
* CdkVirtualScrollViewport組件里面每個item的大小(通過orientation來知道是高度還寬度)
*/
@Input()
get itemSize(): number { return this._itemSize; }
/**
* 這個是CdkFixedSizeVirtualScroll里面的屬性,也是可以設置在CdkVirtualScrollViewport組件上的
* 多加載滾動范圍之后多少像素的距離(最小值),比如CdkVirtualScrollViewport的高度是200px,minBufferPx
* 是100px; item有好多的情況下不用全部繪制出來。繪制200像素的內(nèi)容就好了。
*/
@Input()
get minBufferPx(): number { return this._minBufferPx; }
/**
* 這個是CdkFixedSizeVirtualScroll里面的屬性,也是可以設置在CdkVirtualScrollViewport組件上的
* 多加載滾動范圍之后多少像素的距離(最大值)
*/
@Input()
get maxBufferPx(): number { return this._maxBufferPx; }
/**
* viewport 方向
*/
@Input() orientation: 'horizontal' | 'vertical' = 'vertical';
/**
* 活動過程中,當前可見第一項的index
*/
@Output() scrolledIndexChange: Observable<number> =
Observable.create((observer: Observer<number>) =>
this._scrollStrategy.scrolledIndexChange.subscribe(index =>
Promise.resolve().then(() => this.ngZone.run(() => observer.next(index)))));
/**
* 設置滑動位置
*/
scrollToOffset(offset: number, behavior: ScrollBehavior = 'auto'){}
/**
* 設置滑動的index
*/
scrollToIndex(index: number, behavior: ScrollBehavior = 'auto') {}
/**
* 測量可滑動的大小
*/
measureScrollOffset(from?: 'top' | 'left' | 'right' | 'bottom' | 'start' | 'end'): number {}
/**
* 測量所有item在一起的高度
*/
measureRenderedContentSize(): number{}}
上面主要方法介紹我把CdkVirtualScrollViewport組件和CdkFixedSizeVirtualScroll指令揉到一起去介紹了,因為它倆本來就是一起配合使用的。
3.2 CdkVirtualForOf指令
? ? ? ?Selector: [cdkVirtualFor][cdkVirtualForOf]
? ? ? ?CdkVirtualScrollViewport適用于list的情況,list循環(huán)的時候得配合CdkVirtualForOf指令來使用,CdkVirtualForOf的用法和ngFor的用法是一樣的。CdkVirtualForOf指令是專門為CdkVirtualScrollViewport組件提供的。 就不用ngFor指令了。切記。如果想咱們平常一樣用ngFor指令去循環(huán),很多CdkVirtualScrollViewport組件里面特有的功能就用不了了。
3.3 CdkVirtualScrollViewport組件使用
? ? ? ?想使用angular cdk Scrolling里面的功能先導入ScrollDispatchModule模塊。
import {ScrollDispatchModule} from '@angular/cdk/scrolling';
3.3.1 CdkVirtualForOf指令的使用
? ? ? ?主要是知道CdkVirtualForOf里面有哪些參數(shù)是可用的
import {Component, ViewChild} from '@angular/core';
import {CdkVirtualScrollViewport} from "@angular/cdk/scrolling";
@Component({
selector: 'app-virtual-scroll',
template: `
<!-- itemSize是每個item的高度 -->
<cdk-virtual-scroll-viewport #scrollComponent [itemSize]="18 * 7" class="example-viewport">
<!-- cdkVirtualFor的可以用參數(shù)如下 -->
<div *cdkVirtualFor="let item of items;
let index = index;
let count = count;
let first = first;
let last = last;
let even = even;
let odd = odd;" [class.example-alternate]="odd">
<div class="example-item-detail">Item: {{item}}</div>
<div class="example-item-detail">Index: {{index}}</div>
<div class="example-item-detail">Count: {{count}}</div>
<div class="example-item-detail">First: {{first ? 'Yes' : 'No'}}</div>
<div class="example-item-detail">Last: {{last ? 'Yes' : 'No'}}</div>
<div class="example-item-detail">Even: {{even ? 'Yes' : 'No'}}</div>
<div class="example-item-detail">Odd: {{odd ? 'Yes' : 'No'}}</div>
</div>
</cdk-virtual-scroll-viewport>
<button style="margin-top: 8px; margin-bottom: 8px; background-color: #007bff; color: red"
(click)="onButtonClick()">
點擊跳轉到第10個item
</button>
`,
styles: [`
.example-viewport {
height: 200px;
width: 200px;
border: 1px solid black;
}
.example-item-detail {
height: 18px;
}
`]
})
export class VirtualScrollComponent {
@ViewChild('scrollComponent')
private _scrollViewport: CdkVirtualScrollViewport;
/**
* CdkVirtualScrollViewport組件的數(shù)據(jù)源
*/
items = Array.from({length: 100000}).map((_, i) => `Item #${i}`);
/**
* 點擊按鈕的時候跳轉到第10項
*/
onButtonClick() {
this._scrollViewport.scrollToIndex(10);
}
}
3.3.2 水平方向滾動的CdkVirtualScrollViewport組件
? ? ? ?orientation屬性的使用
import {ChangeDetectionStrategy, Component, ViewEncapsulation} from '@angular/core';
@Component({
selector: 'app-virtual-scroll-horizontal',
template: `
<div class="cdk-virtual-scroll-data-source-example">
<cdk-virtual-scroll-viewport orientation="horizontal" itemSize="50" class="example-viewport">
<div *cdkVirtualFor="let item of items" class="example-item">{{item}}</div>
</cdk-virtual-scroll-viewport>
</div>
`,
styles: [`
.cdk-virtual-scroll-data-source-example .example-viewport {
height: 200px;
width: 200px;
border: 1px solid black;
}
.cdk-virtual-scroll-data-source-example .example-viewport .cdk-virtual-scroll-content-wrapper {
display: flex;
flex-direction: row;
}
.cdk-virtual-scroll-data-source-example .example-item {
width: 50px;
height: 100%;
writing-mode: vertical-lr;
}
`],
encapsulation: ViewEncapsulation.None,
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class VirtualScrollHorizontalComponent {
/**
* CdkVirtualScrollViewport組件里面的數(shù)據(jù)源
*/
items = Array.from({length: 100000}).map((_, i) => `Item #${i}`);
}
? ? ? ?關于angular cdk Scrolling里面咱們就先講這么些,主要講了cdkScrollable指令、ScrollDispatcher Service、ViewportRuler Service、CdkVirtualScrollViewport組件等一些很淺顯的用法。如果大家有什么疑問歡迎留言。文章中涉及到的例子鏈接地址 https://github.com/tuacy/angular-cdk-study