Angular cdk 学习之 Scrolling

       Angular cdk Scrolling包给我们提供了一些指令和Service来应对滑动事件。推荐大家尽量去看官网上的介绍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

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,590评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 86,808评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,151评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,779评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,773评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,656评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,022评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,678评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,038评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,659评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,756评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,411评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,005评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,973评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,053评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,495评论 2 343

推荐阅读更多精彩内容