react加ts制作可视化标尺(附加vue2版本)

先看一下效果,鼠标滑过标尺显示辅助线,点击标尺保留辅助线,双击辅助线消失


WeChatc9123af2459643ba9bfe6bb59be1e625.jpg

1.编写canvasRuler.js

const ruler = {
  /**
   * @el 容器 String
   * @height 刻度尺高度 Number
   * @maxScale 最大刻度 Number
   * @startValue 开始的值 Number
   * @region 区间 Array
   * @background 刻度尺背景颜色 String
   * @color 刻度线和字体的颜色 String
   * @markColor  中心刻度标记颜色 String
   * @isConstant 是否不断地获取值 Boolean
   * @success(res) 滑动结束后的回调 Function
   * */
  initRow: function (params) {
    const initParams = {
      el: params.el.current, // id or node
      height: params.height || 60,
      maxScale: params.maxScale || 200,
      startValue: params.startValue || 0,
      region: params.region || false,
      background: params.background || false,
      color: params.color || false,
      markColor: params.markColor || '#FFCC33',
      isConstant: params.isConstant || false,
      startGap: params.startGap || 0,
    };
    const elNode =
      initParams.el instanceof HTMLElement ? initParams.el : document.querySelector(initParams.el);
    if (!elNode) {
      console.warn('缺少容器');
      return false;
    }
    const rulerWrap = elNode; // 获取容器
    rulerWrap.style.height = initParams.height + 'px';
    // 最大刻度的小值是50
    initParams.maxScale = initParams.maxScale < 50 ? 50 : initParams.maxScale;

    if (initParams.startValue > initParams.maxScale) {
      initParams.startValue = initParams.maxScale;
    }
    const count = initParams.startValue; // 初始值
    const winWidth = rulerWrap.offsetWidth; // 容器宽度
    const division = 10; // 每个刻度的距离 分割线
    let canvas = rulerWrap.getElementsByTagName('canvas')[0]; // 获取容器下的canvas标签
    // 没有canvas就创建一个
    if (!canvas) {
      canvas = document.createElement('canvas'); // 创建canvas标签
      canvas.width = winWidth;
      canvas.height = initParams.height;
      rulerWrap.appendChild(canvas);
    }
    const cxt = canvas.getContext('2d');

    if (window.devicePixelRatio) {
      canvas.width = window.devicePixelRatio * winWidth;
      canvas.height = window.devicePixelRatio * initParams.height;
      cxt.scale(window.devicePixelRatio, window.devicePixelRatio);
    }
    // 画刻度尺
    function drawRuler(count) {
      // count = count - 25
      // 清空画布
      cxt.clearRect(0, 0, winWidth, initParams.height);
      // 刻度尺背景
      if (initParams.background) {
        cxt.fillStyle = initParams.background;
        cxt.fillRect(0, 0, canvas.width, initParams.height);
      }
      // 画刻度线
      for (let i = 0; i < initParams.maxScale; i++) {
        cxt.beginPath();
        cxt.save();
        cxt.strokeStyle = initParams.color ? initParams.color : '#bbb';
        cxt.lineWidth = 1;
        cxt.lineCap = 'round';
        const x = division * i - count * division + initParams.startGap;
        cxt.moveTo(x, initParams.height);
        cxt.lineTo(x, Math.floor(initParams.height * 0.8));
        if (i % 5 === 0) {
          cxt.strokeStyle = initParams.color ? initParams.color : '#666';
          cxt.lineTo(x, Math.floor(initParams.height * 0.5));
        }
        if (i % 10 === 0) {
          cxt.strokeStyle = initParams.color ? initParams.color : '#666';
          cxt.font = '10px Arial';
          cxt.fillStyle = initParams.color ? initParams.color : '#333';
          cxt.textAlign = 'left';
          cxt.textBaseline = 'middle';
          cxt.lineTo(x, 0);
          cxt.fillText(String(i * division), x + 2, Math.floor(initParams.height * 0.5));
        }
        cxt.stroke();
        cxt.restore();
        cxt.closePath();
      }
      // 底部线条
      cxt.beginPath();
      cxt.save();
      cxt.strokeStyle = initParams.color ? initParams.color : '#bbb';
      cxt.lineWidth = 1;
      cxt.lineCap = 'round';
      cxt.moveTo(0, initParams.height - 1);
      cxt.lineTo(winWidth, initParams.height - 1);
      cxt.stroke();
      cxt.restore();
      cxt.closePath();
    }
    if (window.devicePixelRatio) {
      canvas.style.transform = 'scale(' + 1 / window.devicePixelRatio + ')';
      canvas.style.transformOrigin = 'left top';
    }
    drawRuler(count);
  },
  initColumn: function (params) {
    const initParams = {
      el: params.el.current, // id or node
      width: params.width || 60,
      maxScale: params.maxScale || 200,
      startValue: params.startValue || 0,
      region: params.region || false,
      background: params.background || false,
      color: params.color || false,
      markColor: params.markColor || '#FFCC33',
      isConstant: params.isConstant || false,
      startGap: params.startGap || 0,
    };
    const elNode =
      initParams.el instanceof HTMLElement ? initParams.el : document.querySelector(initParams.el);
    if (!elNode) {
      console.warn('缺少容器');
      return false;
    }
    const rulerWrap = elNode; // 获取容器
    rulerWrap.style.width = initParams.height + 'px';
    // 最大刻度的小值是50
    initParams.maxScale = initParams.maxScale < 50 ? 50 : initParams.maxScale;

    if (initParams.startValue > initParams.maxScale) {
      initParams.startValue = initParams.maxScale;
    }
    const count = initParams.startValue; // 初始值
    const winHeight = rulerWrap.offsetHeight; // 容器宽度
    const division = 10; // 每个刻度的距离 分割线
    let canvas = rulerWrap.getElementsByTagName('canvas')[0]; // 获取容器下的canvas标签
    // 没有canvas就创建一个
    if (!canvas) {
      canvas = document.createElement('canvas'); // 创建canvas标签
      canvas.height = winHeight;
      canvas.width = initParams.width;
      rulerWrap.appendChild(canvas);
    }
    const cxt = canvas.getContext('2d');

    if (window.devicePixelRatio) {
      canvas.height = window.devicePixelRatio * winHeight;
      canvas.width = window.devicePixelRatio * initParams.width;
      cxt.scale(window.devicePixelRatio, window.devicePixelRatio);
    }
    // 画刻度尺
    function drawRuler(count) {
      // count = count - 25
      // 清空画布
      cxt.clearRect(0, 0, initParams.width, winHeight);
      // 刻度尺背景
      if (initParams.background) {
        cxt.fillStyle = initParams.background;
        cxt.fillRect(0, 0, canvas.width, winHeight);
      }
      // 画刻度线
      for (let i = 0; i < initParams.maxScale; i++) {
        cxt.beginPath();
        cxt.save();
        cxt.strokeStyle = initParams.color ? initParams.color : '#bbb';
        cxt.lineWidth = 1;
        cxt.lineCap = 'round';
        const y = division * i - count * division + initParams.startGap;
        cxt.moveTo(initParams.width, y);
        cxt.lineTo(Math.floor(initParams.width * 0.8), y);
        if (i % 5 === 0) {
          cxt.strokeStyle = initParams.color ? initParams.color : '#666';
          cxt.lineTo(Math.floor(initParams.width * 0.5), y);
        }
        if (i % 10 === 0) {
          cxt.strokeStyle = initParams.color ? initParams.color : '#666';
          cxt.font = '10px Arial';
          cxt.fillStyle = initParams.color ? initParams.color : '#333';
          cxt.textAlign = 'left';
          cxt.textBaseline = 'middle';
          cxt.lineTo(0, y);
          cxt.fillText(String(i * division), 0, y + 8);
        }
        cxt.stroke();
        cxt.restore();
        cxt.closePath();
      }
      cxt.beginPath();
      cxt.save();
      cxt.strokeStyle = initParams.color ? initParams.color : '#bbb';
      cxt.lineWidth = 1;
      cxt.lineCap = 'round';
      cxt.moveTo(initParams.width - 1, 0);
      cxt.lineTo(initParams.width - 1, winHeight);
      cxt.stroke();
      cxt.restore();
      cxt.closePath();
    }
    if (window.devicePixelRatio) {
      canvas.style.transform = 'scale(' + 1 / window.devicePixelRatio + ')';
      canvas.style.transformOrigin = 'left top';
    }
    drawRuler(count);
  },
};

export default ruler;

2.创建ruler.tsx文件

import { useState, useEffect, useRef } from 'react';
import type { MutableRefObject } from 'react';
import styles from './index.less';
import ruler from './canvasRuler';
import { useModel } from 'umi';
interface RulerProps {
  top: number;
  left: number;
}
function Ruler(props: RulerProps) {
  // const LEFT_SIDE_WIDTH = 240 // 左侧边栏的宽度
  const TOP_RULER_HEIGHT = 21; // 顶部标尺高度
  // const mainPanelScrollY = 0
  // console.log(useModel)
  const { addReferenceLinecol, addReferenceLinerow, removeReferenceLine, removeReferenceLinerow } =
    useModel('useRuler', (data) => ({
      addReferenceLinecol: data.addReferenceLinecol,
      addReferenceLinerow: data.addReferenceLinerow,
      removeReferenceLine: data.removeReferenceLine,
      removeReferenceLinerow: data.removeReferenceLinerow,
    }));

  const { referenceLine } = useModel('useRuler');
  const [topMoving, settopMoving] = useState('none');
  const [columnX, setcolumnX] = useState(0);
  const [leftMoving, setleftMoving] = useState('none');
  const [colHandleMoveReady, setcolHandleMoveReady] = useState(false);
  const [rowHandleMoveReady, setrowHandleMoveReady] = useState(false);
  const [rowY, setrowY] = useState(0);
  const columnXRef = useRef(columnX);
  const rowYRef = useRef(rowY);
  const columnXInRuler = () => {
    return columnX - TOP_RULER_HEIGHT;
  };
  const rowYInRuler = () => {
    return rowY - 50;
  };
  const addColumn = (value?: number) => {
    if (value) {
      addReferenceLinecol(value);
    } else {
      addReferenceLinecol(columnX);
    }
  };
  const addRow = (value?: number) => {
    // debugger
    if (value) {
      addReferenceLinerow(value);
    } else {
      addReferenceLinerow(rowY);
    }
  };
  // const rowElPositionFix = () => {
  //   return {
  //     transform: `translateY(${mainPanelScrollY}px)`
  //   }
  // }
  const topRuler: MutableRefObject<any> = useRef(null);
  const leftRuler: MutableRefObject<any> = useRef(null);
  const topMouseUp = (e: React.MouseEvent<HTMLDivElement, globalThis.MouseEvent>): void => {
    if (colHandleMoveReady) {
      return;
    }
    addColumn();
  };
  const topMouseEnter = (e: React.MouseEvent<HTMLDivElement, globalThis.MouseEvent>) => {
    settopMoving('block');
    setcolumnX(e.pageX - props.left);
  };
  const topMouseMove = (e: React.MouseEvent<HTMLDivElement, globalThis.MouseEvent>) => {
    setcolumnX(e.pageX - props.left);
  };
  const topMouseLeave = () => {
    if (colHandleMoveReady) {
      return;
    }
    settopMoving('none');
  };
  const leftMouseUp = (e: React.MouseEvent<HTMLDivElement, globalThis.MouseEvent>) => {
    if (rowHandleMoveReady) {
      return;
    }
    addRow();
  };

  const leftMouseEnter = () => {
    setleftMoving('block');
  };
  const leftMouseMove = (e: React.MouseEvent<HTMLDivElement, globalThis.MouseEvent>) => {
    // console.log(e.pageY - props.top)
    setrowY(e.pageY - props.top);
  };
  const leftMouseLeave = () => {
    if (rowHandleMoveReady) {
      return;
    }
    setleftMoving('none');
  };

  //添加topruler辅助线拖动功能
  const colHandleDown = (
    e: React.MouseEvent<HTMLDivElement, globalThis.MouseEvent>,
    index: number,
  ) => {
    setcolHandleMoveReady(true);
    let invoked = false;
    document.body.style.cursor = 'col-resize';
    const mouseMoveFn = (_e: any) => {
      if (!invoked) {
        removeReferenceLine(index);
        topMouseEnter(_e);
        invoked = true;
      }
      topMouseMove(_e);
    };
    const mouseUpFn = () => {
      if (invoked) {
        // console.log(columnXRef.current)
        addColumn(columnXRef.current);
      }
      setcolHandleMoveReady(false);
      topMouseLeave();
      document.removeEventListener('mousemove', mouseMoveFn);
      document.removeEventListener('mouseup', mouseUpFn);
      document.body.style.cursor = 'initial';
    };
    document.addEventListener('mousemove', mouseMoveFn);
    document.addEventListener('mouseup', mouseUpFn);
  };
  //添加leftruler辅助线拖动功能
  const rowHandleDown = (
    e: React.MouseEvent<HTMLDivElement, globalThis.MouseEvent>,
    index: number,
  ) => {
    setrowHandleMoveReady(true);
    let invoked = false;
    document.body.style.cursor = 'row-resize';
    const mouseMoveFn = (_e: any) => {
      if (!invoked) {
        removeReferenceLinerow(index);
        leftMouseEnter();
        invoked = true;
      }
      leftMouseMove(_e);
      // console.log(_e.pageY)
    };
    const mouseUpFn = () => {
      if (invoked) {
        // console.log(columnXRef.current)
        addRow(rowYRef.current);
      }
      setrowHandleMoveReady(false);
      leftMouseLeave();
      document.removeEventListener('mousemove', mouseMoveFn);
      document.removeEventListener('mouseup', mouseUpFn);
      document.body.style.cursor = 'initial';
    };
    document.addEventListener('mousemove', mouseMoveFn);
    document.addEventListener('mouseup', mouseUpFn);
  };
  useEffect(() => {
    columnXRef.current = columnX;
    rowYRef.current = rowY;
    ruler.initRow({
      el: topRuler,
      height: TOP_RULER_HEIGHT,
      color: 'rgba(255, 255, 255, 0.2)',
      background: '#0B0C12',
      startGap: TOP_RULER_HEIGHT - 1,
    });
    ruler.initColumn({
      el: leftRuler,
      width: TOP_RULER_HEIGHT,
      color: 'rgba(255, 255, 255, 0.2)',
      background: '#0B0C12',
      startGap: 50 - 1,
      maxScale: 501,
    });
  }, [columnX, rowY]);
  const referenceLineColList = referenceLine.col.map((item, index) => {
    return (
      <div
        key={'col' + index + item}
        className={`${styles.referenceLine} ${styles.column}`}
        style={{ left: item + 'px' }}
        onMouseDown={(e) => {
          colHandleDown(e, index);
        }}
        onDoubleClick={() => {
          removeReferenceLine(index);
        }}
      ></div>
    );
  });
  const referenceLineRowList = referenceLine.row.map((item, index) => {
    return (
      <div
        key={'row' + index + item}
        className={`${styles.referenceLine} ${styles.row}`}
        style={{ top: item + 'px' }}
        onMouseDown={(e) => {
          rowHandleDown(e, index);
        }}
        onDoubleClick={() => {
          removeReferenceLinerow(index);
        }}
      ></div>
    );
  });
  return (
    <div className={styles.posterRuler}>
      <div className={styles.topRuler}>{referenceLineColList}</div>
      <div className={styles.leftRuler}>{referenceLineRowList}</div>
      <div
        onMouseEnter={(e) => {
          topMouseEnter(e);
        }}
        onMouseMove={(e) => {
          topMouseMove(e);
        }}
        onMouseLeave={(e) => {
          topMouseLeave();
        }}
        onMouseUp={(e) => {
          topMouseUp(e);
        }}
        ref={topRuler}
        className={styles.topRuler}
      >
        <div
          style={{ left: columnX + 'px', display: topMoving }}
          className={`${styles.referenceLine} ${styles.column}`}
        >
          <div
            className={styles.tip}
            draggable="false"
            onDragStart={() => {
              return false;
            }}
          >
            {columnXInRuler()}
          </div>
        </div>
      </div>
      <div
        onMouseEnter={(e) => {
          leftMouseEnter();
        }}
        onMouseMove={(e) => {
          leftMouseMove(e);
        }}
        onMouseLeave={(e) => {
          leftMouseLeave();
        }}
        onMouseUp={(e) => {
          leftMouseUp(e);
        }}
        ref={leftRuler}
        className={styles.leftRuler}
      >
        <div
          style={{ top: rowY + 'px', display: leftMoving }}
          className={`${styles.referenceLine} ${styles.row} ${styles.moving}`}
        >
          <div
            className={styles.tip}
            draggable="false"
            onDragStart={() => {
              return false;
            }}
          >
            {rowYInRuler()}
          </div>
        </div>
      </div>
    </div>
  );
}
export default Ruler;

index.less文件

.posterRuler {
  position: absolute;
  z-index: 10;
}

.topRuler {
  position: absolute;
  top: 0;
  left: 0;
  z-index: 100;
  width: 5000px;
  height: 22px;
  cursor: col-resize;
}

.leftRuler {
  position: absolute;
  top: 0;
  left: 0;
  z-index: 99;
  width: 22px;
  height: 6000px;
  cursor: row-resize;
  transition: 0.5s;
}

.tip {
  position: absolute;
  padding: 4px 6px;
  // background-color: rgba($color: #000000, $alpha: 0.6);
  color: #fff;
  font-size: 13px;
  border-radius: 4px;
  user-select: none;
  pointer-events: none;
}

.column {
  top: 0;
  width: 3px;
  height: 5000px;
  margin-left: -2px;
  // background-color: #4A83FF;
  background: linear-gradient(90deg, transparent 33.3%, #eceef0 0, #53ebef 66.6%, transparent 0);
  cursor: col-resize;

  .tip {
    top: 30px;
    left: 10px;
  }
}

.row {
  left: 0;
  width: 5000px;
  height: 3px;
  margin-top: -2px;
  background: linear-gradient(0deg, transparent 33.3%, #eceef0 0, #53ebef 66.6%, transparent 0);
  cursor: row-resize;
  transition: 0.1s;

  .tip {
    top: -30px;
    left: 30px;
  }
}

.moving {
  transition: none;
}

.referenceLine {
  position: absolute;
  z-index: 99;
}

引入useModel(umi的redux)

import { useModel } from 'umi';

useRuler.js文件

import { useState, useCallback, useEffect, useRef } from 'react';

const useRuler = () => {
  const [referenceLine, setreferenceLine] = useState({
    row: [],
    col: [],
  });
  const [referenceLineOpened, setreferenceLineOpened] = useState(true);
  const [matchedLine, setmatchedLine] = useState(null);
  const [mainPanelScrollY, setmainPanelScrollY] = useState(0);
  let referenceLineOpenedRef = useRef(referenceLine);
  const SET_REFERENCE_LINE_VISIBLE = () => {
    setreferenceLineOpened(!referenceLineOpened);
  };
  const addReferenceLinecol = useCallback(
    (c) => {
      setreferenceLine({
        row: [...referenceLine.row],
        col: [...referenceLineOpenedRef.current.col, c],
      });
    },
    [referenceLine.row],
  );
  const removeReferenceLine = useCallback(
    (index) => {
      let oldcol = referenceLineOpenedRef.current.col;
      oldcol.splice(index, 1);
      setreferenceLine({
        row: [...referenceLine.row],
        col: [...oldcol],
      });
    },
    [referenceLine.row],
  );
  const removeReferenceLinerow = useCallback(
    (index) => {
      let oldrow = referenceLineOpenedRef.current.row;
      // debugger
      oldrow.splice(index, 1);
      setreferenceLine({
        row: [...oldrow],
        col: [...referenceLine.col],
      });
    },
    [referenceLine.col],
  );

  const addReferenceLinerow = useCallback(
    (c) => {
      setreferenceLine({
        row: [...referenceLineOpenedRef.current.row, c],
        col: [...referenceLine.col],
      });
    },
    [referenceLine.col],
  );
  useEffect(() => {
    referenceLineOpenedRef.current = referenceLine;
  }, [referenceLine]);
  return {
    referenceLine,
    referenceLineOpened,
    matchedLine,
    mainPanelScrollY,
    SET_REFERENCE_LINE_VISIBLE,
    addReferenceLinecol,
    addReferenceLinerow,
    removeReferenceLine,
    removeReferenceLinerow,
  };
};

export default useRuler;

3.引入ruler组件,传入距离top和left的值

import Ruler from './ruler/rulers';
<Ruler top={100} left={280}></Ruler>

4.下面附加一个vue2版本的

<template>
  <div class="poster-ruler">
    <template v-if="referenceLineOpened">
      <!-- 纵向参考线 -->
      <div
        v-for="(item, index) in referenceLine.col"
        :key="'col' + index + item"
        class="reference-line column"
        :style="{ left: item + 'px' }"
        @dblclick="removeReferenceLine({ type: 'col', index })"
        @mousedown="colHandleDown($event, index)"
      />
      <!-- 横向参考线 -->
      <div
        v-for="(item, index) in referenceLine.row"
        :key="'row' + index + item"
        class="reference-line row"
        :style="{ top: item + 'px', ...rowElPositionFix }"
        @dblclick="removeReferenceLine({ type: 'row', index })"
        @mousedown="rowHandleDown($event, index)"
      />
    </template>
    <!-- 顶部标尺 -->
    <div
      ref="topRuler"
      class="top-ruler"
      @mouseenter="topMouseEnter"
      @mousemove="topMouseMove"
      @mouseleave="topMouseLeave"
      @mouseup="topMouseUp"
    >
      <div
        v-if="topMoving"
        class="reference-line column"
        :style="{ left: columnX + 'px' }"
      >
        <div class="tip" draggable="false" ondragstart="return false">
          {{ columnXInRuler }}
        </div>
      </div>
    </div>
    <!-- 左侧标尺 -->
    <div
      ref="leftRuler"
      class="left-ruler"
      :style="rowElPositionFix"
      @mouseenter="leftMouseEnter"
      @mousemove="leftMouseMove"
      @mouseleave="leftMouseLeave"
      @mouseup="leftMouseUp"
    >
      <div
        v-if="leftMoving"
        class="reference-line row moving"
        :style="{ top: rowY + 'px' }"
      >
        <div class="tip" draggable="false" ondragstart="return false">
          {{ rowYInRuler }}
        </div>
      </div>
    </div>
    <!-- 动态匹配的参考线 -->
    <matched-line :row-el-position-fix="rowElPositionFix" />
  </div>
</template>

<script>
import ruler from 'poster/utils/canvasRuler'
import { mapState, mapActions } from 'poster/poster.vuex'
import matchedLine from './matchedLine'

const LEFT_SIDE_WIDTH = 240 // 左侧边栏的宽度
const TOP_RULER_HEIGHT = 21 // 顶部标尺高度

export default {
  components: { matchedLine },
  data() {
    return {
      topMoving: false,
      columnX: 0,
      leftMoving: false,
      rowY: 0
    }
  },
  computed: {
    ...mapState(['referenceLine', 'referenceLineOpened', 'mainPanelScrollY']),
    columnXInRuler() {
      return this.columnX - TOP_RULER_HEIGHT
    },
    rowYInRuler() {
      return this.rowY - 50
    },
    rowElPositionFix() {
      return {
        transform: `translateY(${this.mainPanelScrollY}px)`
      }
    }
  },
  mounted() {
    ruler.initRow({
      el: this.$refs.topRuler,
      height: TOP_RULER_HEIGHT,
      color: 'rgba(255, 255, 255, 0.5)',
      background: '#0B0C12',
      startGap: TOP_RULER_HEIGHT - 1
    })
    ruler.initColumn({
      el: this.$refs.leftRuler,
      width: TOP_RULER_HEIGHT,
      color: 'rgba(255, 255, 255, 0.5)',
      background: '#0B0C12',
      startGap: 50 - 1,
      maxScale: 501
    })
  },
  methods: {
    ...mapActions(['addReferenceLine', 'removeReferenceLine']),
    addColumn() {
      this.addReferenceLine({ type: 'col', position: this.columnX })
    },
    addRow() {
      this.addReferenceLine({ type: 'row', position: this.rowY })
    },
    topMouseEnter(e) {
      this.topMoving = true
      this.columnX = e.pageX - LEFT_SIDE_WIDTH
    },
    topMouseMove(e) {
      this.columnX = e.pageX - LEFT_SIDE_WIDTH
    },
    topMouseLeave() {
      if (this.colHandleMoveReady) {
        return
      }
      this.topMoving = false
    },
    topMouseUp() {
      if (this.colHandleMoveReady) {
        return
      }
      this.addColumn()
    },
    leftMouseEnter(e) {
      this.leftMoving = true
      // this.rowY = e.pageY - this.mainPanelScrollY
    },
    leftMouseMove(e) {
      this.rowY = e.pageY - this.mainPanelScrollY
    },
    leftMouseLeave() {
      if (this.rowHandleMoveReady) {
        return
      }
      this.leftMoving = false
    },
    leftMouseUp() {
      if (this.rowHandleMoveReady) {
        return
      }
      this.addRow()
    },
    colHandleDown(e, index) {
      this.colHandleMoveReady = true
      let invoked = false
      document.body.style.cursor = 'col-resize'
      const mouseMoveFn = _e => {
        if (!invoked) {
          this.removeReferenceLine({ type: 'col', index })
          this.topMouseEnter(_e)
          invoked = true
        }
        this.topMouseMove(_e)
      }
      const mouseUpFn = () => {
        if (invoked) {
          this.addColumn()
        }
        this.colHandleMoveReady = false
        this.topMouseLeave()
        document.removeEventListener('mouseup', mouseUpFn)
        document.removeEventListener('mousemove', mouseMoveFn)
        document.body.style.cursor = 'initial'
      }
      document.addEventListener('mousemove', mouseMoveFn)
      document.addEventListener('mouseup', mouseUpFn)
    },
    rowHandleDown(e, index) {
      this.rowHandleMoveReady = true
      let invoked = false
      document.body.style.cursor = 'row-resize'
      const mouseMoveFn = _e => {
        if (!invoked) {
          this.removeReferenceLine({ type: 'row', index })
          this.leftMouseEnter(_e)
          invoked = true
        }
        this.leftMouseMove(_e)
      }
      const mouseUpFn = () => {
        if (invoked) {
          this.addRow()
        }
        this.rowHandleMoveReady = false
        this.leftMouseLeave()
        document.removeEventListener('mouseup', mouseUpFn)
        document.removeEventListener('mousemove', mouseMoveFn)
        document.body.style.cursor = 'initial'
      }
      document.addEventListener('mousemove', mouseMoveFn)
      document.addEventListener('mouseup', mouseUpFn)
    }
  }
}
</script>

<style lang="scss" scoped>
.poster-ruler {
  z-index: 10;
}
.top-ruler {
  position: absolute;
  top: 0;
  left: 0;
  height: 22px;
  width: 5000px;
  /* border-bottom: 1px solid #bac3c9; */
  z-index: 100;
  cursor: col-resize;
}
.left-ruler {
  position: absolute;
  top: 0;
  left: 0;
  width: 22px;
  height: 6000px;
  z-index: 99;
  /* border-right: 1px solid #bac3c9; */
  cursor: row-resize;
  transition: 0.5s;
}
.reference-line {
  /* background-color: #ff0000; */
  position: absolute;
  z-index: 99;
  .tip {
    padding: 4px 6px;
    border-radius: 4px;
    background-color: rgba($color: #000000, $alpha: 0.6);
    color: #fff;
    font-size: 13px;
    position: absolute;
    user-select: none;
    pointer-events: none;
  }
  &.column {
    width: 3px;
    height: 5000px;
    margin-left: -2px;
    top: 0;
    cursor: col-resize;
    background-color: #4A83FF;
    background: linear-gradient(
      90deg,
      transparent 33.3%,
      #4A83FF 66.6%,
      #4A83FF 66.6%,
      transparent 0
    );
    .tip {
      top: 30px;
      left: 10px;
    }
  }
  &.row {
    width: 5000px;
    height: 3px;
    margin-top: -2px;
    left: 0;
    cursor: row-resize;
    transition: 0.1s;
    background: linear-gradient(
      0deg,
      transparent 33.3%,
      #4A83FF 66.6%,
      #4A83FF 66.6%,
      transparent 0
    );
    .tip {
      top: -30px;
      left: 30px;
    }
    &.moving {
      transition: none;
    }
  }
}
</style>

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,122评论 6 505
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,070评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,491评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,636评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,676评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,541评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,292评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,211评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,655评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,846评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,965评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,684评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,295评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,894评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,012评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,126评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,914评论 2 355