user1
2024-07-02 6aac0dd9c6d6bd3de148f3d6e123f9c91b1aab4b
src/components/graffiti/index.vue
@@ -1,11 +1,11 @@
<!-- 涂色连线题控件 -->
<template>
  <div class="page">
  <div class="page" :style="{height:imgHeight + 120 + 'px'}">
    <div class="main">
      <div id="canvas_panel">
        <canvas
          id="canvas"
          :style="{
            backgroundImage: `url(${backgroundImage})`,
            backgroundSize: 'cover',
            backgroundPosition: 'center',
          }"
@@ -13,7 +13,7 @@
        >
      </div>
    </div>
    <div class="footer">
    <div class="footer" :style="{backgroundColor:bcColor}">
      <BrushSize :size="brushSize" @change-size="onChangeSize"  @change-color="onChangeColor" />
      <ToolBtns :tool="brushTool" @change-tool="onChangeTool" />
    </div>
@@ -32,13 +32,23 @@
    },
    bcImg:{
      type:String
    },
    imgWidth:{
      type:Number
    },
    imgHeight:{
      type:Number
    },
    bcColor:{
      type:String,
      default:'#fff'
    }
  },
  data() {
    return {
      canvas: null,
      context: null,
      painting: false,
      painting: false,  // 记录状态,鼠标是否在按下状态
      historyData: [], // 存储历史数据,用于撤销
      brushSize: 5, // 笔刷大小
      brushColor: "#000000", // 笔刷颜色
@@ -53,12 +63,14 @@
  },
  mounted() {
    this.backgroundImage = this.bcImg
    this.canvas = document.getElementById("canvas");
    this.canvas = (this.container ? this.container : document).getElementById("canvas");
    if (this.canvas.getContext) {
      this.context = this.canvas.getContext("2d", { willReadFrequently: true });
      this.initCanvas();
      // window.addEventListener('resize', updateCanvasPosition);
      window.addEventListener("scroll",this.updateCanvasOffset,true); // 添加滚动条滚动事件监听器
      (
        this.container ? this.container : document
      ).addEventListener("scroll",this.updateCanvasOffset,true); // 添加滚动条滚动事件监听器
      this.getCanvasOffset();
      this.context.lineGap = "round";
      this.context.lineJoin = "round";
@@ -68,19 +80,42 @@
      this.canvas.addEventListener("mouseleave", this.closePaint);
    }
    this.toolClear();
    const oldData = localStorage.getItem('graffiti-data')
    const oldData = localStorage.getItem(this.config.activeBook.name + '-graffiti-' + this.page)
    if(oldData) {
      this.backgroundImage = oldData
    }
  },
  // watch:{
  //   backgroundImage:{
  //     handler(newValue) {
  //       if(newValue && this.context) {
  //         const imgData = new Image();
  //         // 分两种情况,初次的图片 直接用外部链接,需要加跨域和时间戳,二次保存的作为背景则不需要
  //         const oldData = localStorage.getItem(this.config.activeBook.name + '-graffiti-' + this.page)
  //         if(oldData) {
  //           imgData.src = newValue
  //         } else {
  //           imgData.src = newValue + '?' + new Date().getTime();
  //           imgData.setAttribute('crossOrigin', '');
  //         }
  //         imgData.onload = () => {
  //           this.context.drawImage(imgData,0,0)
  //         }
  //       }
  //     }
  //   }
  // },
  methods: {
    changeBackground(imgUrl) {
      this.backgroundImage = imgUrl;
    },
    // 初始化 画布,设置大小背景色
    initCanvas() {
      const that = this
      function resetCanvas() {
        const elPanel = document.getElementById("canvas_panel");
      const resetCanvas = () => {
        const elPanel = (
        this.container ? this.container : document
      ).getElementById("canvas_panel");
        that.canvas.width = elPanel.clientWidth;
        that.canvas.height = elPanel.clientHeight;
        that.context = that.canvas.getContext("2d", {
@@ -90,9 +125,22 @@
        that.context.fillRect(0, 0, that.canvas.width, that.canvas.height);
        that.context.fillStyle = "black";
        that.getCanvasOffset(); // 更新画布位置
        // 设置画布背景图
        const imgData = new Image();
        // 分两种情况,初次的图片 直接用外部链接,需要加跨域和时间戳,二次保存的作为背景则不需要
        const oldData = localStorage.getItem(this.config.activeBook.name + '-graffiti-' + this.page)
        if(oldData) {
          imgData.src = oldData
        } else {
          imgData.src = this.backgroundImage + '?' + new Date().getTime();
          imgData.setAttribute('crossOrigin', '');
        }
        imgData.onload = () => {
          this.context.drawImage(imgData,0,0,this.imgWidth,this.imgHeight)
        }
      }
      resetCanvas();
      // 监听窗口大小 ,窗口改变重新渲染画布
      window.addEventListener("resize", resetCanvas);
    },
@@ -110,7 +158,7 @@
        y: y - this.canvasOffset.top,
      };
    },
    // 鼠标抬起方法
    downCallback(event) {
      // 先保存之前的数据,用于撤销时恢复(绘制前保存,不是绘制后再保存)
      const data = this.context.getImageData(
@@ -129,7 +177,7 @@
        this.brushTool === "eraser" ? "#FFFFFF" : this.brushColor;
      this.painting = true;
    },
    // 鼠标移动方法(计算坐标并渲染轨迹)
    moveCallback(event) {
      if (!this.painting) {
        return;
@@ -142,19 +190,21 @@
    closePaint() {
      this.painting = false;
    },
    // 重新计算画布的偏移值
    updateCanvasOffset() {
      this.getCanvasOffset(); // 重新计算画布的偏移值
      this.getCanvasOffset();
    },
    // 改变笔刷大小
    onChangeSize(size) {
      this.brushSize = size;
    },
    // 改变笔刷颜色
    onChangeColor(color) {
      this.brushColor = color;
    },
    // 保存,清空等按钮
    onChangeTool(tool) {
      this.brushTool = tool;
      console.log(tool);
      switch (tool) {
        case "clear":
          this.toolClear();
@@ -167,18 +217,34 @@
          break;
      }
    },
    // 清空canvas所有内容(背景图除外)
    toolClear() {
      this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
      this.resetToolActive();
      // 新加内容,清空后将背景图再设置上去
      const imgData = new Image();
      // 分两种情况,初次的图片 直接用外部链接,需要加跨域和时间戳,二次保存的作为背景则不需要
      const oldData = localStorage.getItem(this.config.activeBook.name + '-graffiti-' + this.page)
      if(oldData) {
        imgData.src = oldData
      } else {
        imgData.src = this.backgroundImage + '?' + new Date().getTime();
        imgData.setAttribute('crossOrigin', '');
      }
      imgData.onload = () => {
        this.context.drawImage(imgData,0,0,this.imgWidth,this.imgHeight)
      }
    },
    // 原保存方法
    // 保存画布背景和划线到本地方法
    toolSave() {
      var imgData = this.canvas.toDataURL({format: 'png', quality:1, width:800,});
      localStorage.setItem('graffiti-data',imgData)
      localStorage.setItem(this.config.activeBook.name + '-graffiti-' + this.page,imgData)
    },
    // 保存方法,保存为一张图片并下载
    // 保存为一张图片并下载的方法
    saveImgData() {
      var link = document.createElement("a");
      var link = (
        this.container ? this.container : document
      ).createElement("a");
      var imgData = this.canvas.toDataURL({format: 'png', quality:1, width:20000, height:4000});
      var strDataURI = imgData.substr(22, imgData.length);
      var blob = dataURLtoBlob(imgData);
@@ -196,11 +262,14 @@
        return new Blob([u8arr], {type:mime});
      }
    },
    // 返回上一步方法(撤销)
    toolUndo() {
      //
      if (this.historyData.length <= 0) {
        this.resetToolActive();
        return;
      }
      // 将画的上一步数据写入canvas 重新渲染
      const lastIndex = this.historyData.length - 1;
      this.context.putImageData(this.historyData[lastIndex], 0, 0);
      this.historyData.pop();
@@ -210,7 +279,6 @@
    saveData(data) {
      this.historyData.length >= 50 && this.historyData.shift(); // 设置储存上限为50步
      this.historyData.push(data);
      console.log('数据',this.historyData);
    },
    // 清除、撤销、保存状态不需要保持,操作完后恢复笔刷状态
    resetToolActive() {
@@ -227,7 +295,6 @@
  display: flex;
  flex-direction: column;
  width:100%;
  height: 866px;
}
.main {
@@ -239,7 +306,6 @@
  justify-content: space-around;
  align-items: center;
  height: 88px;
  background-color: #fff;
}
#canvas_panel {