| | |
| | | <!-- 涂色连线题控件 --> |
| | | <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', |
| | | }" |
| | |
| | | > |
| | | </div> |
| | | </div> |
| | | <div class="footer"> |
| | | <BrushSize :size="brushSize" @change-size="onChangeSize" @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> |
| | |
| | | export default { |
| | | name: "graffiti", |
| | | components: { BrushSize, ToolBtns }, |
| | | props:{ |
| | | page:{ |
| | | type:Number |
| | | props: { |
| | | page: { |
| | | type: Number, |
| | | }, |
| | | bcImg:{ |
| | | type:String |
| | | } |
| | | 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", // 笔刷颜色 |
| | |
| | | left: 0, |
| | | top: 0, |
| | | }, |
| | | backgroundImage: |
| | | "", |
| | | backgroundImage: "", |
| | | }; |
| | | }, |
| | | mounted() { |
| | | this.backgroundImage = this.bcImg |
| | | 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,true); // 添加滚动条滚动事件监听器 |
| | | (this.container ? this.container : document).addEventListener( |
| | | "scroll", |
| | | this.updateCanvasOffset, |
| | | true |
| | | ); // 添加滚动条滚动事件监听器 |
| | | this.getCanvasOffset(); |
| | | this.context.lineGap = "round"; |
| | | this.context.lineJoin = "round"; |
| | |
| | | this.canvas.addEventListener("mouseleave", this.closePaint); |
| | | } |
| | | this.toolClear(); |
| | | const oldData = localStorage.getItem('graffiti-data') |
| | | if(oldData) { |
| | | this.backgroundImage = oldData |
| | | 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, |
| | | }); // 添加这一行 |
| | |
| | | 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); |
| | | }, |
| | | |
| | |
| | | y: y - this.canvasOffset.top, |
| | | }; |
| | | }, |
| | | |
| | | // 鼠标抬起方法 |
| | | downCallback(event) { |
| | | // 先保存之前的数据,用于撤销时恢复(绘制前保存,不是绘制后再保存) |
| | | const data = this.context.getImageData( |
| | |
| | | this.brushTool === "eraser" ? "#FFFFFF" : this.brushColor; |
| | | this.painting = true; |
| | | }, |
| | | |
| | | // 鼠标移动方法(计算坐标并渲染轨迹) |
| | | moveCallback(event) { |
| | | if (!this.painting) { |
| | | return; |
| | |
| | | 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(); |
| | |
| | | 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) |
| | | var imgData = this.canvas.toDataURL({ |
| | | format: "png", |
| | | quality: 1, |
| | | width: 800, |
| | | }); |
| | | localStorage.setItem( |
| | | this.config.activeBook.name + "-graffiti-" + this.page, |
| | | imgData |
| | | ); |
| | | }, |
| | | // 保存方法,保存为一张图片并下载 |
| | | // 保存为一张图片并下载的方法 |
| | | saveImgData() { |
| | | var link = document.createElement("a"); |
| | | var imgData = this.canvas.toDataURL({format: 'png', quality:1, width:20000, height:4000}); |
| | | 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--){ |
| | | |
| | | 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}); |
| | | 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(); |
| | |
| | | saveData(data) { |
| | | this.historyData.length >= 50 && this.historyData.shift(); // 设置储存上限为50步 |
| | | this.historyData.push(data); |
| | | console.log('数据',this.historyData); |
| | | }, |
| | | // 清除、撤销、保存状态不需要保持,操作完后恢复笔刷状态 |
| | | resetToolActive() { |
| | |
| | | .page { |
| | | display: flex; |
| | | flex-direction: column; |
| | | width:100%; |
| | | height: 866px; |
| | | width: 100%; |
| | | } |
| | | |
| | | .main { |
| | |
| | | justify-content: space-around; |
| | | align-items: center; |
| | | height: 88px; |
| | | background-color: #fff; |
| | | } |
| | | |
| | | #canvas_panel { |