├── .gitignore ├── README.md ├── _config.yml ├── autopush.bat ├── babel.config.js ├── docs ├── css │ ├── app.6838266e.css │ └── chunk-vendors.2ac5db4b.css ├── favicon.ico ├── fonts │ ├── element-icons.535877f5.woff │ └── element-icons.732389de.ttf ├── index.html └── js │ ├── app.20cd3089.js │ ├── app.20cd3089.js.map │ ├── chunk-vendors.2c95c892.js │ └── chunk-vendors.2c95c892.js.map ├── package-lock.json ├── package.json ├── public ├── favicon.ico └── index.html ├── src ├── App.vue ├── assets │ ├── ali.jpg │ ├── demo.png │ ├── icon │ │ ├── iconfont.js │ │ └── iconfont.svg │ ├── logo.png │ └── wx.jpg ├── components │ └── imgDraw │ │ ├── cursor.js │ │ ├── img.js │ │ └── imgDraw.vue ├── main.js └── plugins │ └── element.js └── vue.config.js /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | # /dist 4 | deploy.sh 5 | # local env files 6 | .env.local 7 | .env.*.local 8 | 9 | # Log files 10 | npm-debug.log* 11 | yarn-debug.log* 12 | yarn-error.log* 13 | pnpm-debug.log* 14 | 15 | # Editor directories and files 16 | .idea 17 | .vscode 18 | *.suo 19 | *.ntvs* 20 | *.njsproj 21 | *.sln 22 | *.sw? 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Vue image-painter 2 | 3 | 图片涂鸦、绘制、标注 4 | 5 | [在线预览(GitHub)](https://kevin123x.github.io/Vue-ImagePainter) 6 | 7 | [在线预览(Gitee)](http://keivn.vip.com.gitee.io/vue-imagepainter) 8 | 9 | ### 支持功能 10 | 11 | 1.画笔颜色 🎨 12 | 13 | 2.画笔粗细 ✏️ 14 | 15 | 3.画布放大 🔍 16 | 17 | 4.画布缩小 🔍 18 | 19 | 5.清除画布 🧹 20 | 21 | 6.保存图片 🏔 22 | 23 | 7.回退一步 ↩️ 24 | 25 | 8.前进一步 ↪️ 26 | 27 | ### 画笔工具类型 28 | 29 | 1.画笔 ✏️ 30 | 31 | 2.直线 📏 32 | 33 | 3.圆形 ⚪️ 34 | 35 | 4.矩形 🔲 36 | 37 | 5.橡皮 🧽 38 | 39 | 6.文字 📝 40 | 41 | ## 实现思路 42 | 43 | 创建三个不同作用的画布 44 | 45 | ``` 46 | // 用于绘制的画板 47 | this.canvas_front = document.getElementById("ctx_front"); 48 | 49 | // 用于生成绘制后效果图的画板 50 | this.canvas_back = document.getElementById("ctx_back"); 51 | 52 | // 底图画板,橡皮擦除时获取像素放到绘制画板中,达到不擦出底图的效果 53 | this.canvas_base = document.getElementById("ctx_base"); 54 | ``` 55 | 56 | 鼠标按下判断工具类型,记录初始位置 57 | 58 | ``` 59 | let mousedown = (e) => { 60 | this.ctx_front.strokeStyle = this.defaultColor; 61 | this.ctx_front.lineWidth = this.slide; 62 | e = e || window.event; 63 | sx = e.clientX - this.canvas_front.offsetLeft; 64 | sy = e.clientY - this.canvas_front.offsetTop; 65 | 66 | this.ctx_front.moveTo(sx, sy); 67 | this.canDraw = true; 68 | ... 69 | ``` 70 | 71 | 若是输入文字则定位输入框到画布中,失焦或回车文字绘制到画布中 72 | 73 | ``` 74 | this.handleTextBlur(); 75 | this.text = ""; 76 | text.style.fontSize = 14 + this.slide * 10 + "px"; 77 | text.style.color = this.defaultColor; 78 | text.style.left = 79 | e.offsetX + this.canvas_front.offsetLeft - 20 + "px"; 80 | text.style.top = 81 | e.offsetY + this.canvas_front.offsetTop - 10 + "px"; 82 | text.style.zIndex = 10; 83 | text.style.display = "block"; 84 | this.tl = e.offsetX - 20; 85 | this.tt = e.offsetY + 10; 86 | break; 87 | 88 | ... 89 | 90 | handleTextBlur() { 91 | let text = document.getElementById("text"); 92 | this.ctx_front.font = `300 ${text.style.fontSize} sans-serif`; 93 | this.ctx_front.fillStyle = this.defaultColor; 94 | this.ctx_front.fillText(this.text, this.tl, this.tt); 95 | text.style.display = "none"; 96 | this.handleSaveCanvasStore(); 97 | } 98 | ``` 99 | 100 | 鼠标移动,根据工具类型绘制不同路径 101 | 102 | ``` 103 | let mousemove = (e) => { 104 | e = e || window.event; 105 | mx = e.clientX - this.canvas_front.offsetLeft; 106 | my = e.clientY - this.canvas_front.offsetTop; 107 | const cbx = this.ctx_base.getImageData( 108 | e.offsetX - this.slide / 2, 109 | e.offsetY - this.slide / 2, 110 | this.slide * 2, 111 | this.slide * 2 112 | ); 113 | ... 114 | ``` 115 | 116 | 若工具类型是橡皮,则从 canvas_base 中获取底图像素放回 canvas_front 中 117 | 118 | ``` 119 | const cbx = this.ctx_base.getImageData( 120 | e.offsetX - this.slide / 2, 121 | e.offsetY - this.slide / 2, 122 | this.slide * 2, 123 | this.slide * 2 124 | ); 125 | 126 | ... 127 | 128 | this.ctx_front.putImageData( 129 | cbx, 130 | e.offsetX - this.slide / 2, 131 | e.offsetY - this.slide / 2 132 | ); 133 | ... 134 | ``` 135 | 136 | 鼠标抬起,从绘制画布 canvas_front 生成绘制图并绘制到 canvas_back 中,然后再重绘 canvas_front 137 | 138 | ``` 139 | handleSaveCanvasStore() { 140 | let url = this.canvas_front.toDataURL(); 141 | let image = new Image(); 142 | image.src = url; 143 | image.onload = () => { 144 | this.ctx_front.clearRect( 145 | 0, 146 | 0, 147 | this.canvas_front.width, 148 | this.canvas_front.height 149 | ); 150 | this.ctx_front.drawImage(image, 0, 0, image.width, image.height); 151 | this.ctx_back.drawImage(image, 0, 0, image.width, image.height); 152 | const url2 = this.canvas_back.toDataURL(); 153 | this.currentImg.url = url2; 154 | this.currentImg.index += 1; 155 | this.canvasStore.push(url2); 156 | this.prevDis = false; 157 | console.log(this.canvasStore); 158 | }; 159 | }, 160 | 161 | ``` 162 | 最终保存图片 163 | ``` 164 | let canvas = document.getElementById("ctx_back"); 165 | this.$refs.download.href = canvas.toDataURL(); 166 | this.$refs.download.click(); 167 | ``` 168 | ### Project setup 169 | 170 | ``` 171 | npm install 172 | ``` 173 | 174 | ### Compiles and hot-reloads for development 175 | 176 | ``` 177 | npm run serve 178 | ``` 179 | 180 | ### Compiles and minifies for production 181 | 182 | ``` 183 | npm run build 184 | ``` 185 | 186 | ### Lints and fixes files 187 | 188 | ``` 189 | npm run lint 190 | ``` 191 | 192 | ### 感谢支持 193 | 194 | > 开发不易,且用且珍惜 ,随手给个星,开心到起飞 195 | -------------------------------------------------------------------------------- /_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-merlot -------------------------------------------------------------------------------- /autopush.bat: -------------------------------------------------------------------------------- 1 | git add . 2 | set yyyy=%date:~,4% 3 | set mm=%date:~5,2% 4 | set day=%date:~8,2% 5 | set "YYYYmmdd=%yyyy%%mm%%day%" 6 | set "YYYYmmdd=%YYYYmmdd: =0%" 7 | echo "YYYYmmdd%YYYYmmdd%YYYYmmdd" 8 | set hh=%time:~0,2% 9 | set mi=%time:~3,2% 10 | set ss=%time:~6,2% 11 | set "hhmiss=%hh%%mi%%ss%" 12 | set "hhmiss=%hhmiss: =0%" 13 | echo "hhmiss%Time%hhmiss" 14 | echo %hhmiss% 15 | set "hhmiss=%hhmiss::=0%" 16 | set "hhmiss=%hhmiss: =0%" 17 | set "timestamp=%YYYYmmdd%%hhmiss%" 18 | set /p input=更新: 19 | git commit -m "%timeStamp%%input%" 20 | git push 21 | 22 | goto exit -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | '@vue/cli-plugin-babel/preset' 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /docs/css/app.6838266e.css: -------------------------------------------------------------------------------- 1 | [data-v-34058e9c]{margin:0;padding:0;box-sizing:border-box}.icon[data-v-34058e9c]{width:1em;height:1em;vertical-align:-.15em;fill:currentColor;overflow:hidden}[data-v-34058e9c] .el-dialog{display:flex;flex-direction:column;align-items:center}[data-v-34058e9c] .el-dialog>div{width:100%;box-sizing:border-box}[data-v-34058e9c] .el-dialog .el-dialog__header{padding:0 20px}[data-v-34058e9c] .el-dialog .el-dialog__header p{padding:20px 0;border-bottom:1px solid #bdbdbd}[data-v-34058e9c] .el-dialog .el-dialog__body{padding:10px 20px;flex:1;height:0;padding-top:0;position:relative;overflow:hidden}[data-v-34058e9c] .el-dialog .el-dialog__body .d_body{height:100%;overflow-y:auto;padding-top:20px}[data-v-34058e9c] .el-dialog .el-dialog__body .d_body .board{position:relative;min-height:100%}[data-v-34058e9c] .el-dialog .el-dialog__body .d_body .board canvas{position:absolute;margin:0 auto;left:0;right:0;top:0}[data-v-34058e9c] .el-dialog .el-dialog__body .d_body .board #ctx_front{z-index:5}[data-v-34058e9c] .el-dialog .el-dialog__body .d_body .board #ctx_back{z-index:3}[data-v-34058e9c] .el-dialog .el-dialog__body .d_body .board #ctx_base{z-index:1}[data-v-34058e9c] .el-dialog .el-dialog__body .d_body .board #text{position:absolute;z-index:-1;resize:none;outline:none;border:1px dashed #eee;overflow:hidden;background:transparent;line-height:30px;display:none}[data-v-34058e9c] .el-dialog .el-dialog__body .d_body .tools{width:100%;position:absolute;display:flex;z-index:5;background:#fff;transition:all .2s ease-in-out}[data-v-34058e9c] .el-dialog .el-dialog__body .d_body .settings{top:0;left:50%;transform:translateX(-50%);z-index:10;padding:5px 10px;border-bottom-left-radius:4px;border-bottom-right-radius:4px;border:1px solid #eee;border-top:0;width:auto}[data-v-34058e9c] .el-dialog .el-dialog__body .d_body .settings .tool_item{display:flex;align-items:center;flex-wrap:nowrap}[data-v-34058e9c] .el-dialog .el-dialog__body .d_body .settings .tool_item:not(:last-of-type){margin-right:25px}[data-v-34058e9c] .el-dialog .el-dialog__body .d_body .settings .go_up{margin-right:0!important}[data-v-34058e9c] .el-dialog .el-dialog__body .d_body .settings button{display:flex;align-items:center;justify-content:center;padding:5px 10px;background:#fff;border:1px solid #eee;outline:none;cursor:pointer;position:relative;flex-wrap:nowrap}[data-v-34058e9c] .el-dialog .el-dialog__body .d_body .settings button svg{color:#333;font-size:18px;margin-right:5px}[data-v-34058e9c] .el-dialog .el-dialog__body .d_body .settings button span{white-space:nowrap}[data-v-34058e9c] .el-dialog .el-dialog__body .d_body .settings button /deep/ .el-color-picker{position:absolute;left:0;top:0;width:100%;height:100%}[data-v-34058e9c] .el-dialog .el-dialog__body .d_body .settings button /deep/ .el-color-picker .el-color-picker__trigger{width:100%;height:100%;opacity:0;filter:alpha(opacity=0)}[data-v-34058e9c] .el-dialog .el-dialog__body .d_body .settings .slide{width:150px;display:flex;flex-direction:row;flex-wrap:nowrap;align-items:center}[data-v-34058e9c] .el-dialog .el-dialog__body .d_body .settings .slide svg{font-size:18px;margin-right:5px}[data-v-34058e9c] .el-dialog .el-dialog__body .d_body .settings .slide /deep/ .el-slider{flex:1;width:0;margin-left:10px}[data-v-34058e9c] .el-dialog .el-dialog__body .d_body .settings .slide /deep/ .el-slider .el-slider__button-wrapper .el-slider__button{width:12px;height:12px}[data-v-34058e9c] .el-dialog .el-dialog__body .d_body .settings .pull{position:absolute;right:20px;bottom:-45px;display:flex;flex-direction:column;align-items:center;justify-content:flex-start;margin-right:0}[data-v-34058e9c] .el-dialog .el-dialog__body .d_body .settings .pull .line{width:2px;height:30px;background:#1e90ff}[data-v-34058e9c] .el-dialog .el-dialog__body .d_body .settings .pull .round{width:15px;height:15px;border-radius:50%;background:#1e90ff;cursor:pointer}[data-v-34058e9c] .el-dialog .el-dialog__body .d_body .bars{top:100px;right:30px;z-index:10;padding:15px;border-top-left-radius:4px;border-bottom-left-radius:4px;border:1px solid #eee;width:auto;display:flex;flex-direction:column}[data-v-34058e9c] .el-dialog .el-dialog__body .d_body .bars .tool_item{cursor:pointer}[data-v-34058e9c] .el-dialog .el-dialog__body .d_body .bars .tool_item:not(:last-of-type){margin-bottom:15px;border-bottom:1px solid #ddd;padding-bottom:10px}[data-v-34058e9c] .el-dialog .el-dialog__body .d_body .bars .tool_item svg{font-size:24px;margin-right:8px}[data-v-34058e9c] .el-dialog .el-dialog__body .d_body .bars .tool_item span{font-size:18px}[data-v-34058e9c] .el-dialog .el-dialog__body .d_body .bars .tool_item:hover span,[data-v-34058e9c] .el-dialog .el-dialog__body .d_body .bars .tool_item:hover svg{color:#1e90ff}[data-v-34058e9c] .el-dialog .el-dialog__body .d_body .bars .activeTool{border-color:#1e90ff!important}[data-v-34058e9c] .el-dialog .el-dialog__body .d_body .bars .activeTool span,[data-v-34058e9c] .el-dialog .el-dialog__body .d_body .bars .activeTool svg{color:#1e90ff}[data-v-34058e9c] .el-dialog .el-dialog__body .d_body .bars .arrow{width:20px;height:20px;text-align:center;line-height:20px;border-radius:50%;border:1px solid #606266;color:#606266;position:absolute;left:-10px;background:#fff;top:50%;transform:translateY(-50%);cursor:pointer}[data-v-34058e9c] .el-dialog .el-dialog__body .d_body .bars .el-icon-s-tools{left:-30px;width:30px;height:30px;line-height:30px;font-size:20px;color:#1e90ff;border-color:#1e90ff}[data-v-34058e9c] .el-dialog .el-dialog__body .d_body .bars .el-icon-arrow-right{left:calc(100% - 10px)}[data-v-34058e9c] .el-dialog .el-dialog__body .d_body .hideTools{right:-100px}[data-v-34058e9c] .el-dialog .el-dialog__body .d_body .noExpand{top:-50px}[data-v-34058e9c] .el-dialog .el-dialog__footer{text-align:center!important}[data-v-34058e9c] .el-dialog .el-dialog__footer span button{padding-left:40px;padding-right:40px}[data-v-34058e9c] .el-dialog .el-dialog__footer span button:first-of-type{margin-right:50px}[data-v-34058e9c] .el-dialog .el-dialog__footer span button:last-of-type{margin-left:50px}#app{font-family:Avenir,Helvetica,Arial,sans-serif;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;text-align:center;color:#2c3e50;margin-top:60px} -------------------------------------------------------------------------------- /docs/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kevin123X/Vue-ImagePainter/b8b5ea4e04d4f20d663b4c47e756fdade06d0252/docs/favicon.ico -------------------------------------------------------------------------------- /docs/fonts/element-icons.535877f5.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kevin123X/Vue-ImagePainter/b8b5ea4e04d4f20d663b4c47e756fdade06d0252/docs/fonts/element-icons.535877f5.woff -------------------------------------------------------------------------------- /docs/fonts/element-icons.732389de.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kevin123X/Vue-ImagePainter/b8b5ea4e04d4f20d663b4c47e756fdade06d0252/docs/fonts/element-icons.732389de.ttf -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | Vue-ImagePainter
-------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Vue-ImagePainter", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "serve": "vue-cli-service serve", 7 | "build": "vue-cli-service build", 8 | "lint": "vue-cli-service lint" 9 | }, 10 | "dependencies": { 11 | "core-js": "^3.6.5", 12 | "element-ui": "^2.4.5", 13 | "less-loader": "^6.1.2", 14 | "vue": "^2.6.11" 15 | }, 16 | "devDependencies": { 17 | "@vue/cli-plugin-babel": "~4.4.0", 18 | "@vue/cli-plugin-eslint": "~4.4.0", 19 | "@vue/cli-service": "~4.4.0", 20 | "babel-eslint": "^10.1.0", 21 | "eslint": "^6.7.2", 22 | "eslint-plugin-vue": "^6.2.2", 23 | "vue-cli-plugin-element": "^1.0.1", 24 | "vue-template-compiler": "^2.6.11" 25 | }, 26 | "eslintConfig": { 27 | "root": true, 28 | "env": { 29 | "node": true 30 | }, 31 | "extends": [ 32 | "plugin:vue/essential", 33 | "eslint:recommended" 34 | ], 35 | "parserOptions": { 36 | "parser": "babel-eslint" 37 | }, 38 | "rules": {} 39 | }, 40 | "browserslist": [ 41 | "> 1%", 42 | "last 2 versions", 43 | "not dead" 44 | ] 45 | } 46 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kevin123X/Vue-ImagePainter/b8b5ea4e04d4f20d663b4c47e756fdade06d0252/public/favicon.ico -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | <%= htmlWebpackPlugin.options.title %> 9 | 10 | 11 | 14 |
15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /src/App.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 17 | 18 | 28 | -------------------------------------------------------------------------------- /src/assets/ali.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kevin123X/Vue-ImagePainter/b8b5ea4e04d4f20d663b4c47e756fdade06d0252/src/assets/ali.jpg -------------------------------------------------------------------------------- /src/assets/demo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kevin123X/Vue-ImagePainter/b8b5ea4e04d4f20d663b4c47e756fdade06d0252/src/assets/demo.png -------------------------------------------------------------------------------- /src/assets/icon/iconfont.js: -------------------------------------------------------------------------------- 1 | !function(a){var t,h,e,o,l,i,n,c='',d=(t=document.getElementsByTagName("script"))[t.length-1].getAttribute("data-injectcss");if(d&&!a.__iconfont__svg__cssinject__){a.__iconfont__svg__cssinject__=!0;try{document.write("")}catch(a){console&&console.log(a)}}function v(){i||(i=!0,o())}h=function(){var a,t,h,e,o,l=document.createElement("div");l.innerHTML=c,c=null,(a=l.getElementsByTagName("svg")[0])&&(a.setAttribute("aria-hidden","true"),a.style.position="absolute",a.style.width=0,a.style.height=0,a.style.overflow="hidden",t=a,(h=document.body).firstChild?(e=t,(o=h.firstChild).parentNode.insertBefore(e,o)):h.appendChild(t))},document.addEventListener?~["complete","loaded","interactive"].indexOf(document.readyState)?setTimeout(h,0):(e=function(){document.removeEventListener("DOMContentLoaded",e,!1),h()},document.addEventListener("DOMContentLoaded",e,!1)):document.attachEvent&&(o=h,l=a.document,i=!1,(n=function(){try{l.documentElement.doScroll("left")}catch(a){return void setTimeout(n,50)}v()})(),l.onreadystatechange=function(){"complete"==l.readyState&&(l.onreadystatechange=null,v())})}(window); -------------------------------------------------------------------------------- /src/assets/icon/iconfont.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 8 | Created by iconfont 9 | 10 | 11 | 12 | 13 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | -------------------------------------------------------------------------------- /src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kevin123X/Vue-ImagePainter/b8b5ea4e04d4f20d663b4c47e756fdade06d0252/src/assets/logo.png -------------------------------------------------------------------------------- /src/assets/wx.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kevin123X/Vue-ImagePainter/b8b5ea4e04d4f20d663b4c47e756fdade06d0252/src/assets/wx.jpg -------------------------------------------------------------------------------- /src/components/imgDraw/cursor.js: -------------------------------------------------------------------------------- 1 | let cursors={ 2 | pen:'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAACd0lEQVRYR+2WT2gTQRTGv7eb9JLkmPoHvHkQLy2i94o0KMiyMxgRbwrmpigeFBUJFPHiRegpgiLiQZbMTFoR/4AX7UVQCD14EfGcQkGFiia7T0Yq1HWTbLeGgnSOszPv9/HNex9L2ORFm8zHloD/1gHyff8cEZ0EEAIIyuXybKPR6MZ7biQOCCEaAM6shRHRrFLq7MgF+L5/nIgexUHMHOXz+XIQBMt/CPuXY+h53m7Xdd8BKPWpO6m1bo9EQLVaHet2uwtEtL8P/HMul9sRBMG3kQjwff8WEV3sA2cAp7TW90fSA0KIwwCeAInBxsx8wRhzO0nchqdACDEeRVHbcZztCQA7gjWt9d1+vbYhAfV63Wm3208BTCcAekR0Win1YFCjb0iAEOIygJsJgB9EdEIppYdNWWYBUsoDzPwawFgMssLMwhjzfBjcfs8sQAjRBCBjkC9RFB1ttVqv0sBTC6hUKoVisSiZeS8RPVZKLQghbNTayP29lpn5iDHmTVp4KgGrVhsAO1cLv9RaH7LuSSlvMPN5AF8BVOIpl0bIwCeoVqvlXq+3CGDbmmI2VI5prZXdm5qaypVKpbH5+fmVNMB1BZGUcoaZr8UvMXPXcZx7RHSn2Wy+BWBFZVp9HajVavmlpaVPa6xPBBDRdaXUTCZ6vynwPK/kuu4lAFeHFWbmK8aYpCwYdvXX978ckFLuY2Zra9plm+9F2sNDe8COXKFQ+AhgPEXRxYmJicl6vR6lOJv8hEm71oUwDB86jrNnQOH3YRh6c3NzH7LCB+aAbcJOp3OQiKaZeRcRWUe+A+gQ0TPXdXX85yKLkMxRnAWWdGdLwJYDPwFn/dQhz4OkVwAAAABJRU5ErkJggg==', 3 | line:'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAA4klEQVRYR+3WTQrCMBCG4fmy8AYeyo2kBS+goAsPJPhzhTEIHso7SEa6EETsXzJjN+m+vE8nhQlo4gcT96kAygRMJ1BV1RrAg5nvbT+7GaCJi8gJwBPAiplvvxAmAO/9hoiOAJyIRCLahhAufwF8fLkjIhGRfQjh8JcjGBtvUGpHkBJXA6TGVQA58WxAbjwLoBFPBmjFkwCa8dEA7fgogEV8MMAqPghgGe8FWMc7Ad8r1Tm3Y+az9i26dRl57xcArkQ061upOajObVjX9TLGOG+7TOSE3++qreNUTAGUCUw+gRcF0dchsIjssQAAAABJRU5ErkJggg==', 4 | round:'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAACpUlEQVRYR+2WPW8TQRCG39mLqUBQuKQiCeJDSiT4DRCFwtk964KQ6BCWoCUfonMJOD1gRAkCjG/ODUmB+AcgpQECCVW6NBFQYecGbbSHEhPfnZ0ijbezduedx7PvzhzhiBcdcX4MAYYV6KsCQRAc29nZMSIyA+ASEZ22JhaRTQCfRCQqFApRo9H4k9fcuQG01mUiegTgTIb4BoAFZg7zQGQCBEHgtdvth0R0r0twTUQ2iMhqjAI427W/NDk5uVitVuM0kEwArfXSnuQdInpGREvNZvPHXuFSqTQ2MjIyLyK3AHhur8bMCwMDuLK/dQLbAMrM/CFN0BhzBUADwEnnDxNFUdQrpmcFrOE6nc4Xd+cdAFNZyZMkWuurRPTOVoKI1j3Pu9jLmD0BfN+/LiKvrCgRPQ7D8G4eUyVnjDF1ALfd71lmtlX5b/UEMMa8BHDDRiilRrvvPAumXC6Px3H8zZ17wcw3+wVYs86O4/hrq9U6n5XwoH1jjAUYB7DGzOf6BfgF4DiAZWa+NgiA1nqFiKYA/GbmE4MCrDDz9CEBfjLz7qvoXmke2L2CtPJlQRljMjVymVBExqIosi0297KNyfO87wObUGs9S0SvncATZr6TOzsA3/efikjFPeMgDMOkoe2TyWpEn12f7yilppvN5vs8EK4bLieNqFgsXqjX6+2+PGAPG2N8AE0XuK2UCrIgXPI3AE65OM3MrV7gmcPIGFMDMOcEbEt+LiK1bk8cNIzs+A7DcDGtapkA1WpVra6uPgAw3yVkm4w1ZjKObcP5t2zyiYmJ+4cex4mi1lorpWr2RaT+I6J1EZlLK/s+0DymSs5UKpXC1taWBjATx/FlpdTuJ1kcx5tKqY9EFBWLxVYvw/Vtwn7gBj2b6YFBhfPGDQGGFfgLW7AHMIfxtKYAAAAASUVORK5CYII=', 5 | trangle:'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAl0lEQVRYR+2WsQ2DMBBF/w1BaLIIW/gKGJIU31uwCJUzhFGKVIAEjiUr0nd/95+f5DsbGh9rnA8ByIAMnBoIISQz62o805zzO8b4OOp1CuDuuUb4twfJw6wrACuApRBmAPD81P4C8CI5lQC4+wxgFIAMyIAM/L2B5qO4ZArvam7vgprrGEAi2d9ax1WufaGJvmQyIAPNDWzonXsh3WqxKQAAAABJRU5ErkJggg==', 6 | eraser:'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAACaElEQVRYR+2Uv4sTQRTHv28ymKBYqAg2WolYJYKCWByiicGoIRvDdlcE4x9gb5PCWtDK7mxOi3Uzk6RT8VrxB/7qD1s5jyOgiLi7TwY2sIb8mM0WB3LTznvv+5nve/MIu3xol/WxB/B/ONButwuj0egOgGNCiIe+72/azlZmB2q1Wr5QKPgArhtRZt5i5nK/3/9iA5EJYFJ8LJgGYmmAWeJJiFwuV/F9//M8J5YCWCSeENxh5ita6/ezIFIDuK67LwgC0/MbNj0GsCOEqPq+/25afCqAeNoVgKuW4uOwbWauaK0/TuZZA2QQH2tuKqVOmo+ShLACiHtuXn4+Tj6c0gETHkopD3qe9ysVgBHP5/M9IrpARJX4r78AkBbigVLKLKt/zlwHzMCFYfiMmVeIqNrr9d6abMdxzhDRSwBHLJ1YK5VKt7vdbmQNEE+7B+AiM1e11m+SySkgZoqbelMdWCQ+BrGAmCs+E8BxnHUiupa0fZbVjuOcJSIzE4cmYhaKTwWo1+v7pZQ/AHSUUms2PW61WueiKHo+hiCix8VisTOt51Yz0Gw2PwEQUsrLnudt2UA0m827AO6lEZ83AyeCINgA8DsMw0uDweDbPAjHcVoAnhKRL6Vc9TwvtIGeCWAuXNc1EK8A/AyCoDwcDr9PK5pFfC6AuWw0GseFEMYJMxNlpdT2xFdc+uXjOgtXcezEBjOPANzUWn+Nl1GHiB4BeCKlvJXG9lSrOOGEBnA6iqIPQoijAE4x8x8iGpo9b9Hz10qp+1a/YFqxeDmtEtEKMx+wEJwMyQawhKBVysIZsKqSIWgPYNcd+AsPkxIwA3VPaAAAAABJRU5ErkJggg==', 7 | text:'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAA5klEQVRYR+2VPwrCMBSHfwlFvIizq6u7y0vR2Qt4A0dP4AmcLSUUhI6Ozs5epA5NpKBQ1Ni0tAThZUxe8r735Z9A4CYC5wcDsAGngTiOJ8aYRR+HVEp5StP09m0tJwARLQEc+wAAsNJaJ38NsDXGnOsVSCnnAHbPvqbxTgam1tp1lUAIcdBaX+sAb1v0kYCIfs5/rdX5GjYB+J4dBmADbIANsIHWBpRSs7IspeszyrLsAsAO9hQT0R3AyJWgKIpxnudVjFdrbSA4gFJqY62NXOVFUbRPkqT0Kr/66n0Dh4pjADYQ3MADakKSIQVRq+oAAAAASUVORK5CYII=' 8 | } 9 | export default cursors; -------------------------------------------------------------------------------- /src/components/imgDraw/imgDraw.vue: -------------------------------------------------------------------------------- 1 | 164 | 165 | 632 | 633 | 634 | 910 | -------------------------------------------------------------------------------- /src/main.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import App from './App.vue' 3 | import './plugins/element.js' 4 | import './assets/icon/iconfont.js' 5 | Vue.config.productionTip = false 6 | // init commit 7 | new Vue({ 8 | render: h => h(App), 9 | }).$mount('#app') 10 | -------------------------------------------------------------------------------- /src/plugins/element.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import Element from 'element-ui' 3 | import 'element-ui/lib/theme-chalk/index.css' 4 | 5 | Vue.use(Element) 6 | -------------------------------------------------------------------------------- /vue.config.js: -------------------------------------------------------------------------------- 1 | const env = process.env.NODE_ENV; 2 | module.exports = { 3 | devServer: { 4 | port: 8080, 5 | open: true, 6 | }, 7 | publicPath: env === "production" ? "./" : "", 8 | indexPath: "index.html", 9 | outputDir: "docs", 10 | assetsDir: "", 11 | }; 12 | --------------------------------------------------------------------------------