Vite2 + React17 + Typescript4 + Ant Design 4 低代码可视化拖拽页面编辑器(三)

470 阅读3分钟

紧接着前面的Vite2 + React17 + Typescript4 + Ant Design 4 低代码可视化拖拽页面编辑器(二)

Vue3版本请点击

  • 主页面结构:左侧菜单栏可选组件列表、中间容器画布、右侧编辑组件定义的属性;
  • 左侧菜单栏可选组件列表渲染;
  • 从菜单栏拖拽组件到容器;
  • 组件(Block)在容器的选中状态;
  • 容器内组件可移动位置;
  • 容器内的组件单选、多选、全选;
  • 命令队列及对应的快捷键;
  • 操作栏按钮:
    • 撤销、重做 重难点
    • 置顶、置底;
    • 删除、清空;
    • 预览、关闭编辑模式;
    • 导入、导出;
  • 右键菜单;
  • 拖拽参考线;
  • 组件可以拖动调整高度和宽度(height,width);
  • 组件可以设置预定好的属性(props);
  • 组件绑定值(model);
  • 设置组件标识(soltName),根据这个标识,定义某个组件的行为(函数触发)和插槽的实现(自定义视图);
  • 完善可选组件列表:
    • 输入框:双向绑定值,调整宽度;
    • 按钮:类型、文字、大小尺寸、拖拽调整宽高;
    • 图片:自定义图片地址、拖拽调整图片宽高;
    • 下拉框:预定义选项值、双向绑定字段;

十六、选中 Block 改变宽度、高度

基础结构

import { VisualEditorComponent } from '../../editor.utils';

import "./block-resize.scss";

export enum VisualBlockResizeDirection {
  start = 'start',
  center = 'center',
  end = 'end',
}

export const VisualBlockResize: React.FC<{
  component: VisualEditorComponent,
  onMouseDown?: (e: React.MouseEvent<HTMLDivElement>, direction: {
    horizotal: VisualBlockResizeDirection,
    vertical: VisualBlockResizeDirection
  }) => void
}> = (props) => {

  const h: JSX.Element[] = [];
  const resize = props.component.resize;
  const onMouseDown = (e: React.MouseEvent<HTMLDivElement>, direction: {
    horizotal: VisualBlockResizeDirection,
    vertical: VisualBlockResizeDirection
  }) => {
    props.onMouseDown && props.onMouseDown(e, direction);
  }
  // 由于使用了数组存放 jsx 编译后虚拟DOM,因此添加一个 key
  if (resize?.height) {
    h.push(<div className="visual-block__resize block-resize__top" key="resize__top" onMouseDown={e => onMouseDown(e, {
      horizotal: VisualBlockResizeDirection.center,
      vertical: VisualBlockResizeDirection.start
    })}></div>);
    h.push(<div className="visual-block__resize block-resize__bottom" key="resize__bottom" onMouseDown={e => onMouseDown(e, {
      horizotal: VisualBlockResizeDirection.center,
      vertical: VisualBlockResizeDirection.end
    })}></div>);
  }
  if (resize?.width) {
    h.push(<div className="visual-block__resize block-resize__left" key="resize__left" onMouseDown={e => onMouseDown(e, {
      horizotal: VisualBlockResizeDirection.start,
      vertical: VisualBlockResizeDirection.center
    })}></div>);
    h.push(<div className="visual-block__resize block-resize__right" key="resize__right" onMouseDown={e => onMouseDown(e, {
      horizotal: VisualBlockResizeDirection.end,
      vertical: VisualBlockResizeDirection.center
    })}></div>);
  }
  if (resize?.width && resize.height) {
    h.push(<div className="visual-block__resize block-resize__top__left" key="resize__top__left" onMouseDown={e => onMouseDown(e, {
      horizotal: VisualBlockResizeDirection.start,
      vertical: VisualBlockResizeDirection.start
    })}></div>);
    h.push(<div className="visual-block__resize block-resize__top__right" key="resize__top__right" onMouseDown={e => onMouseDown(e, {
      horizotal: VisualBlockResizeDirection.end,
      vertical: VisualBlockResizeDirection.start
    })}></div>);
    h.push(<div className="visual-block__resize block-resize__bottom__left" key="resize__bottom__left" onMouseDown={e => onMouseDown(e, {
      horizotal: VisualBlockResizeDirection.start,
      vertical: VisualBlockResizeDirection.end
    })}></div>);
    h.push(<div className="visual-block__resize block-resize__bottom__right" key="resize__bottom__right" onMouseDown={e => onMouseDown(e, {
      horizotal: VisualBlockResizeDirection.end,
      vertical: VisualBlockResizeDirection.end
    })}></div>);
  }
  return (<>{h}</>);
};

效果图

选中可调整宽高

传送门

事件监听

<VisualBlockResize onMouseDown={(e, direction) => resizeDraggier.mousedown(e, direction, block)} />}
//#region 拖拽改变 block 宽高
const resizeDraggier = (() => {
  const dragData = useRef({
    block: {} as VisualEditorBlockData,
    startX: 0, // 拖拽开始的时候, 鼠标的 left 值
    startY: 0, // 拖拽开始的时候, 鼠标的 top 值
    direction: {  // 拖拽开始的时候,标识那个方向的拖动, 拖动时需要对值取反
      horizotal: VisualBlockResizeDirection.start,
      vertical: VisualBlockResizeDirection.start
    },
    startBlock: {
      top: 0, // 拖拽开始的时候, block 组件的 top 值
      left: 0, // 拖拽开始的时候, block 组件的 left 值
      width: 0, // 拖拽开始的时候, block 组件的宽度
      height: 0, // 拖拽开始的时候, block 组件的高度
    },
    dragging: false // 首次移动才区派发 dragstart 事件
  });

  const mousemove = useCallbackRef((ev: MouseEvent) => {
    if (!dragData.current.dragging) {
      dragData.current.dragging = true;
      dragstart.emit();
    }
    let { clientX: moveX, clientY: moveY } = ev;
    const { startX, startY, startBlock, direction, dragging, block } = dragData.current;
    console.log('move')
    // 如果在水平方向,只有左右缩放
    if (direction.horizotal === VisualBlockResizeDirection.center) {
      moveX = startX;
    }
    // 如果在垂直方向,只有上下缩放
    if (direction.vertical === VisualBlockResizeDirection.center) {
      moveY = startY;
    }

    let durX = moveX - startX;
    let durY = moveY - startY;

    // 如果是四个方位角,垂直和水平都有缩放
    if (direction.vertical === VisualBlockResizeDirection.start) {
      durY = -durY;
      block.top = startBlock.top - durY;
    }
    if (direction.horizotal === VisualBlockResizeDirection.start) {
      durX = -durX;
      block.left = startBlock.left - durX;
    }

    const width = startBlock.width + durX;
    const height = startBlock.height + durY;
    block.width = width;
    block.height = height;
    block.hasReasize = true;

    // 更新
    methods.updateBlocks(props.value.blocks);
  });

  const mouseup = useCallbackRef(() => {
    document.removeEventListener('mousemove', mousemove);
    document.removeEventListener('mouseup', mouseup);
    if (dragData.current.dragging) {
      dragend.emit();
    }
    console.log('up')
  });

  const mousedown = useCallbackRef((ev: React.MouseEvent<HTMLDivElement>, direction: {
    horizotal: VisualBlockResizeDirection,
    vertical: VisualBlockResizeDirection
  }, block: VisualEditorBlockData) => {
    ev.stopPropagation();
    document.addEventListener('mousemove', mousemove);
    document.addEventListener('mouseup', mouseup);
    console.log('down')
    dragData.current = {
      block,
      startX: ev.clientX,
      startY: ev.clientY,
      direction,
      startBlock: {
        ...deepcopy(block)
      },
      dragging: false
    };
  });

  return {
    mousedown
  }
})();
//#endregion

传送门

Vite2 + React17 + Typescript4 + Ant Design 4 低代码可视化拖拽页面编辑器(四)

Vue3版本请点击