|
@@ -0,0 +1,1145 @@
|
|
|
|
|
+<template>
|
|
|
|
|
+
|
|
|
|
|
+ <div class="paper_container" ref="paperContainer" @mousedown="onMouseDown" @mousemove="onMouseMove" @mouseup="onMouseUp" @wheel="onWheel">
|
|
|
|
|
+ <canvas id="paperCanvas" ref="paperCanvas" class="paper_canvas" @mouseleave="onCanvasLeave"></canvas>
|
|
|
|
|
+ <div :id="`imgContainer${imageIndex}`" class="img_container">
|
|
|
|
|
+ <img v-if="paperImgUrl" :src="paperImgUrl" >
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="no_paper_url" v-if="paperImgUrl==''">
|
|
|
|
|
+ 暂无数据
|
|
|
|
|
+ <!-- 请先制作模版 -->
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div v-if="showContextMenu" class="custom_context_menu" :style="{ top: contextMenuY + 'px', left: contextMenuX + 'px' }" @click="hideContextMenu">
|
|
|
|
|
+ <div class="menu_item" @click="handleMenuAction('download')">图片另存为</div>
|
|
|
|
|
+ <div class="menu_item" @click="handleMenuAction('fitScreen')">适合屏幕</div>
|
|
|
|
|
+ <div class="menu_item" @click="handleMenuAction('zoomIn')">放大(向上滚轮)</div>
|
|
|
|
|
+ <div class="menu_item" @click="handleMenuAction('zoomOut')">缩小(向下滚轮)</div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+</template>
|
|
|
|
|
+<script>
|
|
|
|
|
+ import { throttle } from 'lodash';
|
|
|
|
|
+ import { mmToPx } from '@/utils/common.js'
|
|
|
|
|
+
|
|
|
|
|
+export default {
|
|
|
|
|
+ components: {
|
|
|
|
|
+
|
|
|
|
|
+ },
|
|
|
|
|
+ props:{
|
|
|
|
|
+ drawData:{
|
|
|
|
|
+ type:Array,
|
|
|
|
|
+ default:()=>[]
|
|
|
|
|
+ },//画的边框的数据
|
|
|
|
|
+
|
|
|
|
|
+ paperInfo:{
|
|
|
|
|
+ type:Object,
|
|
|
|
|
+ default:()=>null
|
|
|
|
|
+ },//纸张大小
|
|
|
|
|
+
|
|
|
|
|
+ paperImgUrl:{
|
|
|
|
|
+ type:String,
|
|
|
|
|
+ default:''
|
|
|
|
|
+ },//试卷地址相关信息
|
|
|
|
|
+
|
|
|
|
|
+ currentId:{
|
|
|
|
|
+ type:String,
|
|
|
|
|
+ default:''
|
|
|
|
|
+ },//当前选中的id
|
|
|
|
|
+
|
|
|
|
|
+ isDrag:{
|
|
|
|
|
+ type:Boolean,
|
|
|
|
|
+ default:true
|
|
|
|
|
+ },//是否可以拖动 默认可以拖动
|
|
|
|
|
+
|
|
|
|
|
+ isAbnormal:{
|
|
|
|
|
+ type:Boolean,
|
|
|
|
|
+ default:false
|
|
|
|
|
+ },//是否异常处使用 区别 异常处理使用的地方 正常答案显示蓝色
|
|
|
|
|
+
|
|
|
|
|
+ downLoadName:{
|
|
|
|
|
+ type:String,
|
|
|
|
|
+ default:''
|
|
|
|
|
+ },//下载图片的名称
|
|
|
|
|
+ imageIndex:{//图片索引
|
|
|
|
|
+ type:[String,Number],
|
|
|
|
|
+ default:''
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+ computed:{
|
|
|
|
|
+
|
|
|
|
|
+ },
|
|
|
|
|
+ data() {
|
|
|
|
|
+ return {
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+ position: {
|
|
|
|
|
+ x: 0,
|
|
|
|
|
+ y: 0
|
|
|
|
|
+ },//初始canvas图片位置
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+ startX: 0,//鼠标按下时的初始位置x坐标
|
|
|
|
|
+ startY: 0,//鼠标按下时的初始位置y坐标
|
|
|
|
|
+
|
|
|
|
|
+ scale: 1,//画布缩放倍数
|
|
|
|
|
+ isDragging:false,//是否拖动画布
|
|
|
|
|
+ isDrawing:false,//是否正在画线
|
|
|
|
|
+
|
|
|
|
|
+ drawType:0,//1画线 0 拖拽模式
|
|
|
|
|
+
|
|
|
|
|
+ image: null,
|
|
|
|
|
+
|
|
|
|
|
+ paperImgInfo:{
|
|
|
|
|
+ width:0,
|
|
|
|
|
+ height:0
|
|
|
|
|
+ },
|
|
|
|
|
+ canvasInfo:{
|
|
|
|
|
+ width:0,
|
|
|
|
|
+ height:0
|
|
|
|
|
+ },//画布的大小
|
|
|
|
|
+
|
|
|
|
|
+ canvas:null,//画板canvas
|
|
|
|
|
+ ctx:null,//画板上下文
|
|
|
|
|
+
|
|
|
|
|
+ zoomRate:0,//图片的缩放比例
|
|
|
|
|
+ dpr:window.devicePixelRatio || 1,
|
|
|
|
|
+ minScale:0.7,//最小缩放值
|
|
|
|
|
+ maxScale:4,//最大缩放值
|
|
|
|
|
+
|
|
|
|
|
+ rectPoint:{
|
|
|
|
|
+ startX:0,
|
|
|
|
|
+ startY:0,
|
|
|
|
|
+ endX:0,
|
|
|
|
|
+ endY:0
|
|
|
|
|
+ },//矩形起始坐标点
|
|
|
|
|
+
|
|
|
|
|
+ // 客观题区域
|
|
|
|
|
+ showObjectArea:false,//是否显示对象区域
|
|
|
|
|
+ addObjectAreaOption:{
|
|
|
|
|
+ derection:1,//排列方向 1 横线 2竖向
|
|
|
|
|
+ questionType:1,//题类型 1单选 2多选 3 判断
|
|
|
|
|
+ startNumber:16,//起始题号
|
|
|
|
|
+ endNumber:20,//结束题号
|
|
|
|
|
+ interval:1,//题号间隔 默认1
|
|
|
|
|
+ answerNumber:4,//选项个数 答案个数
|
|
|
|
|
+ answerWidth:0,//选项宽度
|
|
|
|
|
+ answerHeight:0,//选项高度
|
|
|
|
|
+ },//添加选择题设置信息
|
|
|
|
|
+ currenPoint:{
|
|
|
|
|
+ x:0,
|
|
|
|
|
+ y:0,
|
|
|
|
|
+ w:0,
|
|
|
|
|
+ h:0,
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ // isSelectBox:false,//是否选择框选答案区域框
|
|
|
|
|
+ containerWidth:0,//容器宽度
|
|
|
|
|
+ containerHeight:0,//容器高度
|
|
|
|
|
+ isInit:true, //是否是初始加载
|
|
|
|
|
+
|
|
|
|
|
+ showContextMenu: false,//是否显示右键菜单
|
|
|
|
|
+ contextMenuX: 0, // 右键菜单X坐标
|
|
|
|
|
+ contextMenuY: 0, // 右键菜单Y坐标
|
|
|
|
|
+
|
|
|
|
|
+ CacheAllWrong:new Image(),//缓存的全错图片
|
|
|
|
|
+ CacheAllRight:new Image(),//缓存的全对图片
|
|
|
|
|
+ CacheHalfRight:new Image(),//缓存的半对图片
|
|
|
|
|
+ CacheTypicalError:new Image(),//缓存的典型错误图片
|
|
|
|
|
+ CacheExcellentAnswer:new Image(),//缓存的优秀答案图片
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+ watch:{
|
|
|
|
|
+ paperImgUrl:
|
|
|
|
|
+ {
|
|
|
|
|
+ handler(newVal, oldVal)
|
|
|
|
|
+ {
|
|
|
|
|
+ console.log("地址变化了",this.paperImgUrl);
|
|
|
|
|
+ this.InitData();//初始化数据
|
|
|
|
|
+ },
|
|
|
|
|
+ deep: true ,// 如果需要深度监听数组内部对象的变化
|
|
|
|
|
+ listener: true//立即执行一次
|
|
|
|
|
+ // console.log("地址变化了",this.paperImgUrl);
|
|
|
|
|
+ // this.initData();//初始化数据
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ drawData:{
|
|
|
|
|
+ handler(newVal, oldVal) {
|
|
|
|
|
+
|
|
|
|
|
+ this.drawImage();//更新边框数据并重新绘制
|
|
|
|
|
+ },
|
|
|
|
|
+ deep: true // 如果需要深度监听数组内部对象的变化
|
|
|
|
|
+ },//边框数据
|
|
|
|
|
+
|
|
|
|
|
+ currentId()
|
|
|
|
|
+ {
|
|
|
|
|
+ // console.log("当前选中项的id变化了",this.currentId);
|
|
|
|
|
+ this.drawImage();//更新边框数据并重新绘制
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+ },
|
|
|
|
|
+ created(){
|
|
|
|
|
+
|
|
|
|
|
+ this.CacheAllWrong.src=require('../assets/icon/icon_all_wrong.svg');
|
|
|
|
|
+ this.CacheAllRight.src=require('../assets/icon/icon_all_right.svg');
|
|
|
|
|
+ this.CacheHalfRight.src=require('../assets/icon/icon_half_right.svg');
|
|
|
|
|
+ this.CacheTypicalError.src=require('../assets/tool/model_2.png');
|
|
|
|
|
+ this.CacheExcellentAnswer.src=require('../assets/tool/model_1.png');
|
|
|
|
|
+ this.CacheAllWrong.onload=()=>{
|
|
|
|
|
+ console.log("缓存全错图片加载完成");
|
|
|
|
|
+ };
|
|
|
|
|
+ this.CacheAllRight.onload=()=>{
|
|
|
|
|
+ console.log("缓存全对图片加载完成");
|
|
|
|
|
+ };
|
|
|
|
|
+ this.CacheHalfRight.onload=()=>{
|
|
|
|
|
+ console.log("缓存半对图片加载完成");
|
|
|
|
|
+ };
|
|
|
|
|
+ this.CacheTypicalError.onload=()=>{
|
|
|
|
|
+ console.log("缓存典型错误图片加载完成");
|
|
|
|
|
+ };
|
|
|
|
|
+ this.CacheExcellentAnswer.onload=()=>{
|
|
|
|
|
+ console.log("缓存优秀答案图片加载完成");
|
|
|
|
|
+ };
|
|
|
|
|
+ window.addEventListener('resize', this.handleResize);
|
|
|
|
|
+ // 添加全局鼠标释放事件监听器,处理异常情况
|
|
|
|
|
+ window.addEventListener('mouseup', this.onGlobalMouseUp);
|
|
|
|
|
+ // 添加全局点击事件监听器,用于隐藏右键菜单
|
|
|
|
|
+ document.addEventListener('click', this.handleGlobalEvent);
|
|
|
|
|
+
|
|
|
|
|
+ // 添加全局右键点击事件监听器,用于隐藏右键菜单
|
|
|
|
|
+ document.addEventListener('contextmenu', this.handleGlobalEvent);
|
|
|
|
|
+ },
|
|
|
|
|
+ beforeDestroy() {
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+ // 移除监听 防止内存泄漏
|
|
|
|
|
+ window.removeEventListener('resize', this.handleResize);
|
|
|
|
|
+ window.removeEventListener('mouseup', this.onGlobalMouseUp);
|
|
|
|
|
+ document.removeEventListener('click', this.handleGlobalEvent);
|
|
|
|
|
+ // 移除全局右键点击事件监听器
|
|
|
|
|
+ document.removeEventListener('contextmenu', this.handleGlobalEvent);
|
|
|
|
|
+ // 移除 paper_container 上的事件监听器
|
|
|
|
|
+ if (this.$refs.paperContainer)
|
|
|
|
|
+ {
|
|
|
|
|
+ this.$refs.paperContainer.removeEventListener('contextmenu', this.handleRightClick);
|
|
|
|
|
+ // this.$refs.paperContainer.removeEventListener('click', this.hideContextMenu);
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+ mounted() {
|
|
|
|
|
+ this.InitData();//初始数据处理加载 如定位 居中 等 第一次居中
|
|
|
|
|
+ // 确保 DOM 已经渲染后再添加事件监听器
|
|
|
|
|
+ this.$nextTick(() => {
|
|
|
|
|
+ if (this.$refs.paperContainer) {
|
|
|
|
|
+ this.$refs.paperContainer.addEventListener('contextmenu', this.handleRightClick);
|
|
|
|
|
+ // this.$refs.paperContainer.addEventListener('click', this.hideContextMenu);
|
|
|
|
|
+ console.log('事件监听器已添加');
|
|
|
|
|
+ } else {
|
|
|
|
|
+ console.error('paperContainer 未找到');
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
|
|
+ },
|
|
|
|
|
+ methods: {
|
|
|
|
|
+ // 处理右键点击事件
|
|
|
|
|
+ handleRightClick(event)
|
|
|
|
|
+ {
|
|
|
|
|
+ event.preventDefault();
|
|
|
|
|
+ event.stopPropagation(); // 阻止事件继续向上冒泡
|
|
|
|
|
+ // 设置菜单位置(相对于 paper_container 的位置)
|
|
|
|
|
+ const containerRect = this.$refs.paperContainer.getBoundingClientRect();
|
|
|
|
|
+ this.contextMenuX = event.clientX - containerRect.left;
|
|
|
|
|
+ this.contextMenuY = event.clientY - containerRect.top;
|
|
|
|
|
+ // 显示自定义菜单
|
|
|
|
|
+ this.showContextMenu = true;
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ // 处理全局事件(包括左键点击和右键点击),用于隐藏菜单
|
|
|
|
|
+ handleGlobalEvent(event) {
|
|
|
|
|
+ // 检查点击的元素是否在菜单内部
|
|
|
|
|
+ const menuElement = document.querySelector('.custom_context_menu');
|
|
|
|
|
+
|
|
|
|
|
+ // 如果菜单是显示的,并且点击的元素不在菜单内部,则隐藏菜单
|
|
|
|
|
+ if (this.showContextMenu && menuElement && !menuElement.contains(event.target)) {
|
|
|
|
|
+ this.hideContextMenu();
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ // 隐藏右键菜单
|
|
|
|
|
+ hideContextMenu()
|
|
|
|
|
+ {
|
|
|
|
|
+ this.showContextMenu = false;
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ // 处理菜单项点击
|
|
|
|
|
+ handleMenuAction(action)
|
|
|
|
|
+ {
|
|
|
|
|
+ console.log('执行操作:', action);
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+ switch(action) {
|
|
|
|
|
+ case 'zoomIn':
|
|
|
|
|
+ // 放大
|
|
|
|
|
+ this.scale = Math.min(this.maxScale, this.scale + 0.1);
|
|
|
|
|
+ this.ImageInfoChange();
|
|
|
|
|
+ this.updateCanvasSize();
|
|
|
|
|
+ this.drawImage();
|
|
|
|
|
+ break;
|
|
|
|
|
+ case 'zoomOut':
|
|
|
|
|
+ // 缩小
|
|
|
|
|
+ // 缩小
|
|
|
|
|
+ this.scale = Math.max(this.minScale, this.scale - 0.1);
|
|
|
|
|
+ this.ImageInfoChange();
|
|
|
|
|
+ this.updateCanvasSize();
|
|
|
|
|
+ this.drawImage();
|
|
|
|
|
+ break;
|
|
|
|
|
+ case 'fitScreen':
|
|
|
|
|
+ //适合屏幕
|
|
|
|
|
+ this.fitScreen()
|
|
|
|
|
+ break;
|
|
|
|
|
+ case 'download':
|
|
|
|
|
+ // 下载图片
|
|
|
|
|
+ this.downloadImage();
|
|
|
|
|
+ break;
|
|
|
|
|
+ }
|
|
|
|
|
+ // 隐藏菜单
|
|
|
|
|
+ this.hideContextMenu();
|
|
|
|
|
+ },
|
|
|
|
|
+ //下载图片功能
|
|
|
|
|
+ downloadImage()
|
|
|
|
|
+ {
|
|
|
|
|
+
|
|
|
|
|
+ this.DownloadDrawImage();
|
|
|
|
|
+ //将canvas 转换为data Url
|
|
|
|
|
+ const imgUrl =this.canvas.toDataURL('image/png');
|
|
|
|
|
+ //创建一个隐藏的a标签
|
|
|
|
|
+ let link = document.createElement('a');
|
|
|
|
|
+ link.href = imgUrl;
|
|
|
|
|
+ link.download = this.downLoadName+'.png';
|
|
|
|
|
+
|
|
|
|
|
+ // link.setAttribute('id', 'downloadImg');
|
|
|
|
|
+ // link.setAttribute('_blank', 'target');
|
|
|
|
|
+ //触发点击事件
|
|
|
|
|
+ document.body.appendChild(link);
|
|
|
|
|
+ link.click()
|
|
|
|
|
+ document.body.removeChild(link);
|
|
|
|
|
+
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ //初始数据加载
|
|
|
|
|
+ InitData()
|
|
|
|
|
+ {
|
|
|
|
|
+ //获取容器的宽高
|
|
|
|
|
+ const { width, height } = this.$refs.paperContainer.getBoundingClientRect();
|
|
|
|
|
+
|
|
|
|
|
+ this.containerHeight = Number(height);
|
|
|
|
|
+ this.containerWidth =Number(width);
|
|
|
|
|
+ console.log("容器宽高",this.containerWidth,this.containerHeight);
|
|
|
|
|
+
|
|
|
|
|
+ //初始化获取试卷数据
|
|
|
|
|
+ console.log("打印paperInfo",this.paperInfo);
|
|
|
|
|
+ if(this.paperInfo?.width && this.paperInfo?.height)
|
|
|
|
|
+ {
|
|
|
|
|
+ //设置初始数据
|
|
|
|
|
+ this.paperImgInfo.width=this.paperInfo.width;//获取宽
|
|
|
|
|
+ this.paperImgInfo.height=this.paperInfo.height;//获取高
|
|
|
|
|
+ // 更新缩放率
|
|
|
|
|
+ this.updateZoomAndPaperInfo();
|
|
|
|
|
+ // 更新画布尺寸
|
|
|
|
|
+ this.updateCanvasSize();
|
|
|
|
|
+ console.log("是否初始",this.isInit);
|
|
|
|
|
+ //计算中心位置使图片居中 初始加载图片是居中
|
|
|
|
|
+ if(this.isInit)
|
|
|
|
|
+ {
|
|
|
|
|
+ this.centerCanvas();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+ //加载图片
|
|
|
|
|
+ this.loadImage();
|
|
|
|
|
+ }
|
|
|
|
|
+ else
|
|
|
|
|
+ {
|
|
|
|
|
+ //没有值 默认图片的宽高
|
|
|
|
|
+ console.log("没有纸张试卷的宽高信息");
|
|
|
|
|
+ this.image = new Image();
|
|
|
|
|
+ this.image.crossOrigin = 'anonymous'; // 处理跨域问题
|
|
|
|
|
+ this.image.src = this.paperImgUrl;
|
|
|
|
|
+ this.image.onload = () => {
|
|
|
|
|
+ console.log("图片加载完成",this.image);
|
|
|
|
|
+ this.paperImgInfo.width=this.image.width;//获取宽
|
|
|
|
|
+ this.paperImgInfo.height=this.image.height;//获取高
|
|
|
|
|
+
|
|
|
|
|
+ // 更新缩放率
|
|
|
|
|
+ this.updateZoomAndPaperInfo();
|
|
|
|
|
+ // 更新画布尺寸
|
|
|
|
|
+ this.updateCanvasSize();
|
|
|
|
|
+ //计算中心位置使图片居中
|
|
|
|
|
+ console.log("是否初始",this.isInit);
|
|
|
|
|
+ //计算中心位置使图片居中 初始加载图片是居中
|
|
|
|
|
+ // if(this.isInit)
|
|
|
|
|
+ // {
|
|
|
|
|
+ this.centerCanvas();
|
|
|
|
|
+ // }
|
|
|
|
|
+ //加载图片
|
|
|
|
|
+ this.loadImage();
|
|
|
|
|
+ // this.drawQuestionPosition();
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ //切换试卷图片
|
|
|
|
|
+ chanagePaperImage()
|
|
|
|
|
+ {
|
|
|
|
|
+ // 更新缩放率
|
|
|
|
|
+ this.updateZoomAndPaperInfo();
|
|
|
|
|
+ // 更新画布尺寸
|
|
|
|
|
+ this.updateCanvasSize();
|
|
|
|
|
+ // //计算中心位置使图片居中
|
|
|
|
|
+ // this.centerCanvas();
|
|
|
|
|
+ //加载图片
|
|
|
|
|
+ this.loadImage();
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ //适合屏幕
|
|
|
|
|
+ fitScreen()
|
|
|
|
|
+ {
|
|
|
|
|
+
|
|
|
|
|
+ this.scale=1;
|
|
|
|
|
+ this.isInit=true;
|
|
|
|
|
+ this.InitData();//初始化屏幕加载
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+ // 加载图片
|
|
|
|
|
+ loadImage() {
|
|
|
|
|
+ // this.image = new Image();
|
|
|
|
|
+ // this.image.src = this.paperImgUrl;
|
|
|
|
|
+ // this.image.onload = () => {
|
|
|
|
|
+ // console.log("图片加载完成",this.image);
|
|
|
|
|
+
|
|
|
|
|
+ this.drawImage();//绘制边框数据
|
|
|
|
|
+
|
|
|
|
|
+ // this.drawQuestionPosition();
|
|
|
|
|
+ // };
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+ // 图片宽高坐标变化
|
|
|
|
|
+ ImageInfoChange(){
|
|
|
|
|
+ let {width,height} = this.paperImgInfo;
|
|
|
|
|
+ // console.log("打印纸张试卷的图片宽高",this.paperImgInfo);
|
|
|
|
|
+ let imgDom = document.getElementById(`imgContainer${this.imageIndex}`);
|
|
|
|
|
+ imgDom.style.width = width * this.zoomRate*this.scale + 'px';
|
|
|
|
|
+ imgDom.style.height = height * this.zoomRate*this.scale + 'px';
|
|
|
|
|
+ imgDom.style.left = this.position.x + 'px';
|
|
|
|
|
+ imgDom.style.top = this.position.y + 'px';
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ //图片加载完成后绘制图片
|
|
|
|
|
+ drawImage() {
|
|
|
|
|
+ // if (this.image) {
|
|
|
|
|
+ this.canvas=this.$refs.paperCanvas;
|
|
|
|
|
+ let ctx = this.canvas.getContext('2d');
|
|
|
|
|
+ // ctx.imageSmoothingEnabled = false;//禁用图片平滑
|
|
|
|
|
+ // this.paperCtx.drawImage(this.image, 0, 0,this.canvasInfo.width,this.canvasInfo.height);
|
|
|
|
|
+ // 根据 devicePixelRatio 缩放图片
|
|
|
|
|
+ // ctx.drawImage(
|
|
|
|
|
+ // this.image,
|
|
|
|
|
+ // 0, 0,
|
|
|
|
|
+ // this.image.width, this.image.height,
|
|
|
|
|
+ // 0, 0,
|
|
|
|
|
+ // this.canvasInfo.width, this.canvasInfo.height
|
|
|
|
|
+ // );
|
|
|
|
|
+ ctx.clearRect(0, 0, this.canvasInfo.width, this.canvasInfo.height);//清除边框数据
|
|
|
|
|
+
|
|
|
|
|
+ this.ImageInfoChange();//用image图片替代背景 是为了高清显示
|
|
|
|
|
+
|
|
|
|
|
+ // console.log("加载边框数据",this.drawData);
|
|
|
|
|
+ for(var i=0;i<this.drawData.length;i++)
|
|
|
|
|
+ {
|
|
|
|
|
+ let item=this.drawData[i];
|
|
|
|
|
+ const point=JSON.parse(item.samplingPosition);
|
|
|
|
|
+ var obj={
|
|
|
|
|
+ x:point.x*this.zoomRate*this.scale,
|
|
|
|
|
+ y:point.y*this.zoomRate*this.scale,
|
|
|
|
|
+ }
|
|
|
|
|
+ //如果是批阅块的坐标 需要相加
|
|
|
|
|
+ if(item.pagePaintingVOS)
|
|
|
|
|
+ {
|
|
|
|
|
+ console.log("打印item",item);
|
|
|
|
|
+ // let pageItem = item.pagePaintingVOS.find(item => item.page == point.page);
|
|
|
|
|
+ const pointIndex=point.index || 0;//默认0
|
|
|
|
|
+ let pageItem = item.pagePaintingVOS[pointIndex];
|
|
|
|
|
+ // item.pagePaintingVOS.forEach((item,index) => {
|
|
|
|
|
+ // if(index==point.index && item.page == point.page)
|
|
|
|
|
+ // {
|
|
|
|
|
+ // pageItem=item;
|
|
|
|
|
+ // }
|
|
|
|
|
+ // });
|
|
|
|
|
+ //需要同时根据索引和页面查找
|
|
|
|
|
+ // const pageItem = item.pagePaintingVOS.find(item => item.page == point.page && item.index == point.index);
|
|
|
|
|
+ console.log("打印pageItem",pageItem);
|
|
|
|
|
+
|
|
|
|
|
+ if (pageItem) {
|
|
|
|
|
+ obj.x = (pageItem.x + point.x) * this.zoomRate * this.scale;
|
|
|
|
|
+ obj.y = (pageItem.y + point.y) * this.zoomRate * this.scale;
|
|
|
|
|
+ }
|
|
|
|
|
+ console.log("打印计算后的obj",obj);
|
|
|
|
|
+ }
|
|
|
|
|
+ // 绘制文字 定位去和客观题组不用显示
|
|
|
|
|
+
|
|
|
|
|
+ // console.log("打印标题",item.questionName);
|
|
|
|
|
+ // ctx.fillStyle = 'red';
|
|
|
|
|
+ ctx.fillStyle = '#D81E06';
|
|
|
|
|
+ ctx.textAlign = 'center';
|
|
|
|
|
+ ctx.textBaseline = 'middle';
|
|
|
|
|
+ if(item.questionName=='总分')
|
|
|
|
|
+ {
|
|
|
|
|
+ const fontSize = Math.max(12, 30 * this.scale); // 最小字体12px,基础字体16px
|
|
|
|
|
+ ctx.font = `${fontSize}px Arial`; // 让文字大小跟随缩放
|
|
|
|
|
+
|
|
|
|
|
+ ctx.textAlign = 'left';
|
|
|
|
|
+ }
|
|
|
|
|
+ else
|
|
|
|
|
+ {
|
|
|
|
|
+ const fontSize = Math.max(12, 20 * this.scale); // 最小字体12px,基础字体16px
|
|
|
|
|
+ // ctx.font = '30px Arial';
|
|
|
|
|
+
|
|
|
|
|
+ ctx.font = `${fontSize}px Arial`; // 让文字大小跟随缩放
|
|
|
|
|
+
|
|
|
|
|
+ ctx.textAlign = 'left';
|
|
|
|
|
+ //判断是什么类型 scoreType 分数类型1:全对 2:半对 3: 全错
|
|
|
|
|
+ const iconX=obj.x-20*this.zoomRate*this.scale;
|
|
|
|
|
+ const iconY=obj.y-20*this.zoomRate*this.scale;
|
|
|
|
|
+ const iconWidth = 50*this.zoomRate*this.scale;
|
|
|
|
|
+ const iconHeight = 50*this.zoomRate*this.scale;
|
|
|
|
|
+ // console.log("打印item.score",item.score)
|
|
|
|
|
+ if(item.score==0)
|
|
|
|
|
+ {
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+ //0 分全错
|
|
|
|
|
+ ctx.drawImage(this.CacheAllWrong, iconX, iconY, iconWidth, iconHeight);
|
|
|
|
|
+
|
|
|
|
|
+ }
|
|
|
|
|
+ else if(item.score==item.fullScore)
|
|
|
|
|
+ {
|
|
|
|
|
+ //满分 全对
|
|
|
|
|
+ ctx.drawImage(this.CacheAllRight, iconX, iconY, iconWidth, iconHeight);
|
|
|
|
|
+ }
|
|
|
|
|
+ else
|
|
|
|
|
+ {
|
|
|
|
|
+ //半对
|
|
|
|
|
+ ctx.drawImage(this.CacheHalfRight, iconX, iconY, iconWidth, iconHeight);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ ctx.fillText(item.score,obj.x+35*this.zoomRate*this.scale,obj.y);
|
|
|
|
|
+
|
|
|
|
|
+ // ctx.fillStyle = 'blue';
|
|
|
|
|
+ // ctx.font = `24px Arial`; // 让文字大小跟随缩放
|
|
|
|
|
+ // ctx.textAlign = 'center';
|
|
|
|
|
+ // ctx.textBaseline = 'middle';
|
|
|
|
|
+ // ctx.fillText(item.questionName,obj.x-65*this.zoomRate*this.scale,obj.y);
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+ }
|
|
|
|
|
+ // ctx.restore();
|
|
|
|
|
+ // }
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ //下载图片绘制
|
|
|
|
|
+ DownloadDrawImage()
|
|
|
|
|
+ {
|
|
|
|
|
+ this.canvas=this.$refs.paperCanvas;
|
|
|
|
|
+ let ctx = this.canvas.getContext('2d');
|
|
|
|
|
+
|
|
|
|
|
+ ctx.clearRect(0, 0, this.canvasInfo.width, this.canvasInfo.height);//清除边框数据
|
|
|
|
|
+ ctx.drawImage(this.image, 0, 0,this.canvasInfo.width,this.canvasInfo.height);
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+ // console.log("加载边框数据",this.drawData);
|
|
|
|
|
+ for(var i=0;i<this.drawData.length;i++)
|
|
|
|
|
+ {
|
|
|
|
|
+ let item=this.drawData[i];
|
|
|
|
|
+ const point=JSON.parse(item.samplingPosition);
|
|
|
|
|
+ var obj={
|
|
|
|
|
+ x:point.x*this.zoomRate*this.scale,
|
|
|
|
|
+ y:point.y*this.zoomRate*this.scale,
|
|
|
|
|
+ }
|
|
|
|
|
+ //如果是批阅块的坐标 需要相加
|
|
|
|
|
+ if(item.pagePaintingVOS)
|
|
|
|
|
+ {
|
|
|
|
|
+ console.log("打印item",item);
|
|
|
|
|
+ // let pageItem = item.pagePaintingVOS.find(item => item.page == point.page);
|
|
|
|
|
+ const pointIndex=point.index || 0;//默认0
|
|
|
|
|
+ let pageItem = item.pagePaintingVOS[pointIndex];
|
|
|
|
|
+ // item.pagePaintingVOS.forEach((item,index) => {
|
|
|
|
|
+ // if(index==point.index && item.page == point.page)
|
|
|
|
|
+ // {
|
|
|
|
|
+ // pageItem=item;
|
|
|
|
|
+ // }
|
|
|
|
|
+ // });
|
|
|
|
|
+ //需要同时根据索引和页面查找
|
|
|
|
|
+ // const pageItem = item.pagePaintingVOS.find(item => item.page == point.page && item.index == point.index);
|
|
|
|
|
+ console.log("打印pageItem",pageItem);
|
|
|
|
|
+
|
|
|
|
|
+ if (pageItem) {
|
|
|
|
|
+ obj.x = (pageItem.x + point.x) * this.zoomRate * this.scale;
|
|
|
|
|
+ obj.y = (pageItem.y + point.y) * this.zoomRate * this.scale;
|
|
|
|
|
+ }
|
|
|
|
|
+ console.log("打印计算后的obj",obj);
|
|
|
|
|
+ }
|
|
|
|
|
+ // 绘制文字 定位去和客观题组不用显示
|
|
|
|
|
+
|
|
|
|
|
+ // console.log("打印标题",item.questionName);
|
|
|
|
|
+ // ctx.fillStyle = 'red';
|
|
|
|
|
+ ctx.fillStyle = '#D81E06';
|
|
|
|
|
+ ctx.textAlign = 'center';
|
|
|
|
|
+ ctx.textBaseline = 'middle';
|
|
|
|
|
+ if(item.questionName=='总分')
|
|
|
|
|
+ {
|
|
|
|
|
+ const fontSize = Math.max(12, 50 * this.scale); // 最小字体12px,基础字体16px
|
|
|
|
|
+ ctx.font = `${fontSize}px Arial`; // 让文字大小跟随缩放
|
|
|
|
|
+
|
|
|
|
|
+ ctx.textAlign = 'left';
|
|
|
|
|
+ }
|
|
|
|
|
+ else
|
|
|
|
|
+ {
|
|
|
|
|
+ const fontSize = Math.max(12, 24 * this.scale); // 最小字体12px,基础字体16px
|
|
|
|
|
+ // ctx.font = '30px Arial';
|
|
|
|
|
+
|
|
|
|
|
+ ctx.font = `${fontSize}px Arial`; // 让文字大小跟随缩放
|
|
|
|
|
+
|
|
|
|
|
+ ctx.textAlign = 'left';
|
|
|
|
|
+ //判断是什么类型 scoreType 分数类型1:全对 2:半对 3: 全错
|
|
|
|
|
+ const iconX=obj.x-20*this.zoomRate*this.scale;
|
|
|
|
|
+ const iconY=obj.y-20*this.zoomRate*this.scale;
|
|
|
|
|
+ const iconWidth = 50*this.zoomRate*this.scale;
|
|
|
|
|
+ const iconHeight = 50*this.zoomRate*this.scale;
|
|
|
|
|
+ // console.log("打印item.score",item.score)
|
|
|
|
|
+ if(item.score==0)
|
|
|
|
|
+ {
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+ //0 分全错
|
|
|
|
|
+ ctx.drawImage(this.CacheAllWrong, iconX, iconY, iconWidth, iconHeight);
|
|
|
|
|
+
|
|
|
|
|
+ }
|
|
|
|
|
+ else if(item.score==item.fullScore)
|
|
|
|
|
+ {
|
|
|
|
|
+ //满分 全对
|
|
|
|
|
+ ctx.drawImage(this.CacheAllRight, iconX, iconY, iconWidth, iconHeight);
|
|
|
|
|
+ }
|
|
|
|
|
+ else
|
|
|
|
|
+ {
|
|
|
|
|
+ //半对
|
|
|
|
|
+ ctx.drawImage(this.CacheHalfRight, iconX, iconY, iconWidth, iconHeight);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ ctx.fillText(item.score,obj.x+35*this.zoomRate*this.scale,obj.y);
|
|
|
|
|
+
|
|
|
|
|
+ // ctx.fillStyle = 'blue';
|
|
|
|
|
+ // ctx.font = `24px Arial`; // 让文字大小跟随缩放
|
|
|
|
|
+ // ctx.textAlign = 'center';
|
|
|
|
|
+ // ctx.textBaseline = 'middle';
|
|
|
|
|
+ // ctx.fillText(item.questionName,obj.x-65*this.zoomRate*this.scale,obj.y);
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ //加载答案框数据
|
|
|
|
|
+ drawQuestionPosition()
|
|
|
|
|
+ {
|
|
|
|
|
+
|
|
|
|
|
+ let canvas=this.$refs.paperCanvas;
|
|
|
|
|
+ let ctx = canvas.getContext('2d');
|
|
|
|
|
+ console.log("加载答案边框数据",this.questionPositionData);
|
|
|
|
|
+ for(var i=0;i<this.questionPositionData.length;i++)
|
|
|
|
|
+ {
|
|
|
|
|
+ var item=this.questionPositionData[i];
|
|
|
|
|
+
|
|
|
|
|
+ // var obj={
|
|
|
|
|
+ // x:item.x*this.zoomRate*this.scale,
|
|
|
|
|
+ // y:item.y*this.zoomRate*this.scale,
|
|
|
|
|
+ // width:item.w*this.zoomRate*this.scale,
|
|
|
|
|
+ // height:item.h*this.zoomRate*this.scale,
|
|
|
|
|
+ // blockName:item.blockName,
|
|
|
|
|
+ // // page:'',
|
|
|
|
|
+ // // blockArea:'',
|
|
|
|
|
+ // }
|
|
|
|
|
+ // //外边框数据
|
|
|
|
|
+ // if(item.unit=='mm')
|
|
|
|
|
+ // {
|
|
|
|
|
+ // obj={
|
|
|
|
|
+ // x:mmToPx(item.x)*this.zoomRate*this.scale,
|
|
|
|
|
+ // y:mmToPx(item.y)*this.zoomRate*this.scale,
|
|
|
|
|
+ // width:mmToPx(item.w)*this.zoomRate*this.scale,
|
|
|
|
|
+ // height:mmToPx(item.h)*this.zoomRate*this.scale,
|
|
|
|
|
+ // blockName:item.blockName,
|
|
|
|
|
+ // // page:item.page,
|
|
|
|
|
+ // // blockArea:item.blockArea,
|
|
|
|
|
+ // }
|
|
|
|
|
+ // }
|
|
|
|
|
+
|
|
|
|
|
+ for(var j=0;j<item.questionlist.length;j++)
|
|
|
|
|
+ {
|
|
|
|
|
+ for(var k=0;k<item.questionlist[j].answerList.length;k++)
|
|
|
|
|
+ {
|
|
|
|
|
+ var obj={
|
|
|
|
|
+ x:mmToPx(item.questionlist[j].answerList[k].x)*this.zoomRate*this.scale,
|
|
|
|
|
+ y:mmToPx(item.questionlist[j].answerList[k].y)*this.zoomRate*this.scale,
|
|
|
|
|
+ width:mmToPx(item.questionlist[j].answerList[k].w)*this.zoomRate*this.scale,
|
|
|
|
|
+ height:mmToPx(item.questionlist[j].answerList[k].h)*this.zoomRate*this.scale,
|
|
|
|
|
+ // blockName:item.blockName,
|
|
|
|
|
+ }
|
|
|
|
|
+ ctx.strokeRect(obj.x,obj.y,obj.width,obj.height);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // ctx.font = '15px Arial';
|
|
|
|
|
+
|
|
|
|
|
+ // ctx.textAlign = 'left';
|
|
|
|
|
+ // // ctx.textBaseline = 'middle';
|
|
|
|
|
+ // // 绘制文字
|
|
|
|
|
+ // if(obj.blockArea!=2)
|
|
|
|
|
+ // {
|
|
|
|
|
+ // ctx.fillText(obj.blockName,obj.x, obj.y+15);
|
|
|
|
|
+ // }
|
|
|
|
|
+
|
|
|
|
|
+ }
|
|
|
|
|
+ ctx.restore();
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ // 更新缩放率
|
|
|
|
|
+ updateZoomAndPaperInfo() {
|
|
|
|
|
+ // this.zoomRate = this.containerHeight / this.paperImgInfo.height;//
|
|
|
|
|
+ // 计算基于宽度和高度的缩放率
|
|
|
|
|
+ const widthZoomRate = this.containerWidth / this.paperImgInfo.width;
|
|
|
|
|
+ const heightZoomRate = this.containerHeight / this.paperImgInfo.height;
|
|
|
|
|
+
|
|
|
|
|
+ // 选择较小的缩放率作为基准,确保图像完整显示在容器内
|
|
|
|
|
+ this.zoomRate = Math.min(widthZoomRate, heightZoomRate);
|
|
|
|
|
+
|
|
|
|
|
+ // this.paperImgInfo = this.cardType ==1 ? { width: 1240, height: 1754 } : { width: 2480, height: 1754 };
|
|
|
|
|
+ // console.log("打印this.paperImgInfo",this.paperImgInfo);
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ // 更新画布尺寸
|
|
|
|
|
+ updateCanvasSize()
|
|
|
|
|
+ {
|
|
|
|
|
+ this.canvasInfo.width = Math.round(this.paperImgInfo.width * this.zoomRate * this.scale);
|
|
|
|
|
+ this.canvasInfo.height = Math.round(this.paperImgInfo.height * this.zoomRate * this.scale);
|
|
|
|
|
+ this.$refs.paperCanvas.width = this.canvasInfo.width;
|
|
|
|
|
+ this.$refs.paperCanvas.height = this.canvasInfo.height;
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ // 中心化画布
|
|
|
|
|
+ centerCanvas()
|
|
|
|
|
+ {
|
|
|
|
|
+ console.log("开始居中画布",this.containerWidth,this.containerHeight);
|
|
|
|
|
+ console.log("画布尺寸",this.canvasInfo.width,this.canvasInfo.height);
|
|
|
|
|
+ this.position.x = (this.containerWidth - this.canvasInfo.width) / 2;
|
|
|
|
|
+ this.position.y = (this.containerHeight - this.canvasInfo.height) / 2;
|
|
|
|
|
+ this.$refs.paperCanvas.style.left = `${this.position.x}px`;
|
|
|
|
|
+ this.$refs.paperCanvas.style.top = `${this.position.y}px`;
|
|
|
|
|
+ console.log("中心化画布打印画布位置",this.position.x,this.position.y);
|
|
|
|
|
+ this.isInit=false;//后面不在执行居中操作
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ // 全局鼠标释放事件处理
|
|
|
|
|
+ onGlobalMouseUp() {
|
|
|
|
|
+ this.isDragging = false;
|
|
|
|
|
+ },
|
|
|
|
|
+ // 鼠标按下事件
|
|
|
|
|
+ onMouseDown(event) {
|
|
|
|
|
+ // 只响应左键点击(button值为0表示左键)
|
|
|
|
|
+ if (event.button !== 0) {
|
|
|
|
|
+ // 如果是右键,确保释放拖拽状态
|
|
|
|
|
+
|
|
|
|
|
+ this.isDragging = false;
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ console.log("鼠标按下事件",event);
|
|
|
|
|
+ // 确保在开始新的拖拽前,先重置拖拽状态
|
|
|
|
|
+ this.isDragging = false;
|
|
|
|
|
+ console.log("鼠标按下事件drawType",this.drawType);
|
|
|
|
|
+ if(this.drawType==0)
|
|
|
|
|
+ {
|
|
|
|
|
+ // 如果drawType==0,则切换到拖拽模式
|
|
|
|
|
+ console.log("打印是否可拖拽",this.isDrag);
|
|
|
|
|
+ if(this.isDrag)
|
|
|
|
|
+ {
|
|
|
|
|
+ console.log("开始拖拽");
|
|
|
|
|
+ console.log("打印开始拖拽的坐标",this.startX,this.startY);
|
|
|
|
|
+ console.log("打印鼠标位置",event.clientX,event.clientY);
|
|
|
|
|
+ console.log("打印画布位置",this.position.x,this.position.y);
|
|
|
|
|
+ //可拖动的时候才能拖动
|
|
|
|
|
+ this.isDragging = true;
|
|
|
|
|
+ this.startX = event.clientX - this.position.x;
|
|
|
|
|
+ this.startY = event.clientY - this.position.y;
|
|
|
|
|
+ console.log("打印开始拖拽的坐标",this.startX,this.startY);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+ }
|
|
|
|
|
+ if(this.drawType==1)
|
|
|
|
|
+ {
|
|
|
|
|
+ if(event.target.id!='paperCanvas')
|
|
|
|
|
+ {
|
|
|
|
|
+ this.isDragging = true;
|
|
|
|
|
+ this.startX = event.clientX - this.position.x;
|
|
|
|
|
+ this.startY = event.clientY - this.position.y;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ // 鼠标移动事件
|
|
|
|
|
+ onMouseMove(event)
|
|
|
|
|
+ {
|
|
|
|
|
+ if (this.isDragging)
|
|
|
|
|
+ {
|
|
|
|
|
+ // 在拖拽模式下,移动画布
|
|
|
|
|
+ this.position.x = event.clientX - this.startX;
|
|
|
|
|
+ this.position.y = event.clientY - this.startY;
|
|
|
|
|
+ this.$refs.paperCanvas.style.left = `${this.position.x}px`;
|
|
|
|
|
+ this.$refs.paperCanvas.style.top = `${this.position.y}px`;
|
|
|
|
|
+ this.ImageInfoChange();
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ // 鼠标抬起事件
|
|
|
|
|
+ onMouseUp(event) {
|
|
|
|
|
+ // 只响应左键释放
|
|
|
|
|
+ if (event && event.button !== 0) {
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ this.isDragging = false; // 停止拖拽
|
|
|
|
|
+
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ // // 鼠标滚轮事件
|
|
|
|
|
+ // onWheel(event) {
|
|
|
|
|
+ // event.preventDefault();
|
|
|
|
|
+ // const delta = event.deltaY < 0 ? 1 : -1;
|
|
|
|
|
+ // const newScale = this.scale + delta * 0.1;
|
|
|
|
|
+ // this.scale = Math.max(this.minScale, Math.min(this.maxScale, newScale));
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+ // this.ImageInfoChange();
|
|
|
|
|
+ // this.updateCanvasSize();
|
|
|
|
|
+ // this.drawImage();
|
|
|
|
|
+
|
|
|
|
|
+ // },
|
|
|
|
|
+
|
|
|
|
|
+ // 鼠标滚轮事件
|
|
|
|
|
+ onWheel(event) {
|
|
|
|
|
+ event.preventDefault();
|
|
|
|
|
+ // 计算新的缩放比例
|
|
|
|
|
+ const delta = event.deltaY < 0 ? 1 : -1;
|
|
|
|
|
+ const newScale = this.scale + delta * 0.1;
|
|
|
|
|
+ const clampedScale = Math.max(this.minScale, Math.min(this.maxScale, newScale));
|
|
|
|
|
+
|
|
|
|
|
+ // 如果缩放值没有变化,则直接返回
|
|
|
|
|
+ if (clampedScale === this.scale) return;
|
|
|
|
|
+
|
|
|
|
|
+ // 获取鼠标在容器中的位置
|
|
|
|
|
+ const containerRect = this.$refs.paperContainer.getBoundingClientRect();
|
|
|
|
|
+ const mouseX = event.clientX - containerRect.left;
|
|
|
|
|
+ const mouseY = event.clientY - containerRect.top;
|
|
|
|
|
+
|
|
|
|
|
+ // 计算鼠标相对于当前画布的位置
|
|
|
|
|
+ const mouseRelativeToCanvasX = (mouseX - this.position.x) / this.scale;
|
|
|
|
|
+ const mouseRelativeToCanvasY = (mouseY - this.position.y) / this.scale;
|
|
|
|
|
+
|
|
|
|
|
+ // 更新缩放比例
|
|
|
|
|
+ this.scale = clampedScale;
|
|
|
|
|
+
|
|
|
|
|
+ // 更新画布尺寸
|
|
|
|
|
+ this.updateCanvasSize();
|
|
|
|
|
+
|
|
|
|
|
+ // 重新计算画布位置,使缩放围绕鼠标点进行
|
|
|
|
|
+ this.position.x = mouseX - mouseRelativeToCanvasX * this.scale;
|
|
|
|
|
+ this.position.y = mouseY - mouseRelativeToCanvasY * this.scale;
|
|
|
|
|
+
|
|
|
|
|
+ // 应用新的位置
|
|
|
|
|
+ this.$refs.paperCanvas.style.left = `${this.position.x}px`;
|
|
|
|
|
+ this.$refs.paperCanvas.style.top = `${this.position.y}px`;
|
|
|
|
|
+
|
|
|
|
|
+ // 更新图片位置信息
|
|
|
|
|
+ this.ImageInfoChange();
|
|
|
|
|
+
|
|
|
|
|
+ // 重新绘制内容
|
|
|
|
|
+ this.drawImage();
|
|
|
|
|
+
|
|
|
|
|
+ },
|
|
|
|
|
+ //鼠标复位
|
|
|
|
|
+ MouseReset()
|
|
|
|
|
+ {
|
|
|
|
|
+ this.drawType=0;
|
|
|
|
|
+ this.canvas=this.$refs.paperCanvas;
|
|
|
|
|
+ this.canvas.style.cursor="pointer";
|
|
|
|
|
+ this.canvas.onmousedown=null;
|
|
|
|
|
+ this.canvas.onmousemove=null;
|
|
|
|
|
+ this.canvas.onmouseup=null;
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ //框选答案区域
|
|
|
|
|
+ SelectionBox()
|
|
|
|
|
+ {
|
|
|
|
|
+ console.log("开始框选答案区域");
|
|
|
|
|
+ this.drawType=1;
|
|
|
|
|
+
|
|
|
|
|
+ this.canvas=this.$refs.paperCanvas;
|
|
|
|
|
+ this.canvas.style.cursor="crosshair";
|
|
|
|
|
+ this.canvas.onmousedown=this.onCanvasDown;
|
|
|
|
|
+ this.canvas.onmousemove=this.onCanvasMove;
|
|
|
|
|
+ this.canvas.onmouseup=this.onCanvasUp;
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+ //画圈模式
|
|
|
|
|
+ PaintingCircle()
|
|
|
|
|
+ {
|
|
|
|
|
+
|
|
|
|
|
+ console.log("进入画圈模式");
|
|
|
|
|
+ this.drawType=1;
|
|
|
|
|
+
|
|
|
|
|
+ this.canvas=this.$refs.paperCanvas;
|
|
|
|
|
+ this.canvas.style.cursor="crosshair";
|
|
|
|
|
+ this.canvas.onmousedown=this.onCanvasDown;
|
|
|
|
|
+ this.canvas.onmousemove=this.onCanvasMove;
|
|
|
|
|
+ this.canvas.onmouseup=this.onCanvasUp;
|
|
|
|
|
+
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ //开始画答案区域圈
|
|
|
|
|
+ StartPaintingCircle(obj)
|
|
|
|
|
+ {
|
|
|
|
|
+ this.addObjectAreaOption=obj;
|
|
|
|
|
+ this.drawType=1;
|
|
|
|
|
+ this.canvas=this.$refs.paperCanvas;
|
|
|
|
|
+ this.canvas.style.cursor="crosshair";
|
|
|
|
|
+ this.canvas.onmousedown=this.onCanvasDown;
|
|
|
|
|
+ this.canvas.onmousemove=this.onCanvasMove;
|
|
|
|
|
+ this.canvas.onmouseup=this.onCanvasUp;
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ //canvas上按下事件
|
|
|
|
|
+ onCanvasDown(e)
|
|
|
|
|
+ {
|
|
|
|
|
+ this.canvas.style.cursor="crosshair";
|
|
|
|
|
+ this.isDrawing=true;
|
|
|
|
|
+ this.isDragging = false; // 禁止拖拽
|
|
|
|
|
+ this.rectPoint={
|
|
|
|
|
+ startX:e.offsetX,
|
|
|
|
|
+ startY:e.offsetY,
|
|
|
|
|
+ endX:e.offsetX,
|
|
|
|
|
+ endY:e.offsetY
|
|
|
|
|
+ };
|
|
|
|
|
+ },
|
|
|
|
|
+ //canvas上移动事件
|
|
|
|
|
+ onCanvasMove(e)
|
|
|
|
|
+ {
|
|
|
|
|
+ // this.drawType=1;
|
|
|
|
|
+ // this.isDragging=true;
|
|
|
|
|
+
|
|
|
|
|
+ if(this.isDrawing)
|
|
|
|
|
+ {
|
|
|
|
|
+ // const canvas = this.$refs.paperCanvas;
|
|
|
|
|
+ // const ctx = canvas.getContext('2d');
|
|
|
|
|
+ // // 计算矩形的宽度和高度
|
|
|
|
|
+ // const width = e.offsetX - this.rectPoint.startX;
|
|
|
|
|
+ // const height = e.offsetY - this.rectPoint.startY;
|
|
|
|
|
+ // this.rectPoint.endX=e.offsetX;
|
|
|
|
|
+ // this.rectPoint.endY=e.offsetY;
|
|
|
|
|
+ // // 清除之前的矩形
|
|
|
|
|
+ // ctx.clearRect(0, 0, canvas.width, canvas.height);
|
|
|
|
|
+ // // 重新绘制之前的边框数据
|
|
|
|
|
+ // this.drawImage();
|
|
|
|
|
+ // // 设置矩形样式
|
|
|
|
|
+ // ctx.strokeStyle = 'blue';
|
|
|
|
|
+ // ctx.lineWidth = 1;//画框的线的粗细
|
|
|
|
|
+ // // 绘制矩形
|
|
|
|
|
+ // ctx.strokeRect(this.rectPoint.startX, this.rectPoint.startY, width, height);
|
|
|
|
|
+
|
|
|
|
|
+ const canvas = this.$refs.paperCanvas;
|
|
|
|
|
+ const ctx = canvas.getContext('2d');
|
|
|
|
|
+ // 更新当前鼠标位置
|
|
|
|
|
+ this.rectPoint.endX = e.offsetX;
|
|
|
|
|
+ this.rectPoint.endY = e.offsetY;
|
|
|
|
|
+
|
|
|
|
|
+ // 清除之前的矩形
|
|
|
|
|
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
|
|
|
|
|
+ // 重新绘制之前的边框数据
|
|
|
|
|
+ this.drawImage();
|
|
|
|
|
+ // 设置矩形样式
|
|
|
|
|
+ ctx.strokeStyle = 'blue';
|
|
|
|
|
+ ctx.lineWidth = 1;//画框的线的粗细
|
|
|
|
|
+
|
|
|
|
|
+ // 计算矩形的起点和宽高,处理反向框选
|
|
|
|
|
+ const startX = this.rectPoint.startX;
|
|
|
|
|
+ const startY = this.rectPoint.startY;
|
|
|
|
|
+ const width = e.offsetX - startX;
|
|
|
|
|
+ const height = e.offsetY - startY;
|
|
|
|
|
+
|
|
|
|
|
+ // 绘制矩形(可以处理负宽度和高度)
|
|
|
|
|
+ ctx.strokeRect(startX, startY, width, height);
|
|
|
|
|
+
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+ //canvas抬起事件
|
|
|
|
|
+ onCanvasUp()
|
|
|
|
|
+ {
|
|
|
|
|
+ this.isDrawing = false;
|
|
|
|
|
+ // console.log("鼠标抬起事件,画圈完成,打印四角坐标",this.rectPoint);
|
|
|
|
|
+
|
|
|
|
|
+ // let point={
|
|
|
|
|
+ // x:this.$global.floatNum((this.rectPoint.startX)/this.zoomRate/this.scale),
|
|
|
|
|
+ // y:this.$global.floatNum(this.rectPoint.startY/this.zoomRate/this.scale),
|
|
|
|
|
+ // w:this.$global.floatNum((this.rectPoint.endX-this.rectPoint.startX)/this.zoomRate/this.scale),
|
|
|
|
|
+ // h:this.$global.floatNum((this.rectPoint.endY-this.rectPoint.startY)/this.zoomRate/this.scale),
|
|
|
|
|
+ // unit:'px',
|
|
|
|
|
+ // };//坐标还原成px单位
|
|
|
|
|
+ // this.currenPoint=point;
|
|
|
|
|
+ // console.log("打印当前坐标",point);
|
|
|
|
|
+ // if(point.w>0 && point.h>0)
|
|
|
|
|
+ // {
|
|
|
|
|
+ // console.log("打印当前坐标",point);
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+ // this.$emit("GetRectPoint",point);
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+ // }
|
|
|
|
|
+
|
|
|
|
|
+ // 处理反向框选,确保宽度和高度为正数
|
|
|
|
|
+ let startX = this.rectPoint.startX;
|
|
|
|
|
+ let startY = this.rectPoint.startY;
|
|
|
|
|
+ let endX = this.rectPoint.endX;
|
|
|
|
|
+ let endY = this.rectPoint.endY;
|
|
|
|
|
+
|
|
|
|
|
+ // 确保起点坐标是左上角,终点坐标是右下角
|
|
|
|
|
+ let actualStartX = Math.min(startX, endX);
|
|
|
|
|
+ let actualStartY = Math.min(startY, endY);
|
|
|
|
|
+ let actualEndX = Math.max(startX, endX);
|
|
|
|
|
+ let actualEndY = Math.max(startY, endY);
|
|
|
|
|
+
|
|
|
|
|
+ // 计算实际的宽度和高度(确保为正数)
|
|
|
|
|
+ let width = Math.abs(actualEndX - actualStartX);
|
|
|
|
|
+ let height = Math.abs(actualEndY - actualStartY);
|
|
|
|
|
+
|
|
|
|
|
+ let point = {
|
|
|
|
|
+ x: this.$global.floatNum(actualStartX / this.zoomRate / this.scale),
|
|
|
|
|
+ y: this.$global.floatNum(actualStartY / this.zoomRate / this.scale),
|
|
|
|
|
+ w: this.$global.floatNum(width / this.zoomRate / this.scale),
|
|
|
|
|
+ h: this.$global.floatNum(height / this.zoomRate / this.scale),
|
|
|
|
|
+ unit: 'px',
|
|
|
|
|
+ };//坐标还原成px单位
|
|
|
|
|
+
|
|
|
|
|
+ this.currenPoint = point;
|
|
|
|
|
+ console.log("打印当前坐标", point);
|
|
|
|
|
+
|
|
|
|
|
+ // 只有当宽度和高度都大于0时才触发事件
|
|
|
|
|
+ if (point.w > 0 && point.h > 0) {
|
|
|
|
|
+ console.log("打印当前坐标", point);
|
|
|
|
|
+ this.$emit("GetRectPoint", point);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ //鼠标离开画布事件
|
|
|
|
|
+ onCanvasLeave()
|
|
|
|
|
+ {
|
|
|
|
|
+ console.log("鼠标离开画布事件");
|
|
|
|
|
+ // 如果正在绘制,自动结束绘制
|
|
|
|
|
+ if (this.isDrawing) {
|
|
|
|
|
+ this.onCanvasUp();
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ // 监听窗口大小变化,重新计算表格高度 使用节流防止频繁改变窗口大小导致计算量过大而页面卡顿
|
|
|
|
|
+ handleResize: throttle(function() {
|
|
|
|
|
+
|
|
|
|
|
+ // //窗口变化重新计算canvas的宽高
|
|
|
|
|
+ // let {width,height} = this.$refs.paperContainer.getBoundingClientRect();
|
|
|
|
|
+ // this.updateZoomAndPaperInfo(height);
|
|
|
|
|
+ // this.updateCanvasSize(width, height);
|
|
|
|
|
+ // this.centerCanvas();
|
|
|
|
|
+ // this.loadImage();
|
|
|
|
|
+ }, 500), // 节流 500 毫秒内最多执行一次
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+</script>
|
|
|
|
|
+<style lang="scss" scoped>
|
|
|
|
|
+.paper_container
|
|
|
|
|
+{
|
|
|
|
|
+ width: 100%;
|
|
|
|
|
+ height: 100%;
|
|
|
|
|
+ overflow: hidden;
|
|
|
|
|
+ position: relative;
|
|
|
|
|
+ .paper_canvas
|
|
|
|
|
+ {
|
|
|
|
|
+ position: absolute;
|
|
|
|
|
+ cursor: pointer;
|
|
|
|
|
+ background-color: transparent;
|
|
|
|
|
+ z-index: 10;//使canvas在图片上方
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ .img_container
|
|
|
|
|
+ {
|
|
|
|
|
+ position: absolute;
|
|
|
|
|
+ cursor: pointer;
|
|
|
|
|
+ z-index: 9;
|
|
|
|
|
+ // border:1px solid red;
|
|
|
|
|
+ img
|
|
|
|
|
+ {
|
|
|
|
|
+ width: 100%;
|
|
|
|
|
+ height: 100%;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ //自定义右键菜单层样式
|
|
|
|
|
+ .custom_context_menu
|
|
|
|
|
+ {
|
|
|
|
|
+ position: absolute;
|
|
|
|
|
+ background: white;
|
|
|
|
|
+ border: 1px solid #ccc;
|
|
|
|
|
+ border-radius: 4px;
|
|
|
|
|
+ box-shadow: 0 2px 10px rgba(0, 0, 0, 0.2);
|
|
|
|
|
+ z-index: 9999;
|
|
|
|
|
+ min-width: 120px;
|
|
|
|
|
+ .menu_item
|
|
|
|
|
+ {
|
|
|
|
|
+ padding: 8px 16px;
|
|
|
|
|
+ cursor: pointer;
|
|
|
|
|
+ font-size: 14px;
|
|
|
|
|
+
|
|
|
|
|
+ &:hover {
|
|
|
|
|
+ background-color: #f5f5f5;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ &:not(:last-child) {
|
|
|
|
|
+ border-bottom: 1px solid #eee;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+</style>
|