lowcode-engine icon indicating copy to clipboard operation
lowcode-engine copied to clipboard

组件拖拽是否可以直接拖拽后绝对定位

Open lulg opened this issue 1 year ago • 21 comments

想实现个类似大屏的场景,需要支持以下特性:

能否让组件拖拽到哪就显示在哪,默认绝对定位。 拖拽的组件是否可以覆盖在其他组件之上。

lulg avatar Mar 16 '23 02:03 lulg

目前没现成的东西

JackLian avatar Mar 16 '23 03:03 JackLian

我准备尝试一下这个需求

daifuyang avatar Mar 16 '23 07:03 daifuyang

一周后来反馈进度

daifuyang avatar Mar 16 '23 07:03 daifuyang

目前demo有了新的进展,利用plugin和custom/setter可以完成拖拽了。细节上还需要进一步的优化。稍后会奉上代码

daifuyang avatar Mar 17 '23 04:03 daifuyang

由于是利用官方的插件讨巧实现的,所以感觉有些卡顿。应该是官方做了防抖导致的。具体优化需要等待官方提供更优雅的解决方案。实现原理就是监听官方的画布拖拽事件然后通过设置器实时修改样式属性。稍后附上完整的例子。欢迎讨论提供更好的解决方案 目录结构: plugin-dragon-event |- setters |-|_ dragon-setter.tsx // 设置器 |_ index.tsx

在项目插件目录下,例如:demo-basic-antd/src/plugins下新建自定义插件 index.tsx

import { IPublicModelPluginContext } from '@alilc/lowcode-types';
import DragonSetter from './setters/dragon-setter';

const DragonEventPlugin = (ctx: IPublicModelPluginContext) => {
  return {
    async init() {
      const { setters,event, canvas } = ctx;
      setters.registerSetter("DragonSetter",DragonSetter)
      canvas.dragon?.onDragstart((e) => {
        event.emit('dragon.onDragstart', e);
      });

      canvas.dragon?.onDrag((e) => {
        event.emit('dragon.onDrag', e);
      });

      canvas.dragon?.onDragend((e) => {
        event.emit('dragon.onDragend', e);
      });
    },
  };
};
DragonEventPlugin.pluginName = 'DragonEventPlugin';
export default DragonEventPlugin;

配置设置器 dragon-setter.tsx

import { event } from '@alilc/lowcode-engine';

const DragonSetter = (props: any) => {
  const { field = {}, selected = {} } = props;
  const { parent, node } = field;

  const onDragstart = {
    canvasX: 0,
    canvasY: 0,
  };
  const onDragMove = {
    canvasX: 0,
    canvasY: 0,
  };

  let oldStyle: any = {};

  event.on(`common:dragon.onDragstart`, function (e) {
    onDragstart.canvasX = e.canvasX;
    onDragstart.canvasY = e.canvasY;
    oldStyle = parent?.getPropValue('style') || {};
  });

  event.on(`common:dragon.onDrag`, function (e) {
    const {
      dragObject: { nodes },
    } = e;

    if (nodes?.length > 0 && selected._id == nodes[0].id) {
      onDragMove.canvasX = e.canvasX;
      onDragMove.canvasY = e.canvasY;

      const { left: oldX = 0, top: oldY = 0 } = oldStyle;

      const parentNode = node?.parent;
      if (parentNode) {
        const parentEle = parentNode.getDOMNode();
        const { clientWidth: parentWidth = 0, clientHeight: parentHeight = 0 } = parentEle;

        const element = node.getDOMNode();
        const { clientWidth: eleWidth = 0, clientHeight: eleHeight = 0 } = element;

        // 移动的趋势

        // 大于0为往右位移,小于0为往左位移
        const distanceX = onDragMove.canvasX - onDragstart.canvasX;

        // 大于0往下位移,小于0往上位移
        const distanceY = onDragMove.canvasY - onDragstart.canvasY;

        // 新位置 = left + 移动趋势
        const newLeft = oldX + distanceX;

        // 新位置 = top + 移动趋势
        const newTop = oldY + distanceY;

        const style: any = {};
        if (distanceX >= 0) {
          if (newLeft + eleWidth < parentWidth) {
            style.left = newLeft;
            style.right = '';
          } else {
            style.left = '';
            style.right = 0;
          }
        } else {
          if (newLeft >= 0) {
            style.left = newLeft;
            style.right = '';
          } else {
            style.left = 0;
          }
        }

        if (distanceY >= 0) {
          if (newTop + eleHeight < parentHeight) {
            style.top = newTop;
            style.bottom = '';
          } else {
            style.top = '';
            style.bottom = 0;
          }
        } else {
          if (newTop >= 0) {
            style.top = newTop;
            style.bottom = '';
          } else {
            style.top = 0;
          }
        }

        parent.setPropValue('style', { position: 'absolute', ...style });
      }
    }
  });

  event.on(`common:dragon.onDragend`, function (e) {
    console.log('onDragend', onDragMove);
  });

  return <div>拖拽监听</div>;
};

export default DragonSetter;

index.ts

  await plugins.register(DragonEventPlugin);

配置设置器,组件方可拖拽

meta.js
"setter": {
      "componentName": "DragonSetter",
       "props": { },
        "initialValue": ""
}

daifuyang avatar Mar 18 '23 06:03 daifuyang

由于是利用官方的插件讨巧实现的,所以感觉有些卡顿。应该是官方做了防抖导致的。具体优化需要等待官方提供更优雅的解决方案。实现原理就是监听官方的画布拖拽事件然后通过设置器实时修改样式属性。稍后附上完整的例子。欢迎讨论提供更好的解决方案 目录结构: plugin-dragon-event |- setters |-|_ dragon-setter.tsx // 设置器 |_ index.tsx

在项目插件目录下,例如:demo-basic-antd/src/plugins下新建自定义插件 index.tsx

import { IPublicModelPluginContext } from '@alilc/lowcode-types';
import DragonSetter from './setters/dragon-setter';

const DragonEventPlugin = (ctx: IPublicModelPluginContext) => {
  return {
    async init() {
      const { setters,event, canvas } = ctx;
      setters.registerSetter("DragonSetter",DragonSetter)
      canvas.dragon?.onDragstart((e) => {
        event.emit('dragon.onDragstart', e);
      });

      canvas.dragon?.onDrag((e) => {
        event.emit('dragon.onDrag', e);
      });

      canvas.dragon?.onDragend((e) => {
        event.emit('dragon.onDragend', e);
      });
    },
  };
};
DragonEventPlugin.pluginName = 'DragonEventPlugin';
export default DragonEventPlugin;

配置设置器 dragon-setter.tsx

import { event } from '@alilc/lowcode-engine';

const DragonSetter = (props: any) => {
  const { field = {}, selected = {} } = props;
  const { parent, node } = field;

  const onDragstart = {
    canvasX: 0,
    canvasY: 0,
  };
  const onDragMove = {
    canvasX: 0,
    canvasY: 0,
  };

  let oldStyle: any = {};

  event.on(`common:dragon.onDragstart`, function (e) {
    onDragstart.canvasX = e.canvasX;
    onDragstart.canvasY = e.canvasY;
    oldStyle = parent?.getPropValue('style') || {};
  });

  event.on(`common:dragon.onDrag`, function (e) {
    const {
      dragObject: { nodes },
    } = e;

    if (nodes?.length > 0 && selected._id == nodes[0].id) {
      onDragMove.canvasX = e.canvasX;
      onDragMove.canvasY = e.canvasY;

      const { left: oldX = 0, top: oldY = 0 } = oldStyle;

      const parentNode = node?.parent;
      if (parentNode) {
        const parentEle = parentNode.getDOMNode();
        const { clientWidth: parentWidth = 0, clientHeight: parentHeight = 0 } = parentEle;

        const element = node.getDOMNode();
        const { clientWidth: eleWidth = 0, clientHeight: eleHeight = 0 } = element;

        // 移动的趋势

        // 大于0为往右位移,小于0为往左位移
        const distanceX = onDragMove.canvasX - onDragstart.canvasX;

        // 大于0往下位移,小于0往上位移
        const distanceY = onDragMove.canvasY - onDragstart.canvasY;

        // 新位置 = left + 移动趋势
        const newLeft = oldX + distanceX;

        // 新位置 = top + 移动趋势
        const newTop = oldY + distanceY;

        const style: any = {};
        if (distanceX >= 0) {
          if (newLeft + eleWidth < parentWidth) {
            style.left = newLeft;
            style.right = '';
          } else {
            style.left = '';
            style.right = 0;
          }
        } else {
          if (newLeft >= 0) {
            style.left = newLeft;
            style.right = '';
          } else {
            style.left = 0;
          }
        }

        if (distanceY >= 0) {
          if (newTop + eleHeight < parentHeight) {
            style.top = newTop;
            style.bottom = '';
          } else {
            style.top = '';
            style.bottom = 0;
          }
        } else {
          if (newTop >= 0) {
            style.top = newTop;
            style.bottom = '';
          } else {
            style.top = 0;
          }
        }

        parent.setPropValue('style', { position: 'absolute', ...style });
      }
    }
  });

  event.on(`common:dragon.onDragend`, function (e) {
    console.log('onDragend', onDragMove);
  });

  return <div>拖拽监听</div>;
};

export default DragonSetter;

index.ts

  await plugins.register(DragonEventPlugin);

配置设置器,组件方可拖拽

meta.js
"setter": {
      "componentName": "DragonSetter",
       "props": { },
        "initialValue": ""
}

膜拜

lulg avatar Mar 20 '23 01:03 lulg

经过最近的有一段时间研究,结合官方反馈了一些思路,有了新的进展。稍后我会附上自由拖拽和磁吸的例子

daifuyang avatar Mar 20 '23 02:03 daifuyang

插个眼,坐等大佬新进展 👍

ChiZng avatar Mar 21 '23 02:03 ChiZng

插个眼

guohejun avatar Mar 21 '23 06:03 guohejun

经过没日没夜的尝试:终于完成了可用的版本 http://lowcode.zerocmf.com/ 拖拽一个容器,选上自由布局,然后就可以拖入若干个可以移动的按钮了。遇到问题欢迎反馈。我继续优化 代码:https://github.com/daifuyang/zero-materials 我最近打算做一个用于nextjs的低代码物料。主要用于建站业务。不知道官方的渲染器不知道支不支持ssr。不支持的话后续可能还会实现基于nextjs的渲染器。有志同道合的小伙伴欢迎一起入坑。

daifuyang avatar Mar 22 '23 07:03 daifuyang

反馈个问题哈,把组件拖进自由布局的容器时,可以拖,但是焦点离开这个容器后,再进去拖动发现就失效了

Mrjing avatar Mar 22 '23 09:03 Mrjing

自由画布金禅之前做的有个demo可以参考 https://github.com/jinchanz/plugin-ablosute-designer

Mrjing avatar Mar 22 '23 09:03 Mrjing

反馈个问题哈,把组件拖进自由布局的容器时,可以拖,但是焦点离开这个容器后,再进去拖动发现就失效了

image 需要使组件处于激活状态才行

daifuyang avatar Mar 23 '23 00:03 daifuyang

https://github.com/loganjingdi/lowcode-material-magneticContainer 磁吸布局之前做了一个小demo,大家可以提提意见~

loganjingdi avatar Mar 26 '23 16:03 loganjingdi

https://github.com/loganjingdi/lowcode-material-magneticContainer 磁吸布局之前做了一个小demo,大家可以提提意见~

标题是绝对定位,和磁吸还不太一样。em...,可以参考金蝉大佬的 https://github.com/jinchanz/plugin-ablosute-designer , 不过这里面用的engine-core内核是做了些改造的,太久之前研究的,一时想不起来是哪个分支了。。。

loganjingdi avatar Mar 26 '23 16:03 loganjingdi

image

https://lowcode-engine.cn/demo/demo-general/index.html 官方 DEMO 中容器也可以转化成自由布局容器,欢迎体验。

liujuping avatar Mar 27 '23 08:03 liujuping

image

https://lowcode-engine.cn/demo/demo-general/index.html 官方 DEMO 中容器也可以转化成自由布局容器,欢迎体验。

赞,后续计划支持对齐用的参考线么

Mrjing avatar Mar 27 '23 09:03 Mrjing

image https://lowcode-engine.cn/demo/demo-general/index.html 官方 DEMO 中容器也可以转化成自由布局容器,欢迎体验。

赞,后续计划支持对齐用的参考线么

目前没有,欢迎共建~

liujuping avatar Mar 27 '23 09:03 liujuping

image

https://lowcode-engine.cn/demo/demo-general/index.html 官方 DEMO 中容器也可以转化成自由布局容器,欢迎体验。

反馈个问题: image

daifuyang avatar Mar 27 '23 09:03 daifuyang

AgneseLee avatar Jul 03 '23 15:07 AgneseLee

image

https://lowcode-engine.cn/demo/demo-general/index.html 官方 DEMO 中容器也可以转化成自由布局容器,欢迎体验。

固定组件面板,只能拖入一个组件,后面拖的组件报错

starsoul666 avatar Dec 19 '23 00:12 starsoul666