angular - 如何配置一个在大屏幕和小屏幕上都很好用的 Material cdk 覆盖位置策略?

标签 angular angular-material angular-material2 angular-cdk

问题

如何配置一个在大屏幕和小屏幕上都很好用的 Material cdk 覆盖位置策略?

目标

我的目标是创建一个覆盖 Angular overlay CDK , 遵循以下规则:

  • 切勿将叠加层放置在视口(viewport)之外
  • 最大高度 300 像素
  • 以顶部、中心底部的优先级定位覆盖 y。
  • 在高度低于 300 的屏幕上,使用可用高度。
  • 换句话说,没有最小高度。

  • 什么有效 😎

    我已经完成了上述一些要求,正如您在此处看到的,定位在具有足够垂直空间(在本例中为 512 像素)的设备上效果很好:

    enter image description here

    破损的部分😅

    但是,从下面的 gif 图中可以看出,这个 不工作在垂直空间不足的小型设备上(在本例中为 255 像素)。实际上,正如您从 GIF 中看到的那样,在其中 2 种情况下,它非常接近。位置是正确的,只是高度偏离了。

    enter image description here

    这里的目标是使用可用空间,如红色所示:

    enter image description here

    代码🤓

    我有一个堆栈 Blitz ,你可以在其中进行实验here .

    组件

    openPopup(element: any) {
        const overlayRef = this.overlay.create({
          hasBackdrop: true,
          positionStrategy: this.overlay.position()
          .flexibleConnectedTo(element)
          .withLockedPosition()
          .withPositions([
            {
              panelClass: 'custom-panel1',
              originX: 'center',
              originY: 'center',
              overlayX: 'start',
              overlayY: 'top',
            },
            {
              panelClass: 'custom-panel2',
              originX: 'center',
              originY: 'center',
              overlayX: 'start',
              overlayY: 'center',
            },
            {
              panelClass: 'custom-panel3',
              originX: 'center',
              originY: 'center',
              overlayX: 'start',
              overlayY: 'bottom',
            },
          ])
          ,
          width: '200px',
          maxHeight: 300,
        });
    
        const popupComponentPortal = new ComponentPortal(PopupComponent);
    
        overlayRef.attach(popupComponentPortal);
    
        overlayRef.backdropClick().subscribe(() => {
          overlayRef.dispose();
        });
      }
    

    全局风格

    .cdk-overlay-pane {
        max-height: 300px;
    }
    

    笔记

    我一直在考虑在视口(viewport)高度变小的时候使用全局定位策略。但是,我宁愿避免这种情况,因为我希望有一个解决方案可以处理覆盖层的任何高度(尊重最大偏离航向)。

    在测试 stackblitz 时,我建议使用 stackbllitz 的“在新窗口中实时打开”功能。链接是here again .

    如果您能帮助解决此问题或为我指明正确的方向,我将不胜感激🤷🏾‍♂️

    最佳答案

    我通过在打开对话框后执行检查解决了上述问题。这是通过调整大小观察器完成的。代码如下所示:

    /*
            Read more about the resize observer here: https://developers.google.com/web/updates/2016/10/resizeobserver
            Basicaly, what we do is that we subscribe to size events on the overlay.
            Currently we only get one event, (then we disconnet the resize observer).
            But then we simply calculate if we need to improve the layout.
            */
            const ro = new ResizeObserver(entries => {
                for (const entry of entries) {
                    // We get the hight of the element from the the contnetRect, provided by the resize observer
                    const height = entry.contentRect.height;
    
                    const { left: x, top: y } = entry.target.getBoundingClientRect();
    
                    const offsetPlusHeight = y + height;
    
                    const pixelsOverflow = offsetPlusHeight - this.viewPortHeight;
                    // If y is negative, we are off-screen to the top.
                    // If pixelsOverflow is positive, we are off-screen on the bottom
                    // In either case, we adopt a new strategy.
                    if (y < 0 || pixelsOverflow > 1) {
                        ro.disconnect();
                        overlayRef.updateSize({
                            height: height,
                            maxHeight: config.maxHeight,
                            width: config.width,
                        });
                        // Trust the initial positioning strategy, in terms of the x coordinate
    
                        // Now determine if we need to throw the overlap, all the way to the top.
                        const delta = this.viewPortHeight - height;
                        const yOffset = delta > 0 ? delta : 0;
    
                        // Finnaly, we can apply canculate and apply the new position
                        const newPositionStrategy = this.getGlobalPosition(yOffset, x);
                        overlayRef.updatePositionStrategy(newPositionStrategy);
                    }
                }
            });
    
            // Element for which to observe height and width
            ro.observe(overlayRef.overlayElement);
    

    我认为我需要这个额外检查的原因是因为我真正在寻找的是一个用于高度可变的项目的 n cdk 覆盖策略。

    如果您对有效的解决方案感兴趣,我有一个有效的 stackblitz here :

    enter image description here

    关于angular - 如何配置一个在大屏幕和小屏幕上都很好用的 Material cdk 覆盖位置策略?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58674359/

    相关文章:

    javascript - 如何在 Angular 7 中使用窗口对象

    javascript - 我们如何以一种形式使用 Angular 2 来处理事件

    angular - 2000 毫秒后微调器未显示

    angular - 如何在 Angular Material - Angular 2 中使用 md-sidenav 创建右侧导航栏

    java - 如何使用嵌入式jetty提供index.html?

    javascript - 使用 Angular 4 和 Http observables 进行无限轮询

    javascript - Angular 6 mat table DOM在拼接数据源后不更新

    angular - 如何强制 Angular 7+ Material Autocomplete 输入仅由数据源提供的字母?

    html - Angular Material 2 : mat-button css to look like mat-button-toggle

    sass - Material 2 中的自定义主题返回 : Argument `$map` of `map-get($map, $key)` must be a map