Angular cdk 学习之 Scrolling 梦里梦外; 2022-04-04 06:46 315阅读 0赞 Angular cdk Scrolling包给我们提供了一些指令和Service来应对滑动事件。推荐大家尽量去看官网上的介绍[https://material.angular.io/cdk/scrolling/overview][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里面去监听元素的滑动事件。 ## 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:** 为了避免每次滚动都发送状态变化检测,内部采用的是NgZone.runOutsideAngular。所以如果你想每次都检测变化就采用NgZone.run的放 * 有疑问可以去搜下NgZone的使用 */ scrolled(auditTimeInMs?: number): Observable<CdkScrollable | void>; /** * 监听elementRef视图元素的祖先元素有滚动事件,当然了对应的祖先元素需要添加CdkScrollable指令 * auditTimeInMs参数是为了防止事件发送过快,可以设置多长时间收一次事件 */ 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来监听节点的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() { /** * 监听所有ScrollDispatcher里面注册的CdkScrollable的scroll事件 */ this.scrollDispatcher.scrolled().subscribe((scrollable: CdkScrollable) => { if (scrollable) { console.log('发生scroll了,來源于:'); console.log(scrollable.getElementRef().nativeElement); } }); } ngAfterViewInit(): void { /** * 第二个参数auditTimeInMs表示事件延时多少秒发生 * 当祖先设置了cdkScrollable指令,在孩子里面也能抓到scrolling事件 */ this.scrollDispatcher.ancestorScrolled(this.childDiv).subscribe((scrollable: CdkScrollable) => { if (scrollable) { console.log('祖先发生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; /** * 监听浏览器窗口大小变化 */ 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 用来监听窗口的大小 */ // { 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像素的内容就好了。 */ @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循环的时候得配合CdkVirtualForOf指令来使用,CdkVirtualForOf的用法和ngFor的用法是一样的。CdkVirtualForOf指令是专门为CdkVirtualScrollViewport组件提供的。 就不用ngFor指令了。切记。如果想咱们平常一样用ngFor指令去循环,很多CdkVirtualScrollViewport组件里面特有的功能就用不了了。 ## 3.3 CdkVirtualScrollViewport组件使用 ## 想使用angular cdk Scrolling里面的功能先导入ScrollDispatchModule模块。 import {ScrollDispatchModule} from '@angular/cdk/scrolling'; ### 3.3.1 CdkVirtualForOf指令的使用 ### 主要是知道CdkVirtualForOf里面有哪些参数是可用的 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的可以用参数如下 --> <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组件的数据源 */ 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组件里面的数据源 */ 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][https_github.com_tuacy_angular-cdk-study] [https_material.angular.io_cdk_scrolling_overview]: https://material.angular.io/cdk/scrolling/overview [https_github.com_tuacy_angular-cdk-study]: https://github.com/tuacy/angular-cdk-study
还没有评论,来说两句吧...