之前的内容我们已经简单介绍了如何生成模态框,以及用状态管理插件管理状态,并把批量的action封装成service。下面我们正式来给我们的模态框添加特性。
简单地来说就是监听键盘上的keydown事件,如果keyCode对应的是ESC键,那就隐藏模态框。
private createOverlay() {
this.overlayRef = this.overlay.create();
this.overlayRef.overlayElement.appendChild(this.elementRef.nativeElement);
// 创建模态框的时候就开始监听
this.overlayRef.keydownEvents().subscribe(e => this.handleKeyDownEvent(e));
}
private handleKeyDownEvent(evt: KeyboardEvent) {
if (evt.keyCode === ESCAPE) {
this.onClick();
}
}
另外,我们也不能一直监听,当visible为false的时候显然再监听就不合适了,所以注入OverlayKeyboardDispatcher。当visiable为true的时候add OverlayRef, 为false的时候remove OverlayRef.
// 省略了部分代码
private handleVisiableChange(visiable) {
if (visiable) {
// ....
this.overlayKeyboardDispatcher.add(this.overlayRef);
// ....
} else {
// ....
this.overlayKeyboardDispatcher.remove(this.overlayRef);
// ....
}
this.cdr.markForCheck();
}
(P.S. 注意,这个elmentRef可以直接注入ElementRef来获取到本组件的引用,原理是Angular中的DI的解析)
这个就需要dom操作了,核心思想就是获取模态框的宽高并以更简单的形式返回出去,当然也要获取当前窗口的宽高。
export function getHideDomSize(dom: HTMLElement) {
return {
w: dom.offsetWidth,
h: dom.offsetHeight
};
}
export function getWindowSize() {
return {
w: window.innerWidth || document.documentElement.clientWidth || document.body.offsetWidth,
h: window.innerHeight || document.documentElement.clientHeight || document.body.offsetHeight
};
}
居中的想法就是设置style.left和style.top两个属性。
private keepCenter(modal: HTMLElement, size: { w: number, h: number}) {
const windowSize = getWindowSize();
const left = (windowSize.w - size.w) / 2;
const top = (windowSize.h - size.h) / 2;
modal.style.left = left + 'px';
modal.style.top = top + 'px';
}
然后我们要监听window的resize事件,当window发生resize的时候,我们就得调用keepCenter方法。
这里不用fromEvent了,改用注入render2来实现。
private listenResizeToCenter() {
const modal = this.modalRef.nativeElement;
const modalSize = getHideDomSize(modal);
this.keepCenter(modal, modalSize);
this.resizeHandler = this.rd.listen('window', 'resize', () => this.keepCenter(modal, modalSize));
}
注意我们这里把rd.listen的返回值用变量接住了,这个主要是为了在visiable为false的时候取消订阅。
private handleVisiableChange(visiable) {
if (visiable) {
// ...
this.listenResizeToCenter();
// ...
} else {
// ...
// 取消监听, 只要调用返回的方法就能取消监听了
this.resizeHandler();
// ...
}
当我们打开模态框的时候,我们应该让鼠标不能点击后面的各种链接。
ngOnInit(): void {
this.overlayContainerEl = this.overlayContainerServe.getContainerElement();
this.createOverlay();
}
private changePointerEvents(type: 'none' | 'auto') {
if (this.overlayContainerEl) {
this.overlayContainerEl.style.pointerEvents = type;
}
}
同样我们得在visiable改变的时候传入不同的type,这里就不演示了。
模态框显示的时候我们也不能让页面滚动。
constructor(
private overlay: Overlay)
{
this.scrollStrategy = this.overlay.scrollStrategies.block();
// ...
}
在visiable里面enable和disable即可。
拖拽只要调用material cdk即可,不过拖拽也要限制边界:
之前总是说visiable改变的时候干什么,那他具体干了什么呢?我们来看看:
private handleVisiableChange(visiable) {
if (visiable) {
this.showModal = 'show';
this.scrollStrategy.enable();
this.overlayKeyboardDispatcher.add(this.overlayRef);
// 监听
this.listenResizeToCenter();
this.changePointerEvents('auto');
} else {
this.showModal = 'hide';
this.scrollStrategy.disable();
this.overlayKeyboardDispatcher.remove(this.overlayRef);
// 取消监听
this.resizeHandler();
this.changePointerEvents('none');
}
this.cdr.markForCheck();
}