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工程师 杨振兴