Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/javascript/464.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Javascript 是否有可靠且性能良好的定位策略来对齐下拉组件?_Javascript_Css_Reactjs_Rxjs_Preact - Fatal编程技术网

Javascript 是否有可靠且性能良好的定位策略来对齐下拉组件?

Javascript 是否有可靠且性能良好的定位策略来对齐下拉组件?,javascript,css,reactjs,rxjs,preact,Javascript,Css,Reactjs,Rxjs,Preact,我正在React/Preact中设计一个下拉组件。下拉列表的交互如下所示: 由于组件可以位于页面上的任何位置,并且任何祖先DOM元素都可以创建新的堆栈上下文或使用溢出:隐藏,因此我不能使用相对父级和绝对子级定位组合 我使用门户并在打开时直接将覆盖曲面作为body标记的子对象进行渲染这里的问题-如何将覆盖菜单放置在锚元素上,使其中心相互碰撞? 必须考虑的几件事: 这是一个通用组件。覆盖菜单可以有任何内容,因此可以有任何大小。预先不知道大小。如果需要,可以将其称为InlineModal或Inlin

我正在React/Preact中设计一个下拉组件。下拉列表的交互如下所示:

由于组件可以位于页面上的任何位置,并且任何祖先DOM元素都可以创建新的堆栈上下文或使用
溢出:隐藏
,因此我不能使用
相对
父级和
绝对
子级定位组合

我使用
门户
并在打开时直接将覆盖曲面作为
body
标记的子对象进行渲染这里的问题-如何将覆盖菜单放置在锚元素上,使其中心相互碰撞?

必须考虑的几件事:

  • 这是一个通用组件。覆盖菜单可以有任何内容,因此可以有任何大小。预先不知道大小。如果需要,可以将其称为
    InlineModal
    InlineBox
  • 此外,菜单围绕的锚元素由于多种原因可能会改变其位置。例如,它位于包含AJAX数据的内容下方,加载时会导致组件向下移动。或者,甚至是图像加载
  • 当菜单打开时,由于菜单中的某些复选框在菜单中添加或删除其他选项,其自身大小可能会发生变化
  • 在边上,应调整图元,使其不会被浏览器窗口剪裁。即使未对齐中心,也应尝试将其安装在窗口内
  • 我可以想到以下几种方法:

  • 使用Rx.js
    interval
    animationFrameScheduler
    (内部RAF-
    RequestAnimationFrame
    )并连续计算固定位置点,即
    顶部
    左侧
  • 使用简单的
    onResize
    onScroll
    处理程序重新定位元素。但这并没有考虑到上述因素,因为我无法知道原始锚是否由于页面上的其他操作而发生了移动
  • CSS定位实际上并不重要,因为元素被附加到
    主体中
    <代码>固定和
    绝对
    都可以正常工作

    我正在用Rx.js和Popmotion尝试的东西:

    import { action } from 'popmotion';
    import { Observable, interval, animationFrameScheduler } from 'rxjs';
    import { map, distinctUntilChanged } from 'rxjs/operators';
    
    export type Coordinates = readonly [number, number];
    
    export function stickToCenterAction(reference: HTMLElement, popper: HTMLElement) {
    
      return action(({ update }) => {
        const sub = stickToCenter(reference, popper)
          .subscribe(([x, y]) => update({ x, y }));
    
        return {
          stop: () => {
            sub.unsubscribe();
          }
        };
      });
    
    }
    
    function stickToCenter(reference: HTMLElement, popper: HTMLElement): Observable<Coordinates> {
      return interval(0, animationFrameScheduler).pipe(
        map(() => calculateCenter(reference, popper)),
        distinctUntilChanged(([oX, oY], [nX, nY]) => oX === nX && oY === nY ));
    }
    
    
    function calculateCenter(reference: HTMLElement, popper: HTMLElement): Coordinates {
      const [rCenterX, rCenterY] = findCenterForElement(reference);
      const [x, y] = findXYForGivenCenter(popper, [rCenterX, rCenterY]);
    
      return [x, y];
    }
    
    function findCenterForElement(reference: HTMLElement): Coordinates {
      const { left, top, width, height } = reference.getBoundingClientRect();
    
      const rCenterX = left + (width / 2);
      const rCenterY = top + (height / 2);
    
      return [rCenterX, rCenterY]
    }
    
    
    function findXYForGivenCenter(elm: HTMLElement, co: Coordinates): Coordinates {
    
      const [rCenterX, rCenterY] = co;
      const { width, height } = elm.getBoundingClientRect();
    
      const x = rCenterX - (width / 2);
      const y = rCenterY - (height / 2);
    
      return [x, y];
    }
    
    从'popmotion'导入{action};
    从“rxjs”导入{Observable,interval,animationFrameScheduler};
    从“rxjs/operators”导入{map,distinctUntilChanged};
    导出类型坐标=只读[编号,编号];
    导出函数StickToInteraction(参考:HtmleElement,popper:HtmleElement){
    返回操作(({update})=>{
    const sub=粘滞中心(参考,提升器)
    .subscribe(([x,y])=>update({x,y}));
    返回{
    停止:()=>{
    sub.取消订阅();
    }
    };
    });
    }
    函数stickToCenter(参考:HtmleElement,popper:HtmleElement):可观察{
    返回间隔(0,animationFrameScheduler).pipe(
    映射(()=>calculateCenter(参考,popper)),
    差异化(([oX,oY],[nX,nY])=>oX==nX&&oY==nY));
    }
    函数calculateCenter(参考:HtmleElement,popper:HtmleElement):坐标{
    const[rCenterX,rCenterY]=findCenterForElement(参考);
    常数[x,y]=findXYForGivenCenter(popper[rcenter,rcentry]);
    返回[x,y];
    }
    函数findCenterForElement(参考:HtmleElement):坐标{
    const{left,top,width,height}=reference.getBoundingClientRect();
    constrcenterx=左+(宽度/2);
    常数中心=顶部+(高度/2);
    返回[rCenterX,rCenterY]
    }
    函数findXYForGivenCenter(elm:HTMLElement,co:Coordinates):坐标{
    常数[rCenterX,rCenterY]=co;
    const{width,height}=elm.getBoundingClientRect();
    常数x=r中心-(宽度/2);
    常数y=R中心-(高度/2);
    返回[x,y];
    }
    
    这就是它在组件中的使用方式:

    import { styler, tween, composite } from 'popmotion';
    
    export type InlineBoxProps = {
      anchor: (ref: Ref<any>) => ComponentChildren;
      content: (ref: Ref<any>) => ComponentChildren;
    
      isOpen?: boolean;
    };
    
    
    const scaleEffect = composite({
      opacity: tween({ from: 0, to: 1, duration: 400 }),
      scale: tween({ from: 0.75, to: 1, duration: 240 })
    });
    
    
    export function InlineBox(props: InlineBoxProps) {
    
      // ... References for anchorElm, contentElm, etc.
    
      useEffect(() => {
    
        if (isOpen && anchorElm && contentElm) {
    
          const elmStyle = styler(contentElm);
    
          const positionEffect = stickToCenterAction(anchorElm, contentElm)
            .pipe(({ x, y }: any) => ({ y, x }));
    
          const finalEffect = composite({ boom: scaleEffect, translate: positionEffect })
            .filter((x) => !!x.translate)
            .pipe((({ boom, translate }: any) => ({ ...boom, ...translate })));
    
          const anim = finalEffect
            .start((v: any) => elmStyle.set(v));
    
          return () => anim.stop();
    
        }
    
      }, [isOpen, anchorElm, contentElm]);
    
      return (
        <Fragment>
          {anchor(anchorElmRef)}
          { isOpen
            ? <PortalIntoBody>{content(contentElmRef)}</PortalIntoBody>
            : null }
        </Fragment>
      );
    }
    
    从'popmotion'导入{styler,tween,composite};
    导出类型InlineBoxProps={
    锚定:(ref:ref)=>组件子级;
    内容:(ref:ref)=>ComponentChildren;
    等参数?:布尔数;
    };
    常数scaleEffect=复合({
    不透明度:tween({from:0,to:1,duration:400}),
    比例:tween({from:0.75,to:1,持续时间:240})
    });
    导出函数InlineBox(props:InlineBoxProps){
    //…anchorElm、contentElm等的参考资料。
    useffect(()=>{
    if(等参线和锚定线和内容线){
    const-elmStyle=styler(contentElm);
    const positionEffect=sticktoceinteraction(主播、内容)
    .pipe({x,y}:any)=>({y,x}));
    const finalefect=composite({boom:scaleefect,translate:positionefect})
    .filter((x)=>!!x.translate)
    .pipe((({boom,translate}:any)=>({…boom,…translate}));
    const anim=最终效果
    .start((v:any)=>elmStyle.set(v));
    return()=>anim.stop();
    }
    },[isOpen,anchorElm,contentElm];
    返回(
    {anchor(anchorElmRef)}
    {isOpen
    ?{content(contentElmRef)}
    :null}
    );
    }
    

    我研究了其他的实现,如:强> AND< <强> >强>材料< /强>等,但它们似乎没有考虑上述可能性,它们的菜单在设计级别上受到很大限制,具有任何泛型内容。