Overlay中文翻譯過來意思是覆蓋物,它是Material Design components for Angular中針對彈出動態內容這一場景的封裝,功能強大、使用方便,尤爲在開發本身的組件庫時,可讓你少寫許多代碼,能夠說只要是彈出內容的場景基本均可以使用Overlay.
咱們本身的組件庫中彈出場景基本都已經使用Overlay,如自定義Select、Cascader、Tree Select、Tooltip、Dialog等,總結最重要的的兩點好處:html
下面經過一個示例代碼來展現Overlay的使用,這種彈出場景相似於Tooltip,彈出的overlay內容是基於一個參照的位置源origin元素.node
項目中若是沒有安裝CDK,要先安裝git
npm install @angular/cdk
import {OverlayModule} from '@angular/cdk/overlay'; @NgModule({ imports: [ OverlayModule, // ... ] }) export class AppModule { }
<div class="demo-trigger"> <!--觸發位置源--> <button mat-raised-button cdkOverlayOrigin type="button" [disabled]="overlayRef" (click)="openWithConfig()">Open</button> </div> <!--彈出動態內容模板--> <ng-template #overlay> <div class="demo-overlay"> <div style="overflow: auto;"> <ul><li *ngFor="let item of itemArray; index as i">{{itemText}} {{i}}</li></ul> </div> </div> </ng-template>
除了彈出模板,上面模板中還有一個Open按鈕,後面要用到它做爲位置源origingithub
在組件的constructor構造函數中注入Overlay服務,下面代碼包括組件的定義npm
@Component({ selector: 'overlay-demo', templateUrl: 'connected-overlay-demo.html' }) export class ConnectedOverlayDemo { @ViewChild(CdkOverlayOrigin, {static: false}) _overlayOrigin: CdkOverlayOrigin; @ViewChild('overlay', {static: false}) overlayTemplate: TemplateRef<any>; /** * 注入Overlay服務 */ constructor( public overlay: Overlay) { } openWithConfig() { } }
處理注入服務,上面代碼還經過 ViewChild 取到模板中的兩個對象,後面用到的時候再解釋.編程
首先建立一個位置策略,這裏使用的是 FlexibleConnectedPositionStrategy 策略,先看代碼數組
const positionStrategy = this.overlay.position() .flexibleConnectedTo(this._overlayOrigin.elementRef) .withPositions([ { originX: 'start', originY: 'bottom', overlayX: 'start', overlayY: 'top', } ]);
建立 FlexibleConnectedPositionStrategy 策略的方法 flexibleConnectedTo 必需要提供一個位置源參數,這裏使用的是
this._overlayOrigin.elementRef ,彈出內容的位置是基於這個位置源的,this._overlayOrigin其實就是經過ViewChild取的模板中的Open按鈕.瀏覽器
this.overlayRef = this.overlay.create({ positionStrategy, // 位置策略 scrollStrategy: this.overlay.scrollStrategies.reposition(), // 滾動策略 direction: this.dir.value, // 可用性方面的設置,不用太關注 minWidth: 200, // overlay層的最小寬度 minHeight: 50 // overlay層的最小高度 hasBackdrop: false // 是否顯示遮罩層 });
方法會生成一個OverlayRef類型的對象overlayRef,用overlayRef來管理Overlay。app
一切準備就緒後,這裏就是須要告訴Overlay彈出層要顯示的內容,直接彈出模板,在模板中定義,這裏用到的是overlayRef的attach方法,代碼以下函數
this.overlayRef.attach(new TemplatePortal(this.overlayTemplate, this.viewContainerRef));
代碼中用到了this.overlayTemplate,經過 ViewChild 取到的顯示彈出內容的模板定義.
注:attach方法用到了CDK裏面的Protals,attach方法接收的參數類型實際上是TemplatePortal,由於這個說究竟是動態建立組件,除此以外它還支持組件類型的ComponentPortal,關於Portals能夠參考我前面的文章https://zhuanlan.zhihu.com/p/59719621
經過以上簡單的幾個步驟就實現動態內容的彈出,效果圖以下所示
與上面的徹底不一樣,如今介紹下經過Overlay直接彈出內容在窗口上,不連結任何位置源,很是簡單隻須要更改下位置策略,看下使用的新位置策略的代碼
const positionStrategy = this.overlay.position() .global() .height('300px') .centerHorizontally() .top('70px');
調用 global() 返回的是全局的位置策略 GlobalPositionStrategy ,基於瀏覽器窗口絕對定位的位置策略。
以上代碼實現:水平居中,距離頂部70px,效果圖以下
Overlay經過OverlayPositionBuilder服務提供了三個方法分別對應三種位置策略,OverlayPositionBuilder經過構造函數注入到了Overlay服務中,前面代碼 this.overlay.position() 返回的就是OverlayPositionBuilder類型的對象
注:該策略已被棄用,使用FlexibleConnectedPositionStrategy策略代替,可是這裏的討論能夠繼續,用以說明鏈接點的位置關係
connectedTo 方法返回ConnectedPositionStrategy策略實例,該策略實現基於一個操做源上的位置點到Overlay彈出層的位置點鏈接關係的位置策略,建立策略的代碼以下
connectedTo( elementRef: ElementRef, originPos: OriginConnectionPosition, overlayPos: OverlayConnectionPosition): ConnectedPositionStrategy { return new ConnectedPositionStrategy( originPos, overlayPos, elementRef, this._viewportRuler, this._document, this._platform, this._overlayContainer); }
參數分別是:
用圖表達它所維繫的位置關係
上圖所示的位置點組合只是其中一種狀況(左下點 - 左上點),位置配置代碼以下
{ "originX": "start", "originY": "bottom", "overlayX": "start", "overlayY": "top" }
在x方向上可枚舉值定義以下
export type HorizontalConnectionPos = 'start' | 'center' | 'end';
在y方向上可枚舉值定義
export type VerticalConnectionPos = 'top' | 'center' | 'bottom';
基於以上枚舉值能夠實現各類位置組合。
注:如今的源代碼ConnectedPositionStrategy策略最終也是經過關聯FlexibleConnectedPositionStrategy策略實現的,因此推薦直接使用該策略
經過 flexibleConnectedTo 方法返回FlexibleConnectedPositionStrategy策略實例,這是Overlay最複雜的一個位置策略,因此能稱上Flexible,經過指令方式使用Overlay時就是使用的這個策略,它在位置策略上有更多的控制,特性以下:
建立策略的代碼以下
/** * Creates a flexible position strategy. * @param origin Origin relative to which to position the overlay. */ flexibleConnectedTo(origin: FlexibleConnectedPositionStrategyOrigin): FlexibleConnectedPositionStrategy { return new FlexibleConnectedPositionStrategy(origin, this._viewportRuler, this._document, this._platform, this._overlayContainer); }
只有一個origin參數,提供了要連結的位置源元素引用。
全局位置策略, global 方法返回GlobalPositionStrategy策略實例,無任何參數,建立策略的代碼以下
/** * Creates a global position strategy. */ global(): GlobalPositionStrategy { return new GlobalPositionStrategy(); }
GlobalPositionStrategy提供了全局定位的各類方法,而且能夠經過鏈式的方式調用,以下代碼
const strategy = this.overlay .position() .global() .width('500px') .height('100px') .centerHorizontally() .centerVertically();
還有 top 、 left 、 bottom 、 right 方法提供各個方位的絕對定位,參數是在這個方位上的偏移值,如居上10px參數就是 '10px' ,這是這個偏移值會打破水平或者垂直方向上的居中。
位置策略接口定義以下
import {OverlayReference} from '../overlay-reference'; /** Strategy for setting the position on an overlay. */ export interface PositionStrategy { /** 附加位置策略到overlay */ attach(overlayRef: OverlayReference): void; /** 更新overlay element 元素的位置. */ apply(): void; /** 當overlay調用detach時調用 */ detach?(): void; /** Cleans up any DOM modifications made by the position strategy, if necessary. */ dispose(): void; }
接口定義了位置策略必須包含的方法簽名,這是面向對象編程中的經常使用的抽象方式。
OverlayRef在實現時只依賴PositionStrategy接口而不具體依賴某一個策略的實現,在建立OverlayRef須要提供一個具體的位置策略的實例(通常是在建立Overlay時配置),若是有須要還能夠實現本身的位置策略,實現本身的位置策略只須要實現這個接口而且定義接口簽名的具體實現。
符合面向對象的三大特性 封裝 、 繼承 、 多態 ,符合五大原則中的 單一職責原則 、 開放封閉原則 ,這種抽象思想很是值得學習
Overlay提供了全局服務 ScrollStrategyOptions ,用它提供處理overlay滾動時的處理策略。
在滾動不作任何事情,調用 scrollStrategies 的 noop 方法
noop = () => new NoopScrollStrategy();
一旦用戶有滾動行爲,當即關閉overlay彈層,調用 scrollStrategies 的 close 方法
close = (config?: CloseScrollStrategyConfig) => new CloseScrollStrategy(this._scrollDispatcher, this._ngZone, this._viewportRuler, config)
能夠配置config中的參數 threshold ,設置一個滾動像素的臨界點,只有當滾動距離大於此參數時纔會關閉overlay.
該策略會阻止頁面級的滾動,調用 scrollStrategies 的 block 方法
block = () => new BlockScrollStrategy(this._viewportRuler, this._document);
經過給頁面的html標籤增長樣式 cdk-global-scrollblock 來阻止頁面級別的滾動,樣式定義以下
position: fixed; width: 100%; overflow-y: scroll;
一旦用戶有滾動行爲,該策略會根據滾動的位置更新彈出層的位置,效果就是彈出層會跟隨滾動而滾動,相對於位置源的位置不變,調用 scrollStrategies 的 reposition 方法
reposition = (config?: RepositionScrollStrategyConfig) => new RepositionScrollStrategy( this._scrollDispatcher, this._viewportRuler, this._ngZone, config)
config能夠配置兩個參數, scrollThrottle 參數控制滾動事件觸發從新更新位置的抖動頻率, autoClose 參數配置當滾動事件發生時是否關閉overlay彈層(實現 關閉滾動策略 的功能)
不管是關閉Overlay仍是更新Overlay位置,都須要檢測滾動事件的觸發,這裏要用到CDK提供的處理滾動的服務(cdk/scrolling目錄下),主要用到 ScrollDispatcher ,一個處理全局滾動事件的觸發器.
關閉滾動策略 和 重定位滾動策略 都是訂閱ScrollDispatcher的 scrolled 方法返回的流(後面統一叫scrolled流),有幾點要明確
由於國內的軟件大部分是把整個窗口固定,而後再經過局部元素設置樣式 overflow:scroll 實現內容滾動,這裏僅提供思路
這塊理解起來並無那麼容易,很抽象,又是由overlay 、scroll、position策略、scroll策略組合起來的,如今能夠先作了解,須要瞭解細節時再翻看源代碼。
文字首先介紹了使用Overlay的好處,以及在咱們的組件庫中都有那些組件使用了Overlay,而後經過簡單的示例代碼帶你們瞭解Overlay的使用,後面又介紹了Overlay的位置策略和滾動策略,但願看到最後的各位有些幫助。
Overlay須要說的內容很是的多,並且總體封裝的思想也很值得學習,這裏就簡單介紹這麼多,有任何建議或者疑問歡迎留言討論。
另外文中的示例基本是在Material Design Components for angular 中針對Overlay的Demo,有須要能夠自行clone代碼學習
示例運行命令
git clone https://github.com/angular/components
cd components
yarn install // 若是提示沒有yarn 須要全局裝下yarn,node版本要求10.x
npm run dev-app
本文做者:Worktile工程師 楊振興
文章來源:Worktile技術博客
歡迎訪問交流更多關於技術及協做的問題。
文章轉載請註明出處。