相关文章推荐
瘦瘦的西瓜  ·  python ...·  1 月前    · 
沉稳的油条  ·  chatgpt 课程李一舟 - 抖音·  1 年前    · 
傻傻的柚子  ·  java 8中使用Optional ...·  1 年前    · 
气势凌人的茶叶  ·  python ...·  1 年前    · 
dataModel. value !. blocks = [ ...focusData. value . unfocus , ] as VisualEditorBlockData []; tip : "ctrl+d, backspance, delete," ,
  • map一下 toolButtons 并编写对应样式
  • 	<div class="head">
              {toolButtons.map((btn, index) => (
                <div key={index} class="head-btn" onClick={btn.handler}>
                  <i class={`iconfont ${btn.icon}`}></i>
                  <span>{btn.label}</span>
                </div>
    

    commit代码

    八、给选中组件添加拖拽点

    水平、垂直拖拽

  • 按下shift键拖拽组件时,组件只能横向或纵向移动
  • 修改 blockDragger中的mousemove函数
  • 当鼠标横向移动的距离 大于 纵向移动的距离,将纵向的偏移置为0
  • 当鼠标横向移动的距离 小于 纵向移动的距离,将横向的偏移置为0
  •     const mousemove = (e: MouseEvent) => {
            let durX = e.clientX - dragState.startX;
            let durY = e.clientY - dragState.startY;
            // 按下shift键时,组件只能横向或纵向移动
            if (e.shiftKey) {
              // 当鼠标横向移动的距离 大于 纵向移动的距离,将纵向的偏移置为0
              if (Math.abs(durX) > Math.abs(durY)) {
                durY = 0;
              } else {
                durX = 0;
            focusData.value.focus.forEach((block, i) => {
              block.top = dragState.startPos[i].top + durY;
              block.left = dragState.startPos[i].left + durX;
    

    给组件添加拖拽点

    有些组件可以调整宽度和高度,有些只能调整高度 或 只能调整宽度

    可以调整宽高的显示六个拖拽点,只能调整宽度的显示左右两个点,只能调整高度的显示上下两个点

  • 给声明的数据结构VisualEditorComponent 添加 resize属性,控制组件是否可以调整宽度或者高度
  • export interface VisualEditorComponent {
      key: string;
      label: string;
      preview: () => JSX.Element;
      render: () => JSX.Element;
    +  resize?: { width?: boolean; height?: boolean }; 
    
  • visual.config中给注册的button input组件添加resize属性
  • visualConfig.registry("button", {
      label: "按钮",
      preview: () => <ElButton>按钮</ElButton>,
      render: () => <ElButton>渲染按钮</ElButton>,
      resize: { width: true, height: true }, // 可以调整宽度和高度
    visualConfig.registry("input", {
      label: "输入框",
      preview: () => <ElInput />,
      render: () => <ElInput />,
      resize: { width: true }, // 只能调整宽度
    

    新建 block-resizer组件,负责拖拽点的显示和拖拽拉伸

  • 接受 blockcomponent(在config中注册的组件对象)两个属性
  • 通过 widthheight 控制拖拽点的显示
  • import {
      VisualEditorBlockData,
      VisualEditorComponent,
      VisualEditorConfig,
    } from "@/packages/visual-editor.utils";
    import { defineComponent, PropType } from "vue";
    import "./style.scss";
    export const BlockResizer = defineComponent({
      props: {
        block: { type: Object as PropType<VisualEditorBlockData>, required: true },
        component: {
          type: Object as PropType<VisualEditorComponent>,
          required: true,
      setup(props) {
        const { width, height } = props.component.resize || {};
        return () => (
            {/* 显示上下中间的两个点 */}
            {height && (
                <div class="block-resize block-resize-top"></div>
                <div class="block-resize block-resize-bottom"></div>
            {/* 显示左右中间的两个点 */}
            {width && (
                <div class="block-resize block-resize-left"></div>
                <div class="block-resize block-resize-right"></div>
            {/* 显示组件的四个端点 */}
            {width && height && (
                <div class="block-resize block-resize-top-left"></div>
                <div class="block-resize block-resize-top-right"></div>
                <div class="block-resize block-resize-bottom-left"></div>
                <div class="block-resize block-resize-bottom-right"></div>
    
  • block-resizer样式
  • $space: 6px;
    $size: 6px;
    $primary: #409eff;
    .block-resize {
      position: absolute;
      top: -$space;
      left: -$space;
      right: -$space;
      bottom: -$space;
      width: $size;
      height: $size;
      background-color: $primary;
      &.block-resize-top {
        left: calc(50% - #{$size / 2});
        right: initial;
        bottom: initial;
      &.block-resize-bottom {
        left: calc(50% - #{$size / 2});
        right: initial;
        top: initial;
      &.block-resize-left {
        top: calc(50% - #{$size / 2});
        bottom: initial;
        right: initial;
      &.block-resize-right {
        top: calc(50% - #{$size / 2});
        left: initial;
        bottom: initial;
      &.block-resize-top-left {
        right: initial;
        bottom: initial;
      &.block-resize-top-right {
        left: initial;
        bottom: initial;
      &.block-resize-bottom-left {
        top: initial;
        right: initial;
      &.block-resize-bottom-right {
        left: initial;
        top: initial;
    

    commit代码

  • visual-editor-block 中引用
  • 组件选中状态,且可以调整宽高状态下才显示 拖拽点
  •       <div class={classes.value} style={styles.value} ref={el}>
              {Render}
              {props.block?.focus && (width || height) && (
                <BlockResizer
                  block={props.block!}
                  component={component!}
                ></BlockResizer>
    

    九、调整组件宽高大小

    1、数据类型补充

  • VisualEditorBlockData 添加 宽高 和 是否调整过宽高 的属性
  • VisualEditorComponentrender 方法添加回调参数 size
  • export interface VisualEditorBlockData {
      top: number;
      left: number;
      componentKey: string;
      adjustPosition: boolean; // 是否需要调整位置
      focus: boolean; // 是否是选中状态
    +  width: number;
    +  height: number;
    +  hasResize: boolean; // 是否调整过宽高
    export interface VisualEditorComponent {
      key: string;
      label: string;
      preview: () => JSX.Element;
    +  render: (data: { size: { width?: number; height?: number } }) => JSX.Element;
      resize?: { width?: boolean; height?: boolean };
    
    2、visual-editor-block 渲染组件时将 block的size属性传给render函数
        const renderProps = {
            size: props.block?.hasResize
                  width: props.block.width,
                  height: props.block.height,
              : {},
          const Render = component?.render(renderProps);
    
    3、visual.config 在组件的渲染函数中使用传进来的 size数据
    visualConfig.registry("button", {
      label: "按钮",
      preview: () => <ElButton>按钮</ElButton>,
    +  render: ({ size }) => (
        <ElButton style={{ width: `${size.width}px`, height: `${size.height}px` }}>
        </ElButton>
      resize: { width: true, height: true },
    visualConfig.registry("input", {
      label: "输入框",
      preview: () => <ElInput />,
    +  render: ({ size }) => <ElInput style={{ width: `${size.width}px` }} />,
      resize: { width: true },
    

    监听拉伸节点事件,调整组件大小

    声明Direction枚举,在mousemove时判断是哪个节点发生的事件,做不同操作

    实现onMousedown方法,并监听每个节点的 onMousedown事件

    当鼠标在节点按下的时候,触发 mousedown事件,在该事件中记录初始值,并监听mousemovemouseup事件

    鼠标点击节点移动时,执行 mousemove事件,在这里来计算组件的宽高 (核心点,有兴趣可以深入研究,内含彩蛋),修改block的 widthheight,重新进行渲染。

    import {
      VisualEditorBlockData,
      VisualEditorComponent,
      VisualEditorConfig,
    } from "@/packages/visual-editor.utils";
    import { defineComponent, PropType } from "vue";
    import "./style.scss";
    enum Direction {
      start = "start",
      center = "center",
      end = "end",
    export const BlockResizer = defineComponent({
      props: {
        block: { type: Object as PropType<VisualEditorBlockData>, required: true },
        component: {
          type: Object as PropType<VisualEditorComponent>,
          required: true,
      setup(props) {
        const { width, height } = props.component.resize || {};
        const onMousedown = (() => {
          let data = {
            startX: 0,
            startY: 0,
            startWidth: 0,
            startHeight: 0,
            startLeft: 0,
            startTop: 0,
            direction: {} as { horizontal: Direction; vertical: Direction },
          const mousemove = (e: MouseEvent) => {
            const {
              startX,
              startY,
              startWidth,
              startHeight,
              direction,
              startLeft,
              startTop,
            } = data;
            let { clientX: moveX, clientY: moveY } = e;
            if (direction.horizontal === Direction.center) {
              moveX = startX;
            if (direction.vertical === Direction.center) {
              moveY = startY;
            let durX = moveX - startX;
            let durY = moveY - startY;
            const block = props.block as VisualEditorBlockData;
            if (direction.vertical === Direction.start) {
              durY = -durY;
              block.top = startTop - durY;
            if (direction.horizontal === Direction.start) {
              durX = -durX;
              block.left = startLeft - durX;
            const width = startWidth + durX;
            const height = startHeight + durY;
            block.width = width;
            block.height = height;
            block.hasResize = true;
          const mouseup = (e: MouseEvent) => {
            console.log(e);
            document.body.removeEventListener("mousemove", mousemove);
            document.body.removeEventListener("mouseup", mouseup);
          const mousedown = (
            e: MouseEvent,
            direction: { horizontal: Direction; vertical: Direction }
          ) => {
            e.stopPropagation();
            document.body.addEventListener("mousemove", mousemove);
            document.body.addEventListener("mouseup", mouseup);
            data = {
              startX: e.clientX,
              startY: e.clientY,
              direction,
              startWidth: props.block.width,
              startHeight: props.block.height,
              startLeft: props.block.left,
              startTop: props.block.top,
          return mousedown;
        })();
        return () => (
            {height && (
                  class="block-resize block-resize-top"
                  onMousedown={(e) =>
                    onMousedown(e, {
                      horizontal: Direction.center,
                      vertical: Direction.start,
                ></div>
                  class="block-resize block-resize-bottom"
                  onMousedown={(e) =>
                    onMousedown(e, {
                      horizontal: Direction.center,
                      vertical: Direction.end,
                ></div>
            {width && (
                  class="block-resize block-resize-left"
                  onMousedown={(e) =>
                    onMousedown(e, {
                      horizontal: Direction.start,
                      vertical: Direction.center,
                ></div>
                  class="block-resize block-resize-right"
                  onMousedown={(e) =>
                    onMousedown(e, {
                      horizontal: Direction.end,
                      vertical: Direction.center,
                ></div>
            {width && height && (
                  class="block-resize block-resize-top-left"
                  onMousedown={(e) =>
                    onMousedown(e, {
                      horizontal: Direction.start,
                      vertical: Direction.start,
                ></div>
                  class="block-resize block-resize-top-right"
                  onMousedown={(e) =>
                    onMousedown(e, {
                      horizontal: Direction.end,
                      vertical: Direction.start,
                ></div>
                  class="block-resize block-resize-bottom-left"
                  onMousedown={(e) =>
                    onMousedown(e, {
                      horizontal: Direction.start,
                      vertical: Direction.end,
                ></div>
                  class="block-resize block-resize-bottom-right"
                  onMousedown={(e) =>
                    onMousedown(e, {
                      horizontal: Direction.end,
                      vertical: Direction.end,
                ></div>
    

    拖拽时的鼠标效果,需要给每个节点添加 cursor属性

  • 英文 东西南北 首字母来表示 上下左右

    commit代码

    完整代码 GitHub

    下一节 组件拖拽辅助线对齐与组件属性设置

    Miller Vue.js
    私信
  •