【Angular实战/网易云】模态框(二)

之前的内容我们已经简单介绍了如何生成模态框,以及用状态管理插件管理状态,并把批量的action封装成service。下面我们正式来给我们的模态框添加特性。

一. ESC键退出

简单地来说就是监听键盘上的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,这里就不演示了。

 

四. 禁用scroll

模态框显示的时候我们也不能让页面滚动。

  constructor(
    private overlay: Overlay)
 {
    this.scrollStrategy = this.overlay.scrollStrategies.block();
    // ...
  }

在visiable里面enable和disable即可。

 

五. 允许拖拽

拖拽只要调用material cdk即可,不过拖拽也要限制边界:

{{title[modalType]}}

 

六. watchVisiableChange到底干了什么事情

之前总是说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();
  }

 

你可能感兴趣的:(Angular,#,Angular实战)