如何配置 material cdk 叠加位置策略,在大屏幕和小屏幕上都能很好地工作?

How do I configure a material cdk overlay position strategy that works great, both on big and small screens?

问题

如何配置 material cdk 叠加位置策略,在大屏幕和小屏幕上都能很好地工作?

目标

我的 objective 是用 Angular overlay CDK 创建一个叠加层,遵循这些规则:

有效方法

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

损坏的部分

然而,正如您从下面的 gif 中可以看出的,这 在垂直 space(在本例中为 255px)不足的小型设备上不起作用。实际上,正如您从 GIF 中可以看出的那样,在其中 2 个案例中,它非常接近。位置没错,就是身高不对

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

代码

我有一个堆栈闪电战,你可以在其中进行实验 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;
}

备注

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

我建议在测试 stackblitz 时使用 stackbllitz 的 "Open in New Window LIVE" 功能。 Link 是 here again

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

对于身高,您可以替换为:

maxHeight: 300,

来自

height: window.innerHeight < 300 ? window.innerHeight + 'px' : '300px',

这只是一个想法。在你的组件中直接使用 window 不是一个好主意,因为你不能模拟它。如果您想知道如何正确执行此操作,请检查 this article

我解决了上述问题,方法是在打开对话框后进行检查。这是通过 resize-observer 完成的。代码如下所示:

/*
        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: