紧接着前面的Vite2 + React17 + Typescript4 + Ant Design 4 低代码可视化拖拽页面编辑器(二)
- 主页面结构:左侧菜单栏可选组件列表、中间容器画布、右侧编辑组件定义的属性;
- 左侧菜单栏可选组件列表渲染;
- 从菜单栏拖拽组件到容器;
- 组件(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 低代码可视化拖拽页面编辑器(四)