|
@@ -0,0 +1,399 @@
|
|
|
+<template>
|
|
|
+ <div v-if="show">
|
|
|
+ <el-row :gutter="20">
|
|
|
+ <el-col :span="24">
|
|
|
+ <canvas
|
|
|
+ ref="canvas"
|
|
|
+ class="drawing-canvas"
|
|
|
+ @mousedown="handleMouseDown"
|
|
|
+ @mousemove="handleMouseMove"
|
|
|
+ @mouseup="handleMouseUp"
|
|
|
+ @mouseleave="handleMouseUp"
|
|
|
+ @touchstart="handleTouchStart"
|
|
|
+ @touchmove="handleTouchMove"
|
|
|
+ @touchend="handleTouchEnd"
|
|
|
+ ></canvas>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+ <el-row
|
|
|
+ :gutter="20"
|
|
|
+ class="toolbar"
|
|
|
+ :style="`${showTool ? '' : 'display:none'}`"
|
|
|
+ >
|
|
|
+ <el-col :span="24" class="toolbar-container">
|
|
|
+ <div>
|
|
|
+ <el-button
|
|
|
+ @click="setMode('draw')"
|
|
|
+ icon="el-icon-edit"
|
|
|
+ :class="{ active: mode === 'draw' }"
|
|
|
+ >画笔</el-button
|
|
|
+ >
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div>
|
|
|
+ <el-slider
|
|
|
+ v-model="brushSize"
|
|
|
+ :min="1"
|
|
|
+ :max="100"
|
|
|
+ style="width: 100px"
|
|
|
+ ></el-slider>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div>
|
|
|
+ <el-color-picker
|
|
|
+ v-model="brushColor"
|
|
|
+ @active-change="colorChange"
|
|
|
+ ></el-color-picker>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div>
|
|
|
+ <el-button
|
|
|
+ @click="setMode('erase')"
|
|
|
+ icon="el-icon-connection"
|
|
|
+ :class="{ active: mode === 'erase' }"
|
|
|
+ >橡皮擦</el-button
|
|
|
+ >
|
|
|
+ </div>
|
|
|
+ <!-- <el-button @click="setMode('line')" :class="{ active: mode === 'line' }">直线</el-button>
|
|
|
+ <el-button @click="setMode('rect')" :class="{ active: mode === 'rect' }">矩形</el-button>
|
|
|
+ <el-button @click="setMode('circle')" :class="{ active: mode === 'circle' }">圆圈</el-button> -->
|
|
|
+ <div>
|
|
|
+ <el-button @click="clearCanvas" icon="el-icon-refresh-left"
|
|
|
+ >清空画布</el-button
|
|
|
+ >
|
|
|
+ </div>
|
|
|
+ <div>
|
|
|
+ <el-button-group style="display: flex">
|
|
|
+ <el-button @click="undo" icon="el-icon-arrow-left"
|
|
|
+ >上一步</el-button
|
|
|
+ >
|
|
|
+ <el-button @click="redo"
|
|
|
+ >下一步<i class="el-icon-arrow-right el-icon--right"></i
|
|
|
+ ></el-button>
|
|
|
+ </el-button-group>
|
|
|
+ </div>
|
|
|
+ <!-- <div>
|
|
|
+ <el-button @click="downloadCanvas" icon="el-icon-download"
|
|
|
+ >下载</el-button
|
|
|
+ >
|
|
|
+ </div> -->
|
|
|
+
|
|
|
+ <div>
|
|
|
+ <el-button @click.stop="close()">退出绘画</el-button>
|
|
|
+ </div>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+ </div>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script>
|
|
|
+// import html2canvas from "html2canvas";
|
|
|
+import "element-ui/lib/theme-chalk/index.css";
|
|
|
+
|
|
|
+export default {
|
|
|
+ data() {
|
|
|
+ return {
|
|
|
+ show: false,
|
|
|
+ showTool: false,
|
|
|
+ canvas: null,
|
|
|
+ ctx: null,
|
|
|
+ brushColor: "#000000",
|
|
|
+ brushSize: 5,
|
|
|
+ mode: "draw",
|
|
|
+ history: [],
|
|
|
+ historyStep: -1,
|
|
|
+ startX: 0,
|
|
|
+ startY: 0,
|
|
|
+ isDrawing: false,
|
|
|
+ tempShape: null,
|
|
|
+ tempCtx: null,
|
|
|
+ };
|
|
|
+ },
|
|
|
+ mounted() {
|
|
|
+ // this.initCanvas();
|
|
|
+ },
|
|
|
+ methods: {
|
|
|
+ open() {
|
|
|
+ this.show = true;
|
|
|
+ this.showTool = true;
|
|
|
+ this.mode = "draw";
|
|
|
+ this.history = [];
|
|
|
+ this.historyStep = -1;
|
|
|
+ this.startX = 0;
|
|
|
+ this.startY = 0;
|
|
|
+ this.isDrawing = false;
|
|
|
+ this.tempShape = null;
|
|
|
+ this.tempCtx = null;
|
|
|
+ this.$nextTick(() => {
|
|
|
+ document.body.style.overflow = "hidden"; //禁止页面滚动
|
|
|
+ this.initCanvas();
|
|
|
+ });
|
|
|
+ },
|
|
|
+ close() {
|
|
|
+ this.show = false;
|
|
|
+ this.showTool = false;
|
|
|
+ this.mode = "draw";
|
|
|
+ this.history = [];
|
|
|
+ this.historyStep = -1;
|
|
|
+ this.startX = 0;
|
|
|
+ this.startY = 0;
|
|
|
+ this.isDrawing = false;
|
|
|
+ this.tempShape = null;
|
|
|
+ this.tempCtx = null;
|
|
|
+ document.body.style.overflow = "auto"; //允许页面滚动
|
|
|
+ },
|
|
|
+ initCanvas() {
|
|
|
+ this.canvas = this.$refs.canvas;
|
|
|
+ this.ctx = this.canvas.getContext("2d");
|
|
|
+ this.canvas.width = window.innerWidth;
|
|
|
+ this.canvas.height = window.innerHeight;
|
|
|
+ this.saveState();
|
|
|
+ },
|
|
|
+ setMode(mode) {
|
|
|
+ this.mode = mode;
|
|
|
+ },
|
|
|
+ clearCanvas() {
|
|
|
+ this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
|
|
|
+ this.saveState();
|
|
|
+ },
|
|
|
+ downloadCanvas() {
|
|
|
+ return;
|
|
|
+ const canvas = this.$refs.canvas;
|
|
|
+ this.showTool = false;
|
|
|
+ // console.log(window.scrollX)
|
|
|
+ // console.log(window.scrollY)
|
|
|
+ // console.log(window.innerWidth)
|
|
|
+ // console.log(window.innerHeight)
|
|
|
+ // console.log(window.document.documentElement.clientWidth)
|
|
|
+ // console.log(window.document.documentElement.clientHeight)
|
|
|
+ // return;
|
|
|
+ this.$nextTick(() => {
|
|
|
+ html2canvas(document.body, {
|
|
|
+ x: window.scrollX,
|
|
|
+ y: window.scrollY,
|
|
|
+ width: window.innerWidth,
|
|
|
+ height: window.innerHeight,
|
|
|
+ windowWidth: document.documentElement.clientWidth,
|
|
|
+ windowHeight: document.documentElement.clientHeight,
|
|
|
+ scrollX: 0,
|
|
|
+ scrollY: 0,
|
|
|
+ }).then((capturedCanvas) => {
|
|
|
+ const ctx = capturedCanvas.getContext("2d");
|
|
|
+ ctx.drawImage(canvas, 0, 0);
|
|
|
+
|
|
|
+ const link = document.createElement("a");
|
|
|
+ link.href = capturedCanvas.toDataURL("image/png");
|
|
|
+ link.download = "canvas-drawing.png";
|
|
|
+ link.click();
|
|
|
+ this.showTool = true;
|
|
|
+ });
|
|
|
+ });
|
|
|
+ },
|
|
|
+ undo() {
|
|
|
+ if (this.historyStep > 0) {
|
|
|
+ this.historyStep--;
|
|
|
+ this.restoreState();
|
|
|
+ }
|
|
|
+ },
|
|
|
+ redo() {
|
|
|
+ if (this.historyStep < this.history.length - 1) {
|
|
|
+ this.historyStep++;
|
|
|
+ this.restoreState();
|
|
|
+ }
|
|
|
+ },
|
|
|
+ saveState() {
|
|
|
+ this.history = this.history.slice(0, this.historyStep + 1);
|
|
|
+ this.history.push(this.canvas.toDataURL());
|
|
|
+ this.historyStep++;
|
|
|
+ },
|
|
|
+ restoreState() {
|
|
|
+ const img = new Image();
|
|
|
+ img.src = this.history[this.historyStep];
|
|
|
+ img.onload = () => {
|
|
|
+ this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
|
|
|
+ this.ctx.drawImage(img, 0, 0);
|
|
|
+ };
|
|
|
+ },
|
|
|
+ handleMouseDown(event) {
|
|
|
+ if (this.mode === "erase" || this.mode === "draw") {
|
|
|
+ this.ctx.strokeStyle =
|
|
|
+ this.mode === "erase" ? "white" : this.brushColor;
|
|
|
+ this.ctx.lineWidth = this.brushSize;
|
|
|
+ this.ctx.lineCap = "round";
|
|
|
+ this.ctx.lineJoin = "round";
|
|
|
+ this.isDrawing = true;
|
|
|
+ this.ctx.beginPath();
|
|
|
+ const rect = this.canvas.getBoundingClientRect();
|
|
|
+ this.startX = event.clientX - rect.left;
|
|
|
+ this.startY = event.clientY - rect.top;
|
|
|
+ this.ctx.moveTo(this.startX, this.startY);
|
|
|
+ } else if (["line", "rect", "circle"].includes(this.mode)) {
|
|
|
+ this.tempShape = document.createElement("canvas");
|
|
|
+ this.tempShape.width = this.canvas.width;
|
|
|
+ this.tempShape.height = this.canvas.height;
|
|
|
+ this.tempCtx = this.tempShape.getContext("2d");
|
|
|
+ this.tempCtx.strokeStyle = this.brushColor;
|
|
|
+ this.tempCtx.lineWidth = this.brushSize;
|
|
|
+ this.tempCtx.fillStyle = this.brushColor;
|
|
|
+ this.isDrawing = true;
|
|
|
+ const rect = this.canvas.getBoundingClientRect();
|
|
|
+ this.startX = event.clientX - rect.left;
|
|
|
+ this.startY = event.clientY - rect.top;
|
|
|
+ }
|
|
|
+ },
|
|
|
+ handleMouseMove(event) {
|
|
|
+ if (!this.isDrawing) return;
|
|
|
+ const rect = this.canvas.getBoundingClientRect();
|
|
|
+ const x = event.clientX - rect.left;
|
|
|
+ const y = event.clientY - rect.top;
|
|
|
+ // this.drawPreviewSize(x,y)
|
|
|
+
|
|
|
+ if (this.mode === "draw") {
|
|
|
+ this.ctx.lineTo(x, y);
|
|
|
+ this.ctx.stroke();
|
|
|
+ } else if (this.mode === "erase") {
|
|
|
+ this.ctx.beginPath();
|
|
|
+ this.ctx.clearRect(
|
|
|
+ x - this.brushSize / 2,
|
|
|
+ y - this.brushSize / 2,
|
|
|
+ this.brushSize * 2,
|
|
|
+ this.brushSize * 2
|
|
|
+ );
|
|
|
+ } else if (this.tempShape) {
|
|
|
+ this.tempCtx.clearRect(
|
|
|
+ 0,
|
|
|
+ 0,
|
|
|
+ this.tempShape.width,
|
|
|
+ this.tempShape.height
|
|
|
+ );
|
|
|
+ if (this.mode === "line") {
|
|
|
+ this.tempCtx.beginPath();
|
|
|
+ this.tempCtx.moveTo(this.startX, this.startY);
|
|
|
+ this.tempCtx.lineTo(x, y);
|
|
|
+ this.tempCtx.stroke();
|
|
|
+ } else if (this.mode === "rect") {
|
|
|
+ this.tempCtx.strokeRect(
|
|
|
+ this.startX,
|
|
|
+ this.startY,
|
|
|
+ x - this.startX,
|
|
|
+ y - this.startY
|
|
|
+ );
|
|
|
+ } else if (this.mode === "circle") {
|
|
|
+ const radius = Math.sqrt(
|
|
|
+ Math.pow(x - this.startX, 2) + Math.pow(y - this.startY, 2)
|
|
|
+ );
|
|
|
+ this.tempCtx.beginPath();
|
|
|
+ this.tempCtx.arc(this.startX, this.startY, radius, 0, Math.PI * 2);
|
|
|
+ this.tempCtx.stroke();
|
|
|
+ }
|
|
|
+ this.ctx.drawImage(this.tempShape, 0, 0);
|
|
|
+ // this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
|
|
|
+ this.restoreState();
|
|
|
+ }
|
|
|
+ },
|
|
|
+ drawPreviewSize(x, y) {
|
|
|
+ if (this.mode === "draw" || this.mode === "erase") {
|
|
|
+ const previewSize = this.mode === "draw" ? this.brushSize : 20; // 设置橡皮擦的大小
|
|
|
+ const previewColor = this.mode === "draw" ? this.brushColor : "#FFFFFF"; // 设置橡皮擦的颜色
|
|
|
+
|
|
|
+ this.previewX = x;
|
|
|
+ this.previewY = y;
|
|
|
+
|
|
|
+ this.ctx.save();
|
|
|
+ this.ctx.strokeStyle = previewColor;
|
|
|
+ this.ctx.lineWidth = previewSize;
|
|
|
+ this.ctx.beginPath();
|
|
|
+ this.ctx.arc(x, y, previewSize / 2, 0, Math.PI * 2);
|
|
|
+ this.ctx.stroke();
|
|
|
+ this.ctx.restore();
|
|
|
+ }
|
|
|
+ },
|
|
|
+ clearPreview() {
|
|
|
+ this.ctx.clearRect(this.previewX - 12, this.previewY - 12, 25, 25); // 清除预览的区域
|
|
|
+ },
|
|
|
+ handleMouseUp() {
|
|
|
+ if (!this.isDrawing) return;
|
|
|
+ this.isDrawing = false;
|
|
|
+ this.ctx.closePath();
|
|
|
+ if (this.tempShape) {
|
|
|
+ this.ctx.drawImage(this.tempShape, 0, 0);
|
|
|
+ this.tempShape = null;
|
|
|
+ this.tempCtx = null;
|
|
|
+ }
|
|
|
+ this.saveState();
|
|
|
+ },
|
|
|
+ handleTouchStart(event) {
|
|
|
+ event.preventDefault();
|
|
|
+ const touch = event.touches[0];
|
|
|
+ this.handleMouseDown({
|
|
|
+ clientX: touch.clientX,
|
|
|
+ clientY: touch.clientY,
|
|
|
+ });
|
|
|
+ },
|
|
|
+ handleTouchMove(event) {
|
|
|
+ event.preventDefault();
|
|
|
+ const touch = event.touches[0];
|
|
|
+ this.handleMouseMove({
|
|
|
+ clientX: touch.clientX,
|
|
|
+ clientY: touch.clientY,
|
|
|
+ });
|
|
|
+ },
|
|
|
+ handleTouchEnd(event) {
|
|
|
+ event.preventDefault();
|
|
|
+ this.handleMouseUp();
|
|
|
+ },
|
|
|
+ colorChange(newColor) {
|
|
|
+ this.brushColor = newColor;
|
|
|
+ },
|
|
|
+ },
|
|
|
+ watch: {
|
|
|
+ brushColor(newColor) {
|
|
|
+ this.ctx.strokeStyle = newColor;
|
|
|
+ },
|
|
|
+ brushSize(newSize) {
|
|
|
+ this.ctx.lineWidth = newSize;
|
|
|
+ },
|
|
|
+ },
|
|
|
+};
|
|
|
+</script>
|
|
|
+
|
|
|
+<style scoped>
|
|
|
+.drawing-canvas {
|
|
|
+ position: fixed;
|
|
|
+ top: 0;
|
|
|
+ left: 0;
|
|
|
+ width: 100vw;
|
|
|
+ height: 100vh;
|
|
|
+ background-color: rgba(255, 255, 255, 0);
|
|
|
+}
|
|
|
+
|
|
|
+.toolbar {
|
|
|
+ position: fixed;
|
|
|
+ bottom: 0;
|
|
|
+ left: 50%;
|
|
|
+ width: auto;
|
|
|
+ background: rgba(0, 0, 0, 0.7);
|
|
|
+ color: white;
|
|
|
+ padding: 10px;
|
|
|
+ border-radius: 8px 8px 0 0;
|
|
|
+ box-shadow: 0 -2px 10px rgba(0, 0, 0, 0.5);
|
|
|
+ transform: translateX(-50%);
|
|
|
+ max-width: 100vw;
|
|
|
+ overflow: auto;
|
|
|
+}
|
|
|
+
|
|
|
+.toolbar-container {
|
|
|
+ display: flex;
|
|
|
+ justify-content: space-around;
|
|
|
+ align-items: center;
|
|
|
+}
|
|
|
+
|
|
|
+.toolbar-container > div {
|
|
|
+ margin: 0 20px;
|
|
|
+}
|
|
|
+
|
|
|
+.el-button.active {
|
|
|
+ background-color: #409eff;
|
|
|
+ color: white;
|
|
|
+}
|
|
|
+</style>
|