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。bash
一切準備就緒後,這裏就是須要告訴Overlay彈出層要顯示的內容,直接彈出模板,在模板中定義,這裏用到的是overlayRef的attach方法,代碼以下app
this.overlayRef.attach(new TemplatePortal(this.overlayTemplate, this.viewContainerRef));
複製代碼
代碼中用到了this.overlayTemplate,經過ViewChild
取到的顯示彈出內容的模板定義.
注:attach方法用到了CDK裏面的Protals,attach方法接收的參數類型實際上是TemplatePortal,由於這個說究竟是動態建立組件,除此以外它還支持組件類型的ComponentPortal,關於Portals能夠參考我前面的文章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時就是使用的這個策略,它在位置策略上有更多的控制,特性以下:
withDefaultOffsetX
、withDefaultOffsetY
設置相對基礎位置的偏移。withPositions
參數爲ConnectionPositionPair類型的數組,提供多種位置組合,當某一種位置組合的彈出內容超出窗口,就會應用對應其它的位置組合來避免內容不可見。withFlexibleDimensions
控制Overlay彈出層寬度和高度是否被限制在瀏覽器窗口內,參數設置爲ture時,寬度和高度會自適應到瀏覽器邊界,以滾動條形式展示內容。建立策略的代碼以下
/**
* 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 github.com/angular/com…
cd components
yarn install // 若是提示沒有yarn 須要全局裝下yarn,node版本要求10.x
npm run dev-app
本文做者:Worktile工程師 楊振興