FabricJS-绘制和转换路径时的原点问题

时间:2018-08-02 12:40:54

标签: fabricjs

这是一个非常奇怪的问题,我无法理解并把我先前发布的this issue关联起来。

用户可以在画布上绘制线条(路径)。绘制直线后,将在直线上定位3个锚点。用户应该能够拖动整条线来重新定位它,或者拖动任意一个锚点来更改起点,终点或二次曲线。

我遇到的问题是,当最初绘制路径时,其路径本身的位置已关闭,而锚点的位置却是正确的。如果再沿那条路径拖动,您会看到一切都保持在同一位置。

请注意,每次更改路径时,我都会重绘该路径,这是因为如果拖动任一锚点(example of issue here),则该行周围的边界框不会更新。我还需要确保保存画布后的所有值都是最新的,以便以后可以重新绘制。

我几乎可以确定这与该行的originX和originY有关。更改它的确有效果(如果从Line变量中注释originX:“ center”和originY:“ center”,则可以看到初始绘制是正确的(尽管以我不喜欢的奇怪方式绘制),但是锚的任何后续移动或重新定位都会导致画布周围跳动)。

冗长的代码段的歉意。

let canvas;
let line;
let lineAnchorStart;
let lineAnchorEnd;
let lineAnchorBend;
let activeItem;
let drawingModeOn = true;
const button = document.getElementById('toggle-drawing-mode');

const Line = fabric.util.createClass(fabric.Path, {
  type: 'line',

  initialize(element, options) {
    options || (options = {});
    this.callSuper('initialize', element, options);

    // Set default options
    this.set({
      objectCaching: false,
      hasControls: false,
      // Comment out the below 2 lines
      originX: 'center',
      originY: 'center',
      fill: 'transparent',
      strokeWidth: 2,
      stroke: 'black',
      customProps: {
        category: 'lines',
      },
    });
  },
})

// Repositioning the line anchors after the line is moved or selected line is changed
const repositionLineAnchors = (line) => {
  lineAnchorStart.set({
    left: line.path[0][1],
    top: line.path[0][2]
  }).setCoords();

  lineAnchorEnd.set({
    left: line.path[1][3],
    top: line.path[1][4]
  }).setCoords();

  // If the line is perfectly straight then we want to keep the bend anchor in the middle
  // But if it has had bend applied to it then we let it stay where it was dragged
  if ((line.path[1][1] === line.path[1][3]) && (line.path[1][2] === line.path[1][4])) {
    const centerX = (line.path[0][1] + line.path[1][3]) / 2;
    const centerY = (line.path[0][2] + line.path[1][4]) / 2;
    lineAnchorBend.set({
      left: centerX,
      top: centerY
    }).setCoords();
  } else {
    lineAnchorBend.set({
      left: line.path[1][1],
      top: line.path[1][2]
    }).setCoords();
  }
}

// If the line anchors themselves are moved the
const handleLineAnchorMove = (target) => {
  switch (target.customProps.category) {
    // Moving the line anchors
    case 'line_anchor':
      switch (target.customProps.type) {
        case 'line_anchor_start':
          activeItem.path[0][1] = target.left;
          activeItem.path[0][2] = target.top;
          activeItem.setCoords();
          break;

        case 'line_anchor_end':
          // If the line is perfectly straight then we want to keep the quadratic value the same as the end point to avoid bending it
          // But if it has had bend applied to it then the two can be treated separately
          if ((activeItem.path[1][1] === activeItem.path[1][3]) && (activeItem.path[1][2] === activeItem.path[1][4])) {
            activeItem.path[1][1] = target.left;
            activeItem.path[1][2] = target.top;
          }
          activeItem.path[1][3] = target.left;
          activeItem.path[1][4] = target.top;
          activeItem.setCoords();
          break;

        case 'line_anchor_bend':
          activeItem.path[1][1] = target.left;
          activeItem.path[1][2] = target.top;
          activeItem.setCoords();
          break;
          // no default
      }
      // no default
  }
  fabricCanvas.renderAll();
}

const transformedPoint = (target) => {
  const points = [];
  const path = target.path;
  points.push(new fabric.Point(path[0][1], path[0][2]));
  points.push(new fabric.Point(path[1][3], path[1][4]));
  points.push(new fabric.Point(path[1][1], path[1][2]));
  const matrix = target.calcTransformMatrix();
  return points
    .map(p => new fabric.Point(p.x - target.minX, p.y - target.minY))
    .map(p => fabric.util.transformPoint(p, matrix));
}

const redrawPath = (oldLine) => {
  const transformedPoints = transformedPoint(oldLine);
  const path = [
    [],
    []
  ];
  path[0][0] = 'M';
  path[0][1] = transformedPoints[0].x;
  path[0][2] = transformedPoints[0].y;
  path[1][0] = 'Q';
  path[1][1] = transformedPoints[2].x;
  path[1][2] = transformedPoints[2].y;
  path[1][3] = transformedPoints[1].x;
  path[1][4] = transformedPoints[1].y;

  const newLine = drawLine(path);
  repositionLineAnchors(newLine);
  fabricCanvas.remove(oldLine).add(newLine).setActiveObject(newLine).renderAll();
}

const addLine = () => {
  let isDown;
  let startPoint;

  fabricCanvas.on('mouse:down', (options) => {
    hideLineAnchors();
    isDown = true;
    startPoint = fabricCanvas.getPointer(options.e);
    const path = [
      [],
      []
    ];
    path[0][0] = 'M';
    path[0][1] = startPoint.x;
    path[0][2] = startPoint.y;
    path[1][0] = 'Q';
    path[1][1] = startPoint.x;
    path[1][2] = startPoint.y;
    path[1][3] = startPoint.x;
    path[1][4] = startPoint.y;
    line = drawLine(path);
    line.selectable = false; // This is needed to prevent newly added lines from being dragged if drawing a line right next to them
    fabricCanvas.add(line).renderAll();
  });

  fabricCanvas.on('mouse:move', (options) => {
    if (!isDown) return;
    const pointer = fabricCanvas.getPointer(options.e);
    const lineWidth = pointer.x - startPoint.x;
    const lineHeight = pointer.y - startPoint.y;
    line.path[1][1] = pointer.x;
    line.path[1][2] = pointer.y;
    line.path[1][3] = pointer.x;
    line.path[1][4] = pointer.y;
    line.set({
      height: Math.abs(lineHeight),
      width: Math.abs(lineWidth)
    }).setCoords();
    lineAnchorEnd.set({
      left: pointer.x,
      top: pointer.y
    });
    fabricCanvas.renderAll();
  });

  fabricCanvas.on('mouse:up', (options) => {
    isDown = false;
    const endPoint = fabricCanvas.getPointer(options.e);
    redrawPath(line);
    disableDrawingMode();
  });
}

const handleObjectSelected = (e) => {
  let selectedItem = e.target;
  switch (selectedItem.customProps.category) {
    case 'line_anchor':
      // If we select a line anchor we actually want the line to be the active object
      selectedItem = activeItem;
      disableDrawingMode();
      break;

    case 'lines':
      repositionLineAnchors(selectedItem);
      showLineAnchors();
      fabricCanvas
        .bringToFront(lineAnchorStart)
        .bringToFront(lineAnchorEnd)
        .bringToFront(lineAnchorBend)
        .renderAll();
      break;
  }

  activeItem = selectedItem;
}

const handleObjectMoving = (e) => {
  const selectedItem = e.target;
  // If not a group
  if (selectedItem.customProps) {
    switch (selectedItem.customProps.category) {
      case 'line_anchor':
        switch (selectedItem.customProps.type) {
          case 'line_anchor_start':
          case 'line_anchor_end':
            lineAnchorBend.visible = false;
            // no default
        }
        handleLineAnchorMove(selectedItem);
        break;
      case 'lines':
        {
          lineAnchorStart.visible === true && hideLineAnchors();
          break;
        }
        // no default
    }
  }
}

const handleObjectModified = (e) => {
  const selectedItem = e.target;
  // If not a group
  if (selectedItem.customProps) {
    switch (selectedItem.customProps.category) {
      case 'lines':
        redrawPath(selectedItem);
        showLineAnchors();
        break;

      case 'line_anchor':
        redrawPath(activeItem);
        showLineAnchors();
        break;
        // no default
    }
  }
}

const disableDrawingMode = () => {
	drawingModeOn = false;
  setButtonText();
  fabricCanvas.selection = true;
  fabricCanvas.forEachObject((object, i) => {
    // This is to prevent the pitch background from being set to selectable (it is 0 in the object array)
    if (i > 0) {
      object.selectable = true;
    }
  });
  fabricCanvas.defaultCursor = 'default';
  fabricCanvas.hoverCursor = 'move';
  // Remove event listeners
  fabricCanvas
    .off('mouse:down')
    .off('mouse:move')
    .off('mouse:up')
    .off('mouse:out');
}

const enableDrawingMode = () => {
	drawingModeOn = true;
  setButtonText();
  fabricCanvas.selection = false;
  fabricCanvas.forEachObject((object) => {
    object.selectable = false;
  });
  // Allow line anchors to be draggable while in drawing mode
  lineAnchorStart.selectable = true;
  lineAnchorEnd.selectable = true;
  lineAnchorBend.selectable = true;
  fabricCanvas.defaultCursor = 'crosshair';
  fabricCanvas.hoverCursor = 'crosshair';
  lineAnchorStart.hoverCursor = 'move';
  lineAnchorEnd.hoverCursor = 'move';
  lineAnchorBend.hoverCursor = 'move';
  addLine();
}

const addLineAnchors = () => {
  lineAnchorStart = createLineAnchor('line_anchor_start');
  lineAnchorEnd = createLineAnchor('line_anchor_end');
  lineAnchorBend = createLineAnchorBend('line_anchor_bend');
  fabricCanvas.add(lineAnchorStart, lineAnchorEnd, lineAnchorBend);
}

const showLineAnchors = () => {
  if (lineAnchorStart) {
    lineAnchorStart.visible = true;
    lineAnchorEnd.visible = true;
    lineAnchorBend.visible = true;
  }
}

const hideLineAnchors = () => {
  if (lineAnchorStart) {
    lineAnchorStart.visible = false;
    lineAnchorEnd.visible = false;
    lineAnchorBend.visible = false;
  }
}

const createLineAnchor = anchorType => (
  new fabric.Rect({
    left: 0,
    top: 0,
    hasBorders: false,
    hasControls: false,
    originX: 'center',
    originY: 'center',
    height: 20,
    width: 20,
    strokeWidth: 2,
    stroke: 'green',
    fill: 'rgba(255, 255, 255, 0.1)',
    visible: false,
    excludeFromExport: true,
    customProps: {
      category: 'line_anchor',
      type: anchorType,
    },
  })
)

const createLineAnchorBend = anchorType => (
  new fabric.Circle({
    left: 0,
    top: 0,
    hasBorders: false,
    hasControls: false,
    originX: 'center',
    originY: 'center',
    radius: 10,
    strokeWidth: 2,
    stroke: 'blue',
    fill: 'rgba(63, 149, 220, 0.5)',
    visible: false,
    excludeFromExport: true,
    customProps: {
      category: 'line_anchor',
      type: anchorType,
    },
  })
)

const setButtonText = () => {
  if (drawingModeOn === true) {
    button.textContent = 'Disable Drawing Mode';
  } else {
    button.textContent = 'Enable Drawing Mode';
  }
}

const setDrawingMode = () => {
  if (drawingModeOn === true) {
    enableDrawingMode();
  } else {
    disableDrawingMode();
  }
}

const initFabric = () => {
  fabricCanvas = new fabric.Canvas('c', {
    height: 1000,
    width: 1000,
    targetFindTolerance: 15,
    selection: false,
    preserveObjectStacking: true,
    perPixelTargetFind: true, // To prevent the line having a selectable rectangle drawn around it and instead only have it selectable on direct click
  });

  fabricCanvas.on({
    'object:selected': handleObjectSelected,
    'object:moving': handleObjectMoving,
    'object:modified': handleObjectModified,
  });

  addLineAnchors();
}

const drawLine = (points) => (
  new Line(points)
)

button.addEventListener('click', () => {
  drawingModeOn = !drawingModeOn;
  setDrawingMode();
  setButtonText();
});

initFabric();
setDrawingMode();
canvas {
  border: 1px solid tomato;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.7.21/fabric.min.js"></script>
<canvas id="c"></canvas>
<button id="toggle-drawing-mode"></button>

1 个答案:

答案 0 :(得分:1)

很难调试所有代码,但是在'mouse-up'事件中,顶部和左侧位置被更改为旧行。在绘制newLine之前,我在**redrawPath**函数中做了以下更改:

newLine.set({"top": newLine.pathOffset.y, "left": newLine.pathOffset.x});
  repositionLineAnchors(newLine);

此外,在 transformedPoint 内,我删除了映射点的逻辑:

return points;

代替:

return points
    .map(p => new fabric.Point(p.x - target.minX, p.y - target.minY))
    .map(p => fabric.util.transformPoint(p, matrix));

这里是fiddle

更新:

您的逻辑几乎不错,您只需要设置

  1. originX: "left"originY: "top"而非'center'
  2. target.width-target.height中减去target.strokeWidthmapconst halfStroke = target.get("strokeWidth") / 2; return points.map(p => new fabric.Point(p.x - target.minX - target.width / 2 - halfStroke , p.y - target.minY - target.height / 2 - halfStroke)).map(p => fabric.util.transformPoint(p, matrix));
  3. mouse-move事件上设置pathOffset,宽度和高度,例如:

const dims = line._parseDimensions(); line.setWidth(dims.width); line.setHeight(dims.height); line.pathOffset.x = line.width/2 + line.left; line.pathOffset.y = line.height/2 + line.top;

    let canvas;
    let line;
    let lineAnchorStart;
    let lineAnchorEnd;
    let lineAnchorBend;
    let activeItem;
    let drawingModeOn = true;
    let fabricCanvas = null;
    const button = document.getElementById('toggle-drawing-mode');

    const Line = fabric.util.createClass(fabric.Path, {
      type: 'line',

      initialize(element, options) {
        options || (options = {});
        this.callSuper('initialize', element, options);

        // Set default options
        this.set({
          objectCaching: false,
          hasControls: false,
          // Comment out the below 2 lines
          originX: 'left',
          originY: 'top',
          fill: 'transparent',
          strokeWidth: 2,
          stroke: 'black',
          customProps: {
            category: 'lines',
          },
        });
      },
    })

    // Repositioning the line anchors after the line is moved or selected line is changed
    const repositionLineAnchors = (line) => {
      lineAnchorStart.set({
        left: line.path[0][1],
        top: line.path[0][2]
      }).setCoords();

      lineAnchorEnd.set({
        left: line.path[1][3],
        top: line.path[1][4]
      }).setCoords();

      // If the line is perfectly straight then we want to keep the bend anchor in the middle
      // But if it has had bend applied to it then we let it stay where it was dragged
      if ((line.path[1][1] === line.path[1][3]) && (line.path[1][2] === line.path[1][4])) {
        const centerX = (line.path[0][1] + line.path[1][3]) / 2;
        const centerY = (line.path[0][2] + line.path[1][4]) / 2;
        lineAnchorBend.set({
          left: centerX,
          top: centerY
        }).setCoords();
      } else {
        lineAnchorBend.set({
          left: line.path[1][1],
          top: line.path[1][2]
        }).setCoords();
      }
    }

    // If the line anchors themselves are moved the
    const handleLineAnchorMove = (target) => {
      switch (target.customProps.category) {
        // Moving the line anchors
        case 'line_anchor':
          switch (target.customProps.type) {
            case 'line_anchor_start':
              activeItem.path[0][1] = target.left;
              activeItem.path[0][2] = target.top;
              activeItem.setCoords();
              break;

            case 'line_anchor_end':
              // If the line is perfectly straight then we want to keep the quadratic value the same as the end point to avoid bending it
              // But if it has had bend applied to it then the two can be treated separately
              if ((activeItem.path[1][1] === activeItem.path[1][3]) && (activeItem.path[1][2] === activeItem.path[1][4])) {
                activeItem.path[1][1] = target.left;
                activeItem.path[1][2] = target.top;
              }
              activeItem.path[1][3] = target.left;
              activeItem.path[1][4] = target.top;
              activeItem.setCoords();
              break;

            case 'line_anchor_bend':
              activeItem.path[1][1] = target.left;
              activeItem.path[1][2] = target.top;
              activeItem.setCoords();
              break;
              // no default
          }
          // no default
      }
      fabricCanvas.renderAll();
    }

    const transformedPoint = (target) => {
      const points = [];
      const path = target.path;
      points.push(new fabric.Point(path[0][1], path[0][2]));
      points.push(new fabric.Point(path[1][3], path[1][4]));
      points.push(new fabric.Point(path[1][1], path[1][2]));
      const matrix = target.calcTransformMatrix();
      const halfStroke = target.get("strokeWidth") / 2;
      return points
         .map(p => new fabric.Point(p.x - target.minX - target.width / 2 - halfStroke, p.y - target.minY - target.height / 2 - halfStroke))
         .map(p => fabric.util.transformPoint(p, matrix));
    }

    const redrawPath = (oldLine) => {
    //oldLine.set({"originX": "left", "originY": "top"});
      const transformedPoints = transformedPoint(oldLine);
      const path = [
        [],
        []
      ];
      path[0][0] = 'M';
      path[0][1] = transformedPoints[0].x;
      path[0][2] = transformedPoints[0].y;
      path[1][0] = 'Q';
      path[1][1] = transformedPoints[2].x;
      path[1][2] = transformedPoints[2].y;
      path[1][3] = transformedPoints[1].x;
      path[1][4] = transformedPoints[1].y;
      



      const newLine = drawLine(path);
     	repositionLineAnchors(newLine);
      fabricCanvas.remove(oldLine).add(newLine).setActiveObject(newLine).renderAll();
    }

    const addLine = () => {
      let isDown;
      let startPoint;

      fabricCanvas.on('mouse:down', (options) => {
        hideLineAnchors();
        isDown = true;
        startPoint = fabricCanvas.getPointer(options.e);
        const path = [
          [],
          []
        ];
        path[0][0] = 'M';
        path[0][1] = startPoint.x;
        path[0][2] = startPoint.y;
        path[1][0] = 'Q';
        path[1][1] = startPoint.x;
        path[1][2] = startPoint.y;
        path[1][3] = startPoint.x;
        path[1][4] = startPoint.y;
        line = drawLine(path);
        line.selectable = false; // This is needed to prevent newly added lines from being dragged if drawing a line right next to them
        fabricCanvas.add(line).renderAll();
      });

      fabricCanvas.on('mouse:move', (options) => {
        if (!isDown) return;
        const pointer = fabricCanvas.getPointer(options.e);
        line.path[1][1] = pointer.x;
        line.path[1][2] = pointer.y;
        line.path[1][3] = pointer.x;
        line.path[1][4] = pointer.y;
        const dims = line._parseDimensions();
        line.setWidth(dims.width);
        line.setHeight(dims.height);
        line.pathOffset.x = line.width/2 + line.left;
    		line.pathOffset.y = line.height/2 + line.top;
       
        lineAnchorEnd.set({
          left: pointer.x,
          top: pointer.y
        });
         line.setCoords();
        fabricCanvas.renderAll();
      });

      fabricCanvas.on('mouse:up', (options) => {
        isDown = false;
        const endPoint = fabricCanvas.getPointer(options.e);
        redrawPath(line);
        disableDrawingMode();
      });
    }

    const handleObjectSelected = (e) => {
      let selectedItem = e.target;
      switch (selectedItem.customProps.category) {
        case 'line_anchor':
          // If we select a line anchor we actually want the line to be the active object
          selectedItem = activeItem;
          disableDrawingMode();
          break;

        case 'lines':
          repositionLineAnchors(selectedItem);
          showLineAnchors();
          fabricCanvas
            .bringToFront(lineAnchorStart)
            .bringToFront(lineAnchorEnd)
            .bringToFront(lineAnchorBend)
            .renderAll();
          break;
      }

      activeItem = selectedItem;
    }

    const handleObjectMoving = (e) => {
      const selectedItem = e.target;
      // If not a group
      if (selectedItem.customProps) {
        switch (selectedItem.customProps.category) {
          case 'line_anchor':
            switch (selectedItem.customProps.type) {
              case 'line_anchor_start':
              case 'line_anchor_end':
                lineAnchorBend.visible = false;
                // no default
            }
            handleLineAnchorMove(selectedItem);
            break;
          case 'lines':
            {
              lineAnchorStart.visible === true && hideLineAnchors();
              break;
            }
            // no default
        }
      }
    }

    const handleObjectModified = (e) => {
      const selectedItem = e.target;
      // If not a group
      if (selectedItem.customProps) {
        switch (selectedItem.customProps.category) {
          case 'lines':
            redrawPath(selectedItem);
            showLineAnchors();
            break;

          case 'line_anchor':
            redrawPath(activeItem);
            showLineAnchors();
            break;
            // no default
        }
      }
    }

    const disableDrawingMode = () => {
    	drawingModeOn = false;
      setButtonText();
      fabricCanvas.selection = true;
      fabricCanvas.forEachObject((object, i) => {
        // This is to prevent the pitch background from being set to selectable (it is 0 in the object array)
        if (i > 0) {
          object.selectable = true;
        }
      });
      fabricCanvas.defaultCursor = 'default';
      fabricCanvas.hoverCursor = 'move';
      // Remove event listeners
      fabricCanvas
        .off('mouse:down')
        .off('mouse:move')
        .off('mouse:up')
        .off('mouse:out');
    }

    const enableDrawingMode = () => {
    	drawingModeOn = true;
      setButtonText();
      fabricCanvas.selection = false;
      fabricCanvas.forEachObject((object) => {
        object.selectable = false;
      });
      // Allow line anchors to be draggable while in drawing mode
      lineAnchorStart.selectable = true;
      lineAnchorEnd.selectable = true;
      lineAnchorBend.selectable = true;
      fabricCanvas.defaultCursor = 'crosshair';
      fabricCanvas.hoverCursor = 'crosshair';
      lineAnchorStart.hoverCursor = 'move';
      lineAnchorEnd.hoverCursor = 'move';
      lineAnchorBend.hoverCursor = 'move';
      addLine();
    }

    const addLineAnchors = () => {
      lineAnchorStart = createLineAnchor('line_anchor_start');
      lineAnchorEnd = createLineAnchor('line_anchor_end');
      lineAnchorBend = createLineAnchorBend('line_anchor_bend');
      fabricCanvas.add(lineAnchorStart, lineAnchorEnd, lineAnchorBend);
    }

    const showLineAnchors = () => {
      if (lineAnchorStart) {
        lineAnchorStart.visible = true;
        lineAnchorEnd.visible = true;
        lineAnchorBend.visible = true;
      }
    }

    const hideLineAnchors = () => {
      if (lineAnchorStart) {
        lineAnchorStart.visible = false;
        lineAnchorEnd.visible = false;
        lineAnchorBend.visible = false;
      }
    }

    const createLineAnchor = anchorType => (
      new fabric.Rect({
        left: 0,
        top: 0,
        hasBorders: false,
        hasControls: false,
        originX: 'center',
        originY: 'center',
        height: 20,
        width: 20,
        strokeWidth: 2,
        stroke: 'green',
        fill: 'rgba(255, 255, 255, 0.1)',
        visible: false,
        excludeFromExport: true,
        customProps: {
          category: 'line_anchor',
          type: anchorType,
        },
      })
    )

    const createLineAnchorBend = anchorType => (
      new fabric.Circle({
        left: 0,
        top: 0,
        hasBorders: false,
        hasControls: false,
        originX: 'center',
        originY: 'center',
        radius: 10,
        strokeWidth: 2,
        stroke: 'blue',
        fill: 'rgba(63, 149, 220, 0.5)',
        visible: false,
        excludeFromExport: true,
        customProps: {
          category: 'line_anchor',
          type: anchorType,
        },
      })
    )

    const setButtonText = () => {
      if (drawingModeOn === true) {
        button.textContent = 'Disable Drawing Mode';
      } else {
        button.textContent = 'Enable Drawing Mode';
      }
    }

    const setDrawingMode = () => {
      if (drawingModeOn === true) {
        enableDrawingMode();
      } else {
        disableDrawingMode();
      }
    }

    const initFabric = () => {
      fabricCanvas = new fabric.Canvas('c', {
        height: 1000,
        width: 1000,
        targetFindTolerance: 15,
        selection: false,
        preserveObjectStacking: true,
        perPixelTargetFind: true, // To prevent the line having a selectable rectangle drawn around it and instead only have it selectable on direct click
      });

      fabricCanvas.on({
        'object:selected': handleObjectSelected,
        'object:moving': handleObjectMoving,
        'object:modified': handleObjectModified,
      });

      addLineAnchors();
    }

    const drawLine = (points) => (
      new Line(points)
    )

    button.addEventListener('click', () => {
      drawingModeOn = !drawingModeOn;
      setDrawingMode();
      setButtonText();
    });

    initFabric();
    setDrawingMode();
 canvas {
      border: 1px solid tomato;
    }
    <script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.7.20/fabric.min.js"></script>
      <canvas id="c"></canvas>
      <button id="toggle-drawing-mode"></button>

相关问题