【实战】从零开始打造一个低代码平台——6、组件坐标系


对这个系列感兴趣的可以关注订阅专栏:从零开始打造一个低代码平台

前言

组件的属性里会有位置的信息,例如left top,很多鼠标事件也都会带位置信息,例如 MouseEvent,DragEvent里的clientXclientY等。left/topclientX/clientY是不同的坐标系。前者是相对父元素,后者是相对可视窗口。这一章,我们介绍一下前端几种位置坐标,以及如何使用它们。


一、几种位置坐标

在这里插入图片描述

一、clientX、clientY
相对于当前窗口可视区域的x,y坐标,MouseEvent DragEvent事件里的clientX clientY就是这个坐标系里的坐标,还有DOM组件接口getBoundingClientRect返回的也是这个坐标系下的位置。

二、pageX、pageY
相对于整个页面来说,包括了被卷去的body部分的长度。

三、screenX、screenY
相对于电脑屏幕的x,y坐标。

四、offsetX、offsetY
相对于带有定位的父盒子的x,y坐标。offsetX相对于style.leftoffsetY相当于style.top,两个差异:

  • 前者是数值型,例如100;后者是字符串,例如100px
  • 前者是只读的,后者可修改

这几种坐标中最常用的是clientX clientY,和offsetX offsetY style.left style.top

二、坐标转换

基于上一章,我们拿到DragEvent事件里的坐标clientX clientY,但我们给组件设置的位置属性是style.left style.top,之间怎么转换呢?

left top是相对于父元素的,所以我们将元素的left = clientX - parent.clientX,只要算出parent.clientX就可以得到元素的left

2.1 得到元素的父节点元素


function getParentElement(element: HtmlElement) {
	return element.parentElement;
}

2.2 得到父节点元素的clientX clientY

const { left: parentClientX, top: parentClientY } = parentElement.getBoundingClientRect();

2.3 得到元素的 left & top

const left = clientX - parentClientX;
const top = clientY - parentClientY;

三、解决组件拖拽的位置偏差

基于以上,我们现在可以解决上一章里组件拖拽时的位置偏差问题了。
改造完的Canvas如下:

import { useState } from "react";

interface CanvasProps {
  className?: string;
}

export const Canvas: React.FC<CanvasProps> = ({ className }) => {
  const [widgets, setWidgets] = useState<
    { type: string; left: number; top: number; width: number; height: number }[]
  >([]);

  return (
    <div
      className={className ?? ""}
      onDragOver={(e) => {
        e.preventDefault();
      }}
      onDrop={(e) => {
        const { clientX, clientY, target } = e;
        const targetElement = target as HTMLElement;
        const rect = targetElement.parentElement?.getBoundingClientRect();
        const width = 100;
        const height = 50;
        const left = clientX - (rect?.left ?? 0) - width / 2;
        const top = clientY - (rect?.top?? 0) - height / 2;
        setWidgets([
          ...widgets,
          { type: "btn", left, top, width, height },
        ]);
      }}
    >
      {widgets.map((widget, index) => {
        return (
          <div
            key={index}
            className="absolute bg-gray-700 rounded-md flex justify-center items-center"
            style={{
              left: `${widget.left}px`,
              top: `${widget.top}px`,
              width: `${widget.width}px`,
              height: `${widget.height}px`,
            }}
          >
            <i>Button</i>
          </div>
        );
      })}
    </div>
  );
};

现在拖拽生成的组件就跟鼠标位置一致了。同时,我们还做了一点点调整,让组件的位置偏移[width/2, height/2],这样让鼠标的位置刚好在组件的中心,更符合用户习惯。


总结

这一章我们介绍了前端的几种坐标系,和最常用的两种坐标之间如何转换,基于此我们解决了上一章遗留的组件位置偏差问题。在后续的章节中,我们还会大量地用到这两种坐标和对应的转换,来解决组件移动和缩放等常用操作,所以记住这几种坐标吧。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值