Jelajahi Sumber

课堂助手批注

SanHQin 9 bulan lalu
induk
melakukan
5d802aa1f2

+ 399 - 0
src/components/classRoomHelper/component/AnnotationCanvas.vue

@@ -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>

+ 0 - 1
src/components/classRoomHelper/component/countdown.vue

@@ -583,7 +583,6 @@ ${_textData}
         });
     },
     insertData(data = null) {
-			return;
       if (!data) return;
       let params = [
         {

+ 6 - 3
src/components/classRoomHelper/index.vue

@@ -237,6 +237,7 @@
     </div>
     <levitatedSphere ref="levitatedSphereRef" @startTime="startTime" />
     <timepiece ref="timepieceRef" />
+		<AnnotationCanvas ref="AnnotationCanvasRef"/>
   </div>
 </template>
 
@@ -247,6 +248,7 @@ import dialogArea from "./component/dialogArea.vue";
 import levitatedSphere from "./component/levitatedSphere.vue";
 import timepiece from "./component/timepiece.vue";
 import countdown from "./component/countdown.vue";
+import AnnotationCanvas from './component/AnnotationCanvas.vue'
 export default {
   emits: ["refresh", "goStep", "backPage", "authority", "review","stopRecording","startRecording"],
   components: {
@@ -255,7 +257,8 @@ export default {
     dialogArea,
     levitatedSphere,
     timepiece,
-		countdown
+		countdown,
+		AnnotationCanvas
   },
   props: {
     courseDetail: {
@@ -412,7 +415,7 @@ export default {
       this.recordType = type;
     },
 		commentAndAnnotate(){
-			this.$message.info("批注")
+			this.$refs.AnnotationCanvasRef.open();
 		}
   }
 };
@@ -439,7 +442,7 @@ export default {
   flex-direction: column;
   align-items: center;
   overflow-y: auto;
-  overflow-x: visible; /* 确保横向溢出内容可见 */
+  overflow-x: hidden; /* 确保横向溢出内容可见 */
   position: relative;
 }