| | |
| | | <template> |
| | | <div class="paint" > |
| | | <canvas |
| | | width="400" |
| | | height="600" |
| | | id="canvas" |
| | | ></canvas> |
| | | <div class="paint-btn"> |
| | | <button @click="clearCanvas" >清除</button> |
| | | </div> |
| | | <div class="paint"> |
| | | <canvas width="600" height="800" id="canvas"></canvas> |
| | | <!-- 操作按钮 --> |
| | | <ul class="paint-btn"> |
| | | <li class="btn-box"> |
| | | <button @click="changeDrawMode"> |
| | | {{ isDraw ? "框选模式" : "绘图模式" }} |
| | | </button> |
| | | <button @click="clearCanvas">清除</button> |
| | | <button @click="setModelEraser"> |
| | | {{ isEraser ? "画笔" : "橡皮擦" }} |
| | | </button> |
| | | <button @click="saveImgData">保存</button> |
| | | </li> |
| | | <li> |
| | | |
| | | </li> |
| | | <li> |
| | | <label>画笔:</label> |
| | | <select name="" id="" @change="changeBrush" v-model="brush"> |
| | | <option |
| | | v-for="(item, index) in modelList" |
| | | :value="item.value" |
| | | :key="index" |
| | | > |
| | | {{ item.name }} |
| | | </option> |
| | | </select> |
| | | </li> |
| | | <li> |
| | | <label>线色:</label> |
| | | <input type="color" v-model="lineColor" @input="changeLineColor" /> |
| | | </li> |
| | | <li> |
| | | <label>线宽:</label> |
| | | <input type="range" v-model="lineWidth" @input="changeLineWidth" /> |
| | | </li> |
| | | <li> |
| | | <label>阴影色:</label> |
| | | <input type="color" v-model="showColor" @input="changeShowColor" /> |
| | | </li> |
| | | <li> |
| | | <label>阴影宽度:</label> |
| | | <input type="range" v-model="showWidth" @input="changeShowWidth" /> |
| | | </li> |
| | | <li> |
| | | <label>阴影偏移量:</label> |
| | | <input type="range" v-model="showOffset" @input="changeShowOffset" /> |
| | | </li> |
| | | </ul> |
| | | </div> |
| | | </template> |
| | | |
| | | <script> |
| | | import { fabric } from "fabric-with-erasing"; |
| | | export default { |
| | | data() { |
| | | return { |
| | | export default { |
| | | data() { |
| | | return { |
| | | backgroundImgUrl: "", // 背景 |
| | | isDraw: true, // 绘画、框选模式 |
| | | brush: "Pencil", // 画笔类型 |
| | | lineColor: "#000", |
| | | lineWidth: 1, |
| | | isEraser: false, |
| | | showColor: "#000", // 阴影色 |
| | | showWidth: 0, // 阴影宽度 |
| | | showOffset: 0, |
| | | modelList: [ |
| | | { |
| | | name: "Pencil", |
| | | value: "Pencil", |
| | | }, |
| | | { |
| | | name: "Circle", |
| | | value: "Circle", |
| | | }, |
| | | { |
| | | name: "Spray", |
| | | value: "Spray", |
| | | }, |
| | | { |
| | | name: "Pattern", |
| | | value: "Pattern", |
| | | }, |
| | | { |
| | | name: "hline", |
| | | value: "hline", |
| | | }, |
| | | { |
| | | name: "vline", |
| | | value: "vline", |
| | | }, |
| | | { |
| | | name: "square", |
| | | value: "square", |
| | | }, |
| | | { |
| | | name: "diamond", |
| | | value: "diamond", |
| | | }, |
| | | { |
| | | name: "texture", |
| | | value: "texture", |
| | | }, |
| | | ], |
| | | // 画笔模式 |
| | | vLinePatternBrush: null, |
| | | hLinePatternBrush: null, |
| | | squarePatternBrush: null, |
| | | diamondPatternBrush: null, |
| | | texturePatternBrush: null, |
| | | }; |
| | | }, |
| | | props: { |
| | | imgUrl: { |
| | | type: String, |
| | | default: |
| | | "https://cdn.learnku.com/uploads/images/202206/29/97252/aArKOJpl2A.png!large", |
| | | }, |
| | | page: { |
| | | type: Number, |
| | | default: 1, |
| | | }, |
| | | }, |
| | | mounted() { |
| | | this.init(); |
| | | }, |
| | | methods: { |
| | | // 初始化画布 |
| | | init() { |
| | | this.canvas = new fabric.Canvas("canvas", { |
| | | isDrawingMode: true, |
| | | }); |
| | | // 设置背景 |
| | | this.setBackgroundImage() |
| | | // |
| | | fabric.Object.prototype.transparentCorners = false; |
| | | this.setBrush() |
| | | }, |
| | | // 创建各种笔刷 |
| | | setBrush() { |
| | | if (fabric.PatternBrush) { |
| | | // 画笔样式 |
| | | this.vLinePatternBrush = new fabric.PatternBrush(this.canvas); |
| | | this.vLinePatternBrush.getPatternSrc = () => { |
| | | let patternCanvas = fabric.document.createElement("canvas"); |
| | | patternCanvas.width = patternCanvas.height = 10; |
| | | let ctx = patternCanvas.getContext("2d"); |
| | | ctx.strokeStyle = this.lineColor; |
| | | ctx.lineWidth = 5; |
| | | ctx.beginPath(); |
| | | ctx.moveTo(0, 5); |
| | | ctx.lineTo(10, 5); |
| | | ctx.closePath(); |
| | | ctx.stroke(); |
| | | return patternCanvas; |
| | | }; |
| | | this.hLinePatternBrush = new fabric.PatternBrush(this.canvas); |
| | | this.hLinePatternBrush.getPatternSrc = function () { |
| | | let patternCanvas = fabric.document.createElement("canvas"); |
| | | patternCanvas.width = patternCanvas.height = 10; |
| | | let ctx = patternCanvas.getContext("2d"); |
| | | ctx.strokeStyle = this.lineColor; |
| | | ctx.lineWidth = 5; |
| | | ctx.beginPath(); |
| | | ctx.moveTo(5, 0); |
| | | ctx.lineTo(5, 10); |
| | | ctx.closePath(); |
| | | ctx.stroke(); |
| | | return patternCanvas; |
| | | }; |
| | | this.squarePatternBrush = new fabric.PatternBrush(this.canvas); |
| | | this.squarePatternBrush.getPatternSrc = function () { |
| | | const squareWidth = 10; |
| | | const squareDistance = 2; |
| | | const patternCanvas = fabric.document.createElement("canvas"); |
| | | patternCanvas.width = patternCanvas.height = |
| | | squareWidth + squareDistance; |
| | | const ctx = patternCanvas.getContext("2d"); |
| | | ctx.fillStyle = this.color; |
| | | ctx.fillRect(0, 0, squareWidth, squareWidth); |
| | | return patternCanvas; |
| | | }; |
| | | this.diamondPatternBrush = new fabric.PatternBrush(this.canvas); |
| | | this.diamondPatternBrush.getPatternSrc = function () { |
| | | const squareWidth = 10; |
| | | const squareDistance = 5; |
| | | const patternCanvas = fabric.document.createElement("canvas"); |
| | | const rect = new fabric.Rect({ |
| | | width: squareWidth, |
| | | height: squareWidth, |
| | | angle: 45, |
| | | fill: this.color, |
| | | }); |
| | | var canvasWidth = rect.getBoundingRect().width; |
| | | patternCanvas.width = patternCanvas.height = |
| | | canvasWidth + squareDistance; |
| | | rect.set({ left: canvasWidth / 2, top: canvasWidth / 2 }); |
| | | var ctx = patternCanvas.getContext("2d"); |
| | | rect.render(ctx); |
| | | return patternCanvas; |
| | | }; |
| | | |
| | | const img = new Image(); |
| | | // img.src = "../assets/images/drop.jpg"; |
| | | this.texturePatternBrush = new fabric.PatternBrush(this.canvas); |
| | | this.texturePatternBrush.source = img; |
| | | } |
| | | }, |
| | | mounted() { |
| | | this.init() |
| | | // 设置背景图方法 |
| | | setBackgroundImage() { |
| | | // 使用fabric的Image.fromURL方法来加载图像 |
| | | const oldData = localStorage.getItem( |
| | | this.config.activeBook.name + "-paint-" + this.page |
| | | ); |
| | | this.backgroundImgUrl = oldData || this.imgUrl; |
| | | fabric.Image.fromURL( |
| | | this.backgroundImgUrl, |
| | | (img) => { |
| | | // 图像加载完成后,将其设置为画布的背景 |
| | | img |
| | | .scale( |
| | | this.canvas.width / img.width, |
| | | this.canvas.height / img.height |
| | | ) |
| | | .set({ |
| | | left: 0, |
| | | top: 0, |
| | | originX: "left", |
| | | originY: "top", |
| | | }); |
| | | |
| | | // 将图像添加到画布中,并将其放在最底层 |
| | | this.canvas.setBackgroundImage( |
| | | img, |
| | | this.canvas.renderAll.bind(this.canvas), |
| | | { |
| | | // 可以设置图像的样式,比如不透明度 |
| | | opacity: 0.5, |
| | | } |
| | | ); |
| | | // 渲染画布 |
| | | this.canvas.renderAll(); |
| | | }, |
| | | { |
| | | crossOrigin: "Anonymous", // 如果图像在不同域上,需要设置crossOrigin |
| | | } |
| | | ); |
| | | }, |
| | | methods: { |
| | | // 初始化画布 |
| | | init() { |
| | | this.canvas = new fabric.Canvas("canvas",{ |
| | | isDrawingMode: true |
| | | // 绘图 框选 模式切换 |
| | | changeDrawMode() { |
| | | this.isDraw = !this.isDraw; |
| | | this.canvas.isDrawingMode = !this.canvas.isDrawingMode; |
| | | }, |
| | | // 清空画布 |
| | | clearCanvas() { |
| | | this.canvas.clear(); |
| | | this.setBackgroundImage() |
| | | }, |
| | | // 修改画笔颜色 |
| | | changeLineColor(e) { |
| | | let brush = this.canvas.freeDrawingBrush; |
| | | brush.color = e.srcElement.value; |
| | | if (brush.getPatternSrc) { |
| | | brush.source = brush.getPatternSrc.call(brush); |
| | | } |
| | | console.log(e.srcElement.value); |
| | | }, |
| | | // 修改画笔粗细 |
| | | changeLineWidth(e) { |
| | | console.log(e); |
| | | this.canvas.freeDrawingBrush.width = |
| | | parseInt(e.srcElement.value, 10) || 1; |
| | | }, |
| | | // 画笔样式切换 |
| | | changeBrush() { |
| | | if (this.brush == "hline") { |
| | | this.canvas.freeDrawingBrush = this.vLinePatternBrush; |
| | | } else if (this.brush == "vline") { |
| | | this.canvas.freeDrawingBrush = this.hLinePatternBrush; |
| | | } else if (this.brush == "square") { |
| | | this.canvas.freeDrawingBrush = this.squarePatternBrush; |
| | | } else if (this.brush == "diamond") { |
| | | this.canvas.freeDrawingBrush = this.diamondPatternBrush; |
| | | } else if (this.brush == "texture") { |
| | | this.canvas.freeDrawingBrush = this.texturePatternBrush; |
| | | } else { |
| | | this.canvas.freeDrawingBrush = new fabric[this.brush + "Brush"]( |
| | | this.canvas |
| | | ); |
| | | } |
| | | if (this.canvas.freeDrawingBrush) { |
| | | var brush = this.canvas.freeDrawingBrush; |
| | | brush.color = this.lineColor; |
| | | if (brush.getPatternSrc) { |
| | | brush.source = brush.getPatternSrc.call(brush); |
| | | } |
| | | brush.width = parseInt(this.lineWidth, 10) || 1; |
| | | brush.shadow = new fabric.Shadow({ |
| | | blur: parseInt(this.showWidth, 10) || 0, |
| | | offsetX: 0, |
| | | offsetY: 0, |
| | | affectStroke: true, |
| | | color: this.showColor, |
| | | }); |
| | | }, |
| | | // 清空画布 |
| | | clearCanvas() { |
| | | this.canvas.clear() |
| | | } |
| | | }, |
| | | } |
| | | // 橡皮擦 |
| | | setModelEraser() { |
| | | this.isEraser = !this.isEraser; |
| | | if (this.isEraser) { |
| | | this.canvas.freeDrawingBrush = new fabric.EraserBrush(this.canvas); // 使用橡皮擦画笔 |
| | | this.canvas.freeDrawingBrush.width = this.lineWidth; |
| | | } else { |
| | | this.changeBrush(); |
| | | // this.canvas.freeDrawingBrush = new fabric.PencilBrush(this.canvas); // 使用橡皮擦画笔 |
| | | // this.canvas.freeDrawingBrush.width = this.lineWidth; // 设置画笔粗细 |
| | | } |
| | | }, |
| | | // 修改阴影色 |
| | | changeShowColor(e) { |
| | | this.canvas.contextContainer.shadowColor = e.srcElement.value; |
| | | }, |
| | | |
| | | // 阴影宽度 |
| | | changeShowWidth(e) { |
| | | console.log(this.canvas); |
| | | this.canvas.contextContainer.shadowBlur = |
| | | parseInt(e.srcElement.value, 10) || 0; |
| | | }, |
| | | // 阴影偏移量 |
| | | changeShowOffset(e) { |
| | | this.canvas.contextContainer.shadowOffsetX = |
| | | parseInt(e.srcElement.value, 10) || 0; |
| | | this.canvas.contextContainer.shadowOffsetY = |
| | | parseInt(e.srcElement.value, 10) || 0; |
| | | }, |
| | | // 保存图书 |
| | | saveImgData() { |
| | | const imgData = this.canvas.toDataURL({ |
| | | format: "png", // 指定输出格式,通常是'png'或'jpeg' |
| | | quality: 0.8, // 仅在输出格式为'jpeg'时有效 |
| | | multiplier: 1, // 提高分辨率,可选参数,默认为1 |
| | | left: 0, // 裁剪区域的左边界(可选) |
| | | top: 0, // 裁剪区域的上边界(可选) |
| | | width: canvas.width, // 裁剪区域的宽度(可选,默认为画布的宽度) |
| | | height: canvas.height, // 裁剪区域的高度(可选,默认为画布的高度) |
| | | }); |
| | | localStorage.setItem( |
| | | this.config.activeBook.name + "-paint-" + this.page, |
| | | imgData |
| | | ); |
| | | console.log("本地图", imgData); |
| | | }, |
| | | }, |
| | | }; |
| | | </script> |
| | | |
| | | <style lang="less" scoped> |
| | | #canvas { |
| | | border: 1px solid #ccc |
| | | #canvas { |
| | | border: 1px solid #ccc; |
| | | } |
| | | .paint-btn { |
| | | margin-top:40px; |
| | | padding:20px; |
| | | border:1px solid #ededed; |
| | | width:max-content; |
| | | li { |
| | | margin-bottom:6px; |
| | | } |
| | | </style> |
| | | } |
| | | .btn-box { |
| | | display:flex; |
| | | justify-content:space-between; |
| | | } |
| | | </style> |