fabric.js简单小画板

<template>
  <div class="home">
    <div class="btnwrap">
      <span class="brushColor" ref="brushColor">
        <ColorPicker
          class="colorPicker"
          @on-active-change="updateValue"
          v-model="colors"
        />
      </span>
      <el-input-number
        :step="1"
        size="small"
        v-model="width"
        :min="1"
        :max="100"
        label="线条粗细"
      ></el-input-number>
      <span class="up">
        <el-button
          size="small"
          type="primary"
          icon="el-icon-wallet"
          @click="toFile"
          >上传图片</el-button
        >
        <input
          type="file"
          accept="image/*"
          style="display: none"
          id="upload"
          @change="uploadFile"
        />
      </span>
      <el-button-group>
        <el-button size="small" icon="el-icon-edit" @click.native="freeDraw"
          >自由画笔</el-button
        >
        <el-button size="small" icon="el-icon-rank" @click="freeMove"
          >移动</el-button
        >
        <el-button size="small" icon="el-icon-refresh-left" @click="undo"
          >撤销</el-button
        >
        <el-button size="small" icon="el-icon-refresh-right" @click="redo"
          >重做</el-button
        >
        <el-button
          size="small"
          @click="clear"
          icon="el-icon-delete"
          class="clearBtn"
          >清除</el-button
        >
        <el-button size="small" @click="exportPic" icon="el-icon-download"
          >导出</el-button
        >
        <el-button
          size="small"
          @click="unSelectDraw"
          icon="el-icon-document-delete"
          >不可选中</el-button
        >
      </el-button-group>
      <span v-if="currentTool == 'text'">
        <el-input-number
          :step="1"
          size="small"
          v-model="fontSize"
          :min="1"
          :max="100"
        ></el-input-number>
        文字大小</span
      >
      <div class="tools">
        <el-button-group class="simpleTool">
          <el-button size="small" @click="currentTool = 'pencil'">🖊</el-button>
          <el-button size="small" @click="currentTool = 'line'">|</el-button>
          <el-button size="small" @click="currentTool = 'rect'">口</el-button>
          <el-button size="small" @click="currentTool = 'circle'">⚪</el-button>
          <el-button size="small" @click="currentTool = 'triangle'"
            >▲</el-button
          >
          <el-button size="small" @click="currentTool = 'text'">Aa</el-button>
          <el-button size="small" @click="eraser">目</el-button>
        </el-button-group>
      </div>
    </div>
    <canvas id="c" width="1200px" height="700px"></canvas>
  </div>
</template>

<script>
export default {
  name: "Home",
  components: {},
  data() {
    return {
      // Control the state of eraser
      eraserState: "0",
      // Text
      textObject: null,
      // Text font size
      fontSize: "18",
      // Component Button Group
      currentTool: "pencil",
      // Mouse Up
      mouseUp: {
        x: "0",
        y: "0",
      },
      // Mouse Down
      mouseDown: {
        x: "0",
        y: "0",
      },
      // the computed state Array
      stateArr: [],
      // uploaded pictures list
      fileList: [],
      colors: "#478E7C",
      width: 5,
      canvas: null,
    };
  },
  watch: {
    width(val) {
      this.canvas.freeDrawingBrush.width = parseInt(this.width, 5);
    },
    currentTool() {
      this.cmposition();
      switch (this.currentTool) {
        case "pencil":
          this.initPencil();
          break;
        case "line":
          this.initLine();
          break;
        case "rect":
          this.initRect();
          break;
        case "circle":
          this.initCircle();
          break;
        case "triangle":
          this.initTriangle();
          break;
        case "text":
          this.initText();
          break;

        default:
          break;
      }
    },
  },
  methods: {
    initTriangle() {
      let pattern = new fabric.Triangle({
        left: this.mouseDown.x,
        top: this.mouseDown.y,
        width: this.mouseUp.x - this.mouseDown.x,
        height: this.mouseUp.y - this.mouseDown.y,
        stroke: this.colors,
        strokeWidth: this.width,
        fill: null,
      });
      // console.log("triangle");
      this.toggleDrawingObject(pattern);
    },
    initText() {
      this.canvas.isDrawingMode = false;
      this.canvas.selection = false;
      this.textObject = new fabric.Textbox("", {
        left: this.mouseDown.x,
        top: this.mouseDown.y,
        fontSize: this.fontSize,
        fill: this.colors,
        hasControls: false,
      });
      this.canvas.add(this.textObject);
      this.textObject.enterEditing();
      this.textObject.hiddenTextarea.focus();
      console.log("text");
    },
    initCircle() {
      let left = this.mouseDown.x;
      let top = this.mouseDown.y;
      let radius =
        Math.sqrt(
          (this.mouseUp.x - left) * (this.mouseUp.x - left) +
            (this.mouseUp.y - top) * (this.mouseUp.y - top)
        ) / 2;
      let pattern = new fabric.Circle({
        left: left,
        top: top,
        originX: "center",
        originY: "center",
        stroke: this.colors,
        strokeWidth: this.width,
        fill: null,
        radius: radius,
      });
      // console.log("circle");
      this.toggleDrawingObject(pattern);
    },
    initRect() {
      let pattern = new fabric.Rect({
        left: this.mouseDown.x,
        top: this.mouseDown.y,
        stroke: this.colors,
        strokeWidth: this.width,
        fill: null,
        width: this.mouseUp.x - this.mouseDown.x,
        height: this.mouseUp.y - this.mouseDown.y,
      });
      // console.log("rect");
      this.toggleDrawingObject(pattern);
    },
    initLine() {
      let pattern = new fabric.Line(
        [this.mouseDown.x, this.mouseDown.y, this.mouseUp.x, this.mouseUp.y],
        {
          stroke: this.colors, //画笔颜色
          strokeWidth: this.width, //画笔宽度
        }
      );
      // console.log("line");
      this.toggleDrawingObject(pattern);
    },
    //toggle the draw model
    toggleDrawingObject(canvasObject) {
      this.canvas.isDrawingMode = false;
      if (this.drawingObject) {
        this.canvas.remove(this.drawingObject);
      }
      // No the canvasObject, the painting will be extremely weird
      // console.log('drawingObject',this.drawingObject)
      this.canvas.add(canvasObject);
      this.drawingObject = canvasObject;

      if (this.textObject) {
        this.textObject.exitEditing();
        this.textObject = null;
      }
      this.unSelectDraw();
    },
    //click to start to upload file
    toFile() {
      document.getElementById("upload").click();
    },
    // Input image methods to get image file which was uploaded
    // the kind of return value is FileList
    uploadFile(e) {
      this.canvas.isDrawingMode = false;
      // event -> event.target(input Element) -> event.target.files
      // event.target.files[0] is the image what uploaded
      // console.log('e.target.files[0]',e.target.files[0])
      var file = e.target.files[0];
      var reader = new FileReader();
      reader.onload = (e) => {
        var data = e.target.result;
        fabric.Image.fromURL(data, (img) => {
          // add the control of size and rotation diretion
          img.hasControls = true;
          this.canvas.add(img).renderAll();
        });
      };
      reader.readAsDataURL(file);
      e.target.value = "";
    },
    // freeMove: change the position of the element
    freeMove() {
      // The effect of [mouse: up] Action always exists.
      // To eliminate the effect, use the eraserState to be state flag
      this.eraserState = 0;

      this.canvas.isDrawingMode = false;
      this.currentTool = "";
      this.canvas.skipTargetFind = false;
      this.canvas.selectable = true;
      this.canvas.on("mouse:up", (options) => {
        console.log("now tool[" + this.currentTool + "]catch me");
        // this.canvas.remove(this.canvas.getActiveObject());
      });
    },
    // redo the step
    redo() {
      // only stateArr is not null ,it can redo
      if (this.stateArr.length > 0) {
        this.isRedoing = true;
        this.canvas.add(this.stateArr.pop());
        this.canvas.renderAll();
        // console.log("redo successfully.");
      }
    },
    // undo the step
    undo() {
      if (this.canvas._objects.length > 0) {
        this.stateArr.push(this.canvas._objects.pop());
        this.canvas.renderAll();
        // console.log("undo successfully.");
      } else {
        this.$message({
          message: "You can not undo the blank canvas.",
          center: true,
          offset: 300,
          duration: 1000,
        });
      }
    },
    //导出图片
    exportPic() {
      //canvas的toDataURL()方法返回一个包含图片展示的数据URL
      const dataURL = this.canvas.toDataURL({
        width: this.canvas.width,
        height: this.canvas.height,
        left: 0,
        top: 0,
        format: "png",
      });
      //创建一个a标签,指向图片的URL地址,
      //点击即可下载名为canvas.png的图片
      const link = document.createElement("a");
      link.download = "canvas.png";
      link.href = dataURL;
      //当前文档的Body对象上挂载一个元素,此处为a标签
      //模拟a标签的点击,下载后移除该元素
      document.body.appendChild(link);
      link.click();
      document.body.removeChild(link);
    },
    //初始化笔刷颜色
    initBrushColor() {
      this.$refs.brushColor.style.backgroundColor = this.colors;
    },
    //每次选色后更新笔刷颜色
    updateValue(val) {
      console.log(val);
      this.colors = val;
      //   this.initBrushColor();
      this.canvas.freeDrawingBrush.color = this.colors;
    },
    //自由绘画
    freeDraw() {
      if (this.canvas == null) {
        this.canvas = new fabric.Canvas("c");
        this.canvas.backgroundColor = "#efefef";
        this.canvas.isDrawingMode = 1;
      }
      this.canvas.isDrawingMode = 1;

      this.canvas.freeDrawingBrush.color = this.colors;
      this.canvas.freeDrawingBrush.width = this.width;
      this.canvas.renderAll();
    },
    //清除画布
    clear() {
      this.canvas.clear();
      this.canvas.backgroundColor = "#efefef";
      this.fileList = [];
      //redo undo list also need to clear
      this.stateArr = [];
    },
    //clean mouse position
    cmposition() {
      (this.mouseDown = {}), (this.mouseUp = {});
    },
    // find which painting pencil is selected and decide do what
    // this mouse position will be the start point of the pattern
    initEvent() {
      let eventType = ["line", "circle", "rect", "triangle", "text"];
      this.canvas.on("mouse:down", (options) => {
        if (eventType.indexOf(this.currentTool) != -1) {
          console.log("have event");
          this.canvas.selection = false;
          this.idDrawing = true;
          this.mouseDown.x = options.e.clientX;
          // -32 because the top tools bar has height
          // to get the correct height, should minus its height
          this.mouseDown.y = options.e.clientY - 32;
          switch (this.currentTool) {
            // TextArea should add on the canvas firstly.
            // Otherwise it wont show the edit area
            case "text":
              this.initText();
              break;
            default:
              break;
          }
        }
      });
      // Mouse movement is a constant state, need always function
      this.canvas.on("mouse:move", (options) => {
        if (this.idDrawing && eventType.indexOf(this.currentTool) != -1) {
          this.mouseUp.x = options.e.clientX;
          this.mouseUp.y = options.e.clientY - 32;
          switch (this.currentTool) {
            case "pencil":
              this.initPencil();
              break;
            case "line":
              this.initLine();
              break;
            case "rect":
              this.initRect();
              break;
            case "circle":
              this.initCircle();
              break;
            case "triangle":
              this.initTriangle();
              break;
            case "text":
              this.initText();
              break;
            // case "eraser":
            //   this.eraser();
            //   break;

            default:
              break;
          }
        }
      });
      this.canvas.on("mouse:up", (options) => {
        if (eventType.indexOf(this.currentTool) != -1) {
          this.mouseUp.x = options.e.clientX;
          this.mouseUp.y = options.e.clientY - 42;
          this.drawingObject = null;
          this.idDrawing = false;
          this.cmposition();
        }
      });
    },
    // Pencil
    initPencil() {
      this.canvas.freeDrawingBrush.color = this.colors; //画笔颜色
      this.canvas.freeDrawingBrush.width = this.width; //画笔宽度
      this.canvas.isDrawingMode = 1;
      console.log("pencil");
    },
    // Eraser
    eraser() {
      // Eraser Ⅰ: use the brush which is same with the background color
      // this.unSelectDraw();
      // this.canvas.isDrawingMode = 1;
      // this.canvas.freeDrawingBrush.color = "#efefef";

      // Eraser Ⅱ : remove the chosen object
      this.freeMove();
      this.eraserState = 1;

      // The eraserState is affected by the end code [L461]
      // To avoid the suitation, we change the position of time that state flag change
      this.canvas.on("mouse:up", (options) => {
        if (this.eraserState == 1) {
          this.canvas.remove(this.canvas.getActiveObject());
        }
      });
      this.canvas.renderAll();
      //      this.eraserState = 0;
      console.log("Eraser---");
    },
    // About the situation of the different selected object
    unSelectDraw() {
      this.removeTextObject();
      this.canvas.skipTargetFind = true;
      // console.log("This whole canvas cant be selected.");
    },
    // When the selected area is the textArea
    removeTextObject() {
      this.currentType = "";
      if (this.textObject) {
        console.log("remove text");
        this.textObject.exitEditing();
        this.textObject = null;
      }
    },
  },
  mounted() {
    document.title = "小画板";
    this.initBrushColor();
    this.freeDraw();
    this.initEvent();

    //control the canvas actions
    this.canvas.on({
      "object:moving": (e) => {
        //when move the object, its opacity will be 0.5
        e.target.opacity = 0.5;
      },
      //after the move actions, the opacity will return 1
      "object:modified": (e) => {
        e.target.opacity = 1;
      },
    });
  },
};
</script>
<style>
.up {
  float: left;
}
.tools {
  background-color: cadetblue;
  width: 100%;
  float: left;
  position: absolute;
  bottom: 0vw;
  z-index: 10;
}
.simple el-button {
  margin: 10px 0 !important;
}
#c {
  position: relative;
}
</style>

posted @ 2021-08-20 10:00  乐盘游  阅读(266)  评论(0编辑  收藏  举报