前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Popover 下拉容器定位原理浅析

Popover 下拉容器定位原理浅析

作者头像
不换
发布2024-04-30 14:12:13
680
发布2024-04-30 14:12:13
举报

(封面图片来自于基于 CC0 协议的 shopify )

使用过 Antd 的小伙伴应该深有体会,但是我们用的多了,也就想了解它怎么做的(:跟我一样吧

乍一看实现方式并不难,但是其实本篇文章更想表述一些细节的内容。

目标能力

  1. 精准定位
  2. 跟随屏幕缩放实时定位
  3. 跟随滚动

构造容器

代码语言:javascript
复制
"use client";
import {FC, PropsWithChildren, RefObject, useCallback, useEffect, useRef, useState} from "react";
import { createPortal } from "react-dom";
import { cloneElement } from "react";

const getStyle = (ref: RefObject<HTMLElement>, position: 'top' | 'bottom' | 'right' | 'left') => {
    const styles = ref.current?.getBoundingClientRect();

    const top = styles!.top + styles.height + 30 + 'px';
    const left = styles!.left + 'px';

    return {
        top,
        left,
    }
}

const Popover: FC<PropsWithChildren> = ({ children }) => {

    const ref = useRef(null);
    const [styles, setStyles] = useState({});

    const [opacity, setOpacity] = useState(0);


    useEffect(() => {
        const _ = getStyle(ref)
        setStyles(_);
    }, []);

    const onMouseEnter = useCallback(() => {
        setOpacity(1)
    }, [])
    const onMouseLeave = useCallback(() => {
        setOpacity(0)
    }, [])

    const newChild = cloneElement(children as any, {
        ref,
        onMouseEnter,
        onMouseLeave
    })

    return <>
        {newChild}
        {
            createPortal(<div className="popover-wrapper bg-amber-300 w-[100px] h-[80px] absolute" style={{...styles, opacity }}>
                    Hello )_________
            </div>, document.body)
        }
    </>
}


export  default Popover;

其实大家大可关注到精髓点:

  • createPortal
  • getBoundingClientRect

createPortal 的目的是脱离文档流,getBoundingClientRect 是为了拿到目标元素的具体位置 坐标宽高

我们可以设置 Popover 的模式,大体分为两种 hoverclick 模式:

  • click
    • • 监听 onClick 事件即可
  • hover
    • • 监听 onMouseEnter
    • • 监听 onMouseLeave

到这里,你觉得结束了吗?

答案是:没有,因为我们要考虑,点击模式常驻屏幕,滚动跟随移动的场景

在这里我们借助 ResizeObserver 来实现:

代码语言:javascript
复制
const resizeObserver = new ResizeObserver((entries) => {
  for (const entry of entries) {
       const _ = getStyle(ref)
       setStyles(_);
  }
});

resizeObserver.observe(document.target || document.body);

兼容性还是可以的,但是我们依旧要考虑我们程序的鲁棒性:

代码语言:javascript
复制

async function handleWatch() {
    if(!window.ResizeObserver) {
       await  import ('resize-observer-polyfill')
    }

    const resizeObserver = new ResizeObserver((entries) => {
      for (const entry of entries) {
           const _ = getStyle(ref)
           setStyles(_);
      }
    });

    resizeObserver.observe(document.target || document.body);
}

到此,一个简单的跟随移动 Popover 就实现了,如果你有更好的想法 ,欢迎交流~

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2024-04-03,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 不换的随想乐园 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 目标能力
  • 构造容器
相关产品与服务
容器服务
腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档