zhongshujie
2024-07-03 fc44c25a5c190a5bb9caefb92f8f6899fb8bb8e8
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,9 +13,12 @@
        >
      </div>
    </div>
    <div class="footer">
      <BrushSize :size="brushSize" @change-size="onChangeSize" />
      <ColorPicker :color="brushColor" @change-color="onChangeColor" />
    <div class="footer" :style="{ backgroundColor: bcColor }">
      <BrushSize
        :size="brushSize"
        @change-size="onChangeSize"
        @change-color="onChangeColor"
      />
      <ToolBtns :tool="brushTool" @change-tool="onChangeTool" />
    </div>
  </div>
@@ -23,16 +26,33 @@
<script>
import BrushSize from "./components/brushSize.vue";
import ColorPicker from "./components/colorPicker.vue";
import ToolBtns from "./components/toolBtns.vue";
export default {
  name: "graffiti",
  components: { BrushSize, ColorPicker, ToolBtns },
  components: { BrushSize, ToolBtns },
  props: {
    page: {
      type: Number,
    },
    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", // 笔刷颜色
@@ -41,17 +61,23 @@
        left: 0,
        top: 0,
      },
      backgroundImage:
        "https://t7.baidu.com/it/u=1819248061,230866778&fm=193&f=GIF",
      backgroundImage: "",
    };
  },
  mounted() {
    this.canvas = document.getElementById("canvas");
    this.backgroundImage = this.bcImg;
    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); // 添加滚动条滚动事件监听器
      (this.container ? this.container : document).addEventListener(
        "scroll",
        this.updateCanvasOffset,
        true
      ); // 添加滚动条滚动事件监听器
      this.getCanvasOffset();
      this.context.lineGap = "round";
      this.context.lineJoin = "round";
@@ -61,17 +87,49 @@
      this.canvas.addEventListener("mouseleave", this.closePaint);
    }
    this.toolClear();
    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");
        that.canvas.width = elPanel.clientWidth;
        that.canvas.height = elPanel.clientHeight;
      const that = this;
      const resetCanvas = () => {
        const elPanel = (
          this.container ? this.container : document
        ).getElementById("canvas_panel");
        try {
          that.canvas.width = elPanel.clientWidth;
          that.canvas.height = elPanel.clientHeight;
        } catch (error) {}
        that.context = that.canvas.getContext("2d", {
          willReadFrequently: true,
        }); // 添加这一行
@@ -79,9 +137,24 @@
        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);
    },
@@ -99,7 +172,7 @@
        y: y - this.canvasOffset.top,
      };
    },
    // 鼠标抬起方法
    downCallback(event) {
      // 先保存之前的数据,用于撤销时恢复(绘制前保存,不是绘制后再保存)
      const data = this.context.getImageData(
@@ -118,7 +191,7 @@
        this.brushTool === "eraser" ? "#FFFFFF" : this.brushColor;
      this.painting = true;
    },
    // 鼠标移动方法(计算坐标并渲染轨迹)
    moveCallback(event) {
      if (!this.painting) {
        return;
@@ -131,16 +204,19 @@
    closePaint() {
      this.painting = false;
    },
    // 重新计算画布的偏移值
    updateCanvasOffset() {
      this.getCanvasOffset(); // 重新计算画布的偏移值
      this.getCanvasOffset();
    },
    // 改变笔刷大小
    onChangeSize(size) {
      this.brushSize = size;
    },
    // 改变笔刷颜色
    onChangeColor(color) {
      this.brushColor = color;
    },
    // 保存,清空等按钮
    onChangeTool(tool) {
      this.brushTool = tool;
      switch (tool) {
@@ -155,32 +231,79 @@
          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() {
      const imageDataUrl = this.canvas.toDataURL("image/png");
      console.log(imageDataUrl);
      // const imgUrl = canvas.toDataURL('image/png');
      // const el = document.createElement('a');
      // el.setAttribute('href', imgUrl);
      // el.setAttribute('target', '_blank');
      // el.setAttribute('download', `graffiti-${Date.now()}`);
      // document.body.appendChild(el);
      // el.click();
      // document.body.removeChild(el);
      // resetToolActive();
      var imgData = this.canvas.toDataURL({
        format: "png",
        quality: 1,
        width: 800,
      });
      localStorage.setItem(
        this.config.activeBook.name + "-graffiti-" + this.page,
        imgData
      );
    },
    // 保存为一张图片并下载的方法
    saveImgData() {
      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);
      var objurl = URL.createObjectURL(blob);
      link.download = "grid1.png";
      link.href = objurl;
      link.click();
      function dataURLtoBlob(dataurl) {
        var arr = dataurl.split(","),
          mime = arr[0].match(/:(.*?);/)[1],
          bstr = atob(arr[1]),
          n = bstr.length,
          u8arr = new Uint8Array(n);
        while (n--) {
          u8arr[n] = bstr.charCodeAt(n);
        }
        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();
      this.resetToolActive();
    },
    // 存储数据
@@ -202,8 +325,7 @@
.page {
  display: flex;
  flex-direction: column;
  width: 1038px;
  height: 866px;
  width: 100%;
}
.main {
@@ -215,7 +337,6 @@
  justify-content: space-around;
  align-items: center;
  height: 88px;
  background-color: #fff;
}
#canvas_panel {