imgDraw.vue 32 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112
  1. <template>
  2. <div class="imgDraw">
  3. <a href="javascript:void(0);" ref="download" download="picture.png" v-show="false"></a>
  4. <el-button type="primary" @click="handleShowCanvas" v-show="false">showCanvas</el-button>
  5. <el-dialog destroy-on-close :close-on-click-modal="false" :close-on-press-escape="false" :show-close="false"
  6. :visible.sync="show" fullscreen>
  7. <!-- <p slot="title" class="title">Vue-ImagePainter🎨</p> -->
  8. <div class="d_body">
  9. <div class="board" ref="board">
  10. <img id="img1" ref="img1" v-bind:src="baseMap" hidden="hidden" />
  11. <canvas id="ctx_front" ref="ctx_front" :style="'cursor:' + cursor"></canvas>
  12. <canvas id="ctx_base" ref="ctx_base" :style="'cursor:' + cursor"></canvas>
  13. <canvas id="ctx_back" ref="ctx_back" :style="'cursor:' + cursor"></canvas>
  14. <input name="text" id="text" @blur="handleTextBlur" @keyup.enter="handleTextBlur" v-model="text" autofocus
  15. autocomplete="off" :style="
  16. 'font-size:' + (this.slide * 10 + 14) + 'px;color:' + defaultColor
  17. " />
  18. </div>
  19. <div :class="['tools', 'settings', isExpand ? '' : 'noExpand']">
  20. <div class="tool_item" v-for="(item, index) in settings" :key="item.icon">
  21. <button v-if="index == 0">
  22. <svg class="icon" aria-hidden="true" :style="'color:' + defaultColor">
  23. <use :xlink:href="item.icon" />
  24. </svg>
  25. <span :style="'color:' + defaultColor">{{ item.name }}</span>
  26. <el-color-picker v-model="defaultColor"></el-color-picker>
  27. </button>
  28. <div v-else-if="index == 1" class="slide">
  29. <svg class="icon" aria-hidden="true">
  30. <use :xlink:href="item.icon" />
  31. </svg>
  32. <span>{{ item.name }}</span>&emsp;&emsp;
  33. <el-slider ref="slide" v-model="slide" :min="1" :max="100" :step="1" style="width: 70px"></el-slider>
  34. </div>
  35. <button v-else @click.stop="item.fun">
  36. <svg class="icon" aria-hidden="true">
  37. <use :xlink:href="item.icon" />
  38. </svg>
  39. <span>{{ item.name }}</span>
  40. </button>
  41. </div>
  42. <div class="tool_item" v-for="item in btns" :key="item.icon">
  43. <button @click.stop="item.fun" v-if="item.name == '上一步'" :disabled="prevDis"
  44. :style="prevDis ? 'cursor:not-allowed' : ''">
  45. <svg class="icon" aria-hidden="true">
  46. <use :xlink:href="item.icon" />
  47. </svg>
  48. <span>{{ item.name }}</span>
  49. </button>
  50. <button @click.stop="item.fun" v-else-if="item.name == '下一步'" :disabled="nextDis"
  51. :style="nextDis ? 'cursor:not-allowed' : ''">
  52. <svg class="icon" aria-hidden="true">
  53. <use :xlink:href="item.icon" />
  54. </svg>
  55. <span>{{ item.name }}</span>
  56. </button>
  57. <button @click.stop="item.fun" v-else>
  58. <svg class="icon" aria-hidden="true" v-if="item.icon">
  59. <use :xlink:href="item.icon" />
  60. </svg>
  61. <span>{{ item.name }}</span>
  62. </button>
  63. </div>
  64. <div class="tool_item go_up">
  65. <button @click.stop="handleShowOrHide(0)">
  66. <i class="el-icon-caret-top"></i>
  67. <span>收起</span>
  68. </button>
  69. </div>
  70. <div class="pull" v-if="!isExpand">
  71. <span class="line"></span>
  72. <span class="round" title="展开" @click.stop="handleShowOrHide(1)"></span>
  73. </div>
  74. </div>
  75. <div :class="['tools', 'bars', showTools ? '' : 'hideTools']">
  76. <div class="el-icon-s-tools arrow" v-if="!showTools" title="展开" @click.stop="handleShowTools(1)"></div>
  77. <div class="el-icon-arrow-right arrow" v-else title="收起" @click.stop="handleShowTools(0)"></div>
  78. <div :class="[
  79. 'tool_item',
  80. activeTool == item.toolType ? 'activeTool' : '',
  81. ]" v-for="item in tools" :key="item.toolType" @click.stop="handleChangeToolType(item.toolType)">
  82. <svg class="icon" aria-hidden="true">
  83. <use :xlink:href="item.icon" />
  84. </svg>
  85. <span>{{ item.name }}</span>
  86. </div>
  87. </div>
  88. </div>
  89. </el-dialog>
  90. </div>
  91. </template>
  92. <script>
  93. // eslint-disable-next-line no-unused-vars
  94. import cursors from "./cursor";
  95. import "../../../assets/drawIcon/iconfont";
  96. import bgA from "./img";
  97. export default {
  98. name: "imgDraw",
  99. props: {
  100. drawShow: {
  101. type: Boolean,
  102. default: false,
  103. },
  104. bg: {
  105. type: String,
  106. },
  107. closeDraw: {
  108. type: Function,
  109. },
  110. },
  111. data() {
  112. return {
  113. show: true,
  114. defaultColor: "#333333",
  115. cursor: `url('${cursors.pen}'),auto`,
  116. slide: 1,
  117. settings: [
  118. {
  119. icon: "#icon-youqitong_huaban1",
  120. name: "颜色",
  121. fun: "",
  122. },
  123. {
  124. icon: "#icon-huabi_huaban1",
  125. name: "粗细",
  126. fun: "",
  127. },
  128. {
  129. icon: "#icon-fangda_huaban1",
  130. name: "放大",
  131. fun: () => {
  132. return this.handleBeLarge();
  133. },
  134. },
  135. {
  136. icon: "#icon-suoxiao_huaban1",
  137. name: "缩小",
  138. fun: () => {
  139. return this.handleBeSmall();
  140. },
  141. },
  142. ],
  143. activeTool: 1,
  144. tools: [
  145. {
  146. icon: "#icon-huabi_huaban1",
  147. name: "画笔",
  148. toolType: 1,
  149. },
  150. // {
  151. // icon: "#icon-zhixian_huaban1",
  152. // name: "直线",
  153. // toolType: 2,
  154. // },
  155. // {
  156. // icon: "#icon-yuanquan_huaban1",
  157. // name: "圆形",
  158. // toolType: 3,
  159. // },
  160. // {
  161. // icon: "#icon-juxinggongju_huaban1",
  162. // name: "矩形",
  163. // toolType: 4,
  164. // },
  165. // {
  166. // icon: "#icon-xiangpi_huaban1",
  167. // name: "橡皮",
  168. // toolType: 5,
  169. // },
  170. {
  171. icon: "#icon-wenzi_huaban1",
  172. name: "文字",
  173. toolType: 6,
  174. },
  175. ],
  176. btns: [
  177. {
  178. icon: "#icon-chexiao",
  179. name: "上一步",
  180. fun: () => {
  181. return this.handlePrev();
  182. },
  183. },
  184. {
  185. icon: "#icon-zhongzuo",
  186. name: "下一步",
  187. fun: () => {
  188. return this.handleNext();
  189. },
  190. },
  191. {
  192. icon: "#icon-lajixiang_huaban1",
  193. name: "清除",
  194. fun: () => {
  195. return this.handleClearCanvas();
  196. },
  197. },
  198. {
  199. // icon: "#icon-baocun",
  200. icon: "",
  201. name: "关闭",
  202. fun: () => {
  203. // return this.handleCanvas2Img();
  204. // return (this.show = false);
  205. return this.$emit("closeDraw");
  206. },
  207. },
  208. {
  209. icon: "",
  210. name: "保存批注",
  211. fun: () => {
  212. return this.handleSave();
  213. },
  214. },
  215. ],
  216. canvas_front: null,
  217. canvas_back: null,
  218. canvas_base: null,
  219. ctx_base: null,
  220. ctx_front: null,
  221. ctx_back: null,
  222. currentImg: {
  223. url: "",
  224. width: "",
  225. height: "",
  226. scale: 1,
  227. index: 0,
  228. },
  229. isExpand: 1,
  230. showTools: 1,
  231. canDraw: false,
  232. text: "",
  233. canvasStore: [""],
  234. prevDis: true,
  235. nextDis: true,
  236. baseMap: "",
  237. tl: 0,
  238. tt: 0,
  239. };
  240. },
  241. methods: {
  242. /** 显示或隐藏设置栏*/
  243. handleShowOrHide(status) {
  244. this.isExpand = status;
  245. },
  246. /** 显示或隐藏工具栏*/
  247. handleShowTools(status) {
  248. this.showTools = status;
  249. },
  250. handleShowCanvas() {
  251. this.show = true;
  252. },
  253. /** 工具切换*/
  254. handleChangeToolType(type) {
  255. this.activeTool = type;
  256. switch (type) {
  257. case 0:
  258. this.cursor = `pointer`;
  259. break;
  260. case 1:
  261. this.cursor = `url('${cursors.pen}'),auto`;
  262. break;
  263. case 2:
  264. this.cursor = `crosshair`;
  265. break;
  266. case 3:
  267. this.cursor = `crosshair`;
  268. break;
  269. case 4:
  270. this.cursor = `crosshair`;
  271. break;
  272. case 5:
  273. this.cursor = `url('${cursors.eraser}'),auto`;
  274. break;
  275. case 6:
  276. this.cursor = `url('${cursors.text}'),auto`;
  277. break;
  278. default:
  279. this.cursor = `url('${cursors.pen}'),auto`;
  280. break;
  281. }
  282. this.handleDrawCanvas(type);
  283. },
  284. /** 初始化画布*/
  285. handleInitCanvas() {
  286. this.currentImg = {
  287. url: this.bg || bgA,
  288. width: "",
  289. height: "",
  290. scale: 1,
  291. index: 0,
  292. };
  293. this.canvasStore = [this.bg || bgA];
  294. this.prevDis = true;
  295. this.nextDis = true;
  296. this.baseMap = this.bg || bgA;
  297. // 用于绘制的画板
  298. this.canvas_front = document.getElementById("ctx_front");
  299. // 用于生成绘制后图片的画板
  300. this.canvas_back = document.getElementById("ctx_back");
  301. // 底图画板,橡皮擦除时获取像素放到绘制画板中,达到不擦出底图的效果
  302. this.canvas_base = document.getElementById("ctx_base");
  303. this.ctx_base = this.canvas_base.getContext("2d");
  304. this.ctx_front = this.canvas_front.getContext("2d");
  305. this.ctx_back = this.canvas_back.getContext("2d");
  306. this.ctx_front.strokeStyle = this.defaultColor;
  307. let img = new Image();
  308. img.src = this.baseMap;
  309. img.crossOrigin = "";
  310. let _this = this;
  311. img.onload = function () {
  312. let _wWith = window.screen.availWidth - 100
  313. let _width = parseInt(this.width);
  314. let _height = parseInt(this.height);
  315. let width = _wWith
  316. let height = _height / _width * width
  317. _this.currentImg.width = width;
  318. _this.currentImg.height = height;
  319. _this.canvas_front.width = width;
  320. _this.canvas_front.height = height;
  321. _this.canvas_back.width = width;
  322. _this.canvas_back.height = height;
  323. _this.canvas_base.width = width;
  324. _this.canvas_base.height = height;
  325. _this.ctx_front.drawImage(this, 0, 0, width, height);
  326. _this.ctx_back.drawImage(this, 0, 0, width, height);
  327. _this.ctx_base.drawImage(this, 0, 0, width, height);
  328. };
  329. },
  330. /** 处理放大缩小*/
  331. handleDrawImage() {
  332. let _this = this;
  333. let img = new Image();
  334. let baseImg = new Image();
  335. img.src = this.currentImg.url;
  336. baseImg.src = this.baseMap;
  337. _this.currentImg.width = _this.currentImg.width * this.currentImg.scale;
  338. _this.currentImg.height = _this.currentImg.height * this.currentImg.scale;
  339. img.onload = function () {
  340. _this.canvas_front.width = _this.currentImg.width;
  341. _this.canvas_front.height = _this.currentImg.height;
  342. _this.canvas_back.width = _this.currentImg.width;
  343. _this.canvas_back.height = _this.currentImg.height;
  344. _this.ctx_front.drawImage(
  345. this,
  346. 0,
  347. 0,
  348. _this.currentImg.width,
  349. _this.currentImg.height
  350. );
  351. _this.ctx_back.drawImage(
  352. this,
  353. 0,
  354. 0,
  355. _this.currentImg.width,
  356. _this.currentImg.height
  357. );
  358. };
  359. baseImg.onload = () => {
  360. _this.canvas_base.width = _this.currentImg.width;
  361. _this.canvas_base.height = _this.currentImg.height;
  362. _this.ctx_base.drawImage(
  363. baseImg,
  364. 0,
  365. 0,
  366. _this.currentImg.width,
  367. _this.currentImg.height
  368. );
  369. };
  370. },
  371. handleBeLarge() {
  372. this.currentImg.scale = 1;
  373. this.currentImg.scale += 0.1;
  374. this.$nextTick(() => {
  375. this.handleDrawImage();
  376. });
  377. },
  378. handleBeSmall() {
  379. this.currentImg.scale = 1;
  380. this.currentImg.scale -= 0.1;
  381. this.$nextTick(() => {
  382. this.handleDrawImage();
  383. });
  384. },
  385. /** 下载图片*/
  386. handleCanvas2Img() {
  387. let canvas = document.getElementById("ctx_back");
  388. this.$refs.download.href = canvas.toDataURL();
  389. this.$refs.download.click();
  390. },
  391. /** 清除画布*/
  392. handleClearCanvas() {
  393. this.handleInitCanvas();
  394. },
  395. handleFrommatCanvas() {
  396. this.ctx_front.clearRect(
  397. 0,
  398. 0,
  399. this.canvas_front.width,
  400. this.canvas_front.height
  401. );
  402. },
  403. handleDrawCanvas(type) {
  404. // type
  405. // const type = this.activeTool
  406. this.canDraw = false;
  407. let sx, sy, mx, my;
  408. let text = document.getElementById("text");
  409. //鼠标按下
  410. let mousedown = (e) => {
  411. this.ctx_front.strokeStyle = this.defaultColor;
  412. this.ctx_front.lineWidth = this.slide;
  413. e = e || window.event;
  414. sx = e.clientX - this.canvas_front.offsetLeft;
  415. sy = e.clientY - this.canvas_front.offsetTop;
  416. const cbx = this.ctx_base.getImageData(
  417. (e.offsetX || e.pageX) - this.slide / 2,
  418. (e.offsetY || e.pageY) - this.slide / 2,
  419. this.slide * 2,
  420. this.slide * 2
  421. );
  422. this.ctx_front.moveTo(sx, sy);
  423. this.canDraw = true;
  424. switch (type) {
  425. case 1:
  426. this.ctx_front.beginPath();
  427. break;
  428. case 5:
  429. this.ctx_front.putImageData(
  430. cbx,
  431. (e.offsetX || e.pageX) - this.slide / 2,
  432. (e.offsetY || e.pageY) - this.slide / 2
  433. );
  434. break;
  435. case 6:
  436. this.handleTextBlur();
  437. this.text = "";
  438. text.style.fontSize = 14 + this.slide * 10 + "px";
  439. text.style.color = this.defaultColor;
  440. text.style.left =
  441. (e.offsetX || e.pageX) + this.canvas_front.offsetLeft - 20 + "px";
  442. text.style.top =
  443. (e.offsetY || e.pageY) + this.canvas_front.offsetTop - 10 + "px";
  444. text.style.zIndex = 10;
  445. text.style.display = "block";
  446. this.tl = (e.offsetX || e.pageX) - 20;
  447. this.tt = (e.offsetY || e.pageY) + 10;
  448. break;
  449. }
  450. };
  451. let mousemove = (e) => {
  452. e = e || window.event;
  453. var scrollL =
  454. document.getElementsByClassName("d_body")[0].scrollLeft ||
  455. document.getElementsByClassName("d_body")[0].scrollLeft;
  456. var scrollT =
  457. document.getElementsByClassName("d_body")[0].scrollTop ||
  458. document.getElementsByClassName("d_body")[0].scrollTop;
  459. mx = e.clientX - this.canvas_front.offsetLeft + scrollL;
  460. my = e.clientY - this.canvas_front.offsetTop + scrollT;
  461. const cbx = this.ctx_base.getImageData(
  462. (e.offsetX || e.pageX) - this.slide / 2,
  463. (e.offsetY || e.pageY) - this.slide / 2,
  464. this.slide * 2,
  465. this.slide * 2
  466. );
  467. if (this.canDraw) {
  468. switch (type) {
  469. case 1:
  470. this.ctx_front.lineTo(mx - 8, my);
  471. this.ctx_front.stroke();
  472. break;
  473. case 2:
  474. this.handleFrommatCanvas();
  475. this.ctx_front.beginPath();
  476. this.ctx_front.moveTo(sx - 10, sy - 20);
  477. this.ctx_front.lineTo(mx - 10, my - 20);
  478. this.ctx_front.stroke();
  479. break;
  480. case 3:
  481. this.handleFrommatCanvas();
  482. this.ctx_front.beginPath();
  483. // eslint-disable-next-line no-case-declarations
  484. let rds = Math.sqrt(
  485. (sx - 10 - mx) * (sx - 10 - mx) +
  486. (sy - 49 - my) * (sy - 49 - my)
  487. );
  488. this.ctx_front.arc(sx - 15, sy - 69, rds, 0, Math.PI * 2, false);
  489. this.ctx_front.stroke();
  490. break;
  491. case 4:
  492. this.handleFrommatCanvas();
  493. this.ctx_front.beginPath();
  494. this.ctx_front.moveTo(sx - 10, sy - 79);
  495. this.ctx_front.lineTo(mx - 10, sy - 79);
  496. this.ctx_front.lineTo(mx - 10, my - 79);
  497. this.ctx_front.lineTo(sx - 10, my - 79);
  498. this.ctx_front.lineTo(sx - 10, sy - 79);
  499. this.ctx_front.stroke();
  500. break;
  501. case 5:
  502. this.ctx_front.putImageData(
  503. cbx,
  504. (e.offsetX || e.pageX) - this.slide + 20,
  505. (e.offsetY || e.pageY) - this.slide + 20
  506. );
  507. break;
  508. }
  509. }
  510. };
  511. let mouseup = () => {
  512. if (this.canDraw) {
  513. this.canDraw = false;
  514. this.ctx_front.closePath();
  515. if (type != 6) {
  516. this.handleSaveCanvasStore();
  517. }
  518. }
  519. };
  520. this.canvas_front.onmousedown = (e) => mousedown(e);
  521. this.canvas_front.onmousemove = (e) => mousemove(e);
  522. this.canvas_front.onmouseup = (e) => mouseup(e);
  523. this.canvas_front.onmouseout = (e) => mouseup(e);
  524. this.canvas_front.onmouseleave = (e) => mouseup(e);
  525. const newHandlemousedown = function (e) {
  526. mousedown(e.touches[0])
  527. }
  528. const newHandlemousemove = function (e) {
  529. mousemove(e.touches[0])
  530. }
  531. const newHandlemouseup = function (e) {
  532. mouseup(e.touches[0])
  533. }
  534. // this.canvas_front.removeEventListener("touchstart",newHandlemousedown,true);
  535. // this.canvas_front.removeEventListener("touchmove",newHandlemousemove,true);
  536. // this.canvas_front.removeEventListener("touchend",newHandlemouseup,true);
  537. this.canvas_front.ontouchstart = newHandlemousedown
  538. this.canvas_front.ontouchmove = newHandlemousemove
  539. this.canvas_front.ontouchend = newHandlemouseup
  540. // this.canvas_front.addEventListener("touchstart",newHandlemousedown,false);
  541. // this.canvas_front.addEventListener("touchmove",newHandlemousemove,false);
  542. // this.canvas_front.addEventListener("touchend",newHandlemouseup,false);
  543. // this.canvas_front.ontouchstart = (e) => mousedown(e);
  544. // this.canvas_front.ontouchmove = (e) => mousemove(e);
  545. // this.canvas_front.ontouchend = (e) => mouseup(e);
  546. },
  547. /** 失焦或者回车绘制文本,框隐藏*/
  548. handleTextBlur() {
  549. let text = document.getElementById("text");
  550. this.ctx_front.font = `300 ${text.style.fontSize} sans-serif`;
  551. this.ctx_front.fillStyle = this.defaultColor;
  552. this.ctx_front.fillText(this.text, this.tl, this.tt);
  553. text.style.display = "none";
  554. this.text = "";
  555. text.value = "";
  556. this.handleSaveCanvasStore();
  557. },
  558. /** 上一步*/
  559. handlePrev() {
  560. if (this.currentImg.index > 0) {
  561. this.nextDis = false;
  562. this.currentImg.index -= 1;
  563. this.currentImg.index == 0
  564. ? (this.prevDis = true)
  565. : (this.prevDis = false);
  566. this.currentImg.url = this.canvasStore[this.currentImg.index];
  567. this.currentImg.scale = 1;
  568. this.handleDrawImage();
  569. } else {
  570. this.prevDis = true;
  571. }
  572. },
  573. /** 下一步*/
  574. handleNext() {
  575. if (this.currentImg.index < this.canvasStore.length - 1) {
  576. this.prevDis = false;
  577. this.currentImg.index += 1;
  578. this.currentImg.index == this.canvasStore.length - 1
  579. ? (this.nextDis = true)
  580. : (this.nextDis = false);
  581. this.currentImg.url = this.canvasStore[this.currentImg.index];
  582. this.currentImg.scale = 1;
  583. this.handleDrawImage();
  584. } else {
  585. this.nextDis = true;
  586. }
  587. },
  588. /** 保存绘制*/
  589. handleSaveCanvasStore() {
  590. let url = this.canvas_front.toDataURL();
  591. let image = new Image();
  592. image.src = url;
  593. image.onload = () => {
  594. this.ctx_front.clearRect(
  595. 0,
  596. 0,
  597. this.canvas_front.width,
  598. this.canvas_front.height
  599. );
  600. this.ctx_front.drawImage(image, 0, 0, image.width, image.height);
  601. this.ctx_back.drawImage(image, 0, 0, image.width, image.height);
  602. const url2 = this.canvas_back.toDataURL();
  603. this.currentImg.url = url2;
  604. this.currentImg.index += 1;
  605. this.canvasStore.push(url2);
  606. this.prevDis = false;
  607. console.log(this.canvasStore);
  608. };
  609. },
  610. // 将网络图片转换成base64格式
  611. transBase64FromImage(image) {
  612. let canvas = document.createElement("canvas");
  613. canvas.width = image.width;
  614. canvas.height = image.height;
  615. let ctx = canvas.getContext("2d");
  616. ctx.drawImage(image, 0, 0, image.width, image.height);
  617. // 可选其他值 image/jpeg
  618. return canvas.toDataURL("image/jpeg");
  619. },
  620. // 设置需要展示的图片 base64
  621. setAvatarBase64(src, callback) {
  622. let _this = this;
  623. let image = new Image();
  624. // 处理缓存
  625. // image.src = src + "?v=" + Math.random();
  626. image.src = src;
  627. // 支持跨域图片
  628. image.crossOrigin = "*";
  629. image.onload = function () {
  630. let base64 = _this.transBase64FromImage(image);
  631. // callback && callback(base64);
  632. console.log(base64);
  633. // return base64
  634. };
  635. },
  636. handleSave() {
  637. let url = this.canvas_back.toDataURL();
  638. console.log(url);
  639. this.$emit("addImgDraw", url);
  640. },
  641. //双指
  642. doubleFin() {
  643. // this.$nextTick()将回调延迟到下次 DOM 更新循环之后执行
  644. let that = this;
  645. this.$nextTick(() => {
  646. // 获取放大或缩小的区域DOM
  647. let matrix_box = document.querySelector(".board");
  648. matrix_box.addEventListener("touchstart", function (event) {
  649. var touches = event.touches;
  650. var events = touches[0];
  651. var events2 = touches[1];
  652. // event.preventDefault();
  653. // 第一个触摸点的坐标
  654. that.displacement.pageX = events.pageX;
  655. that.displacement.pageY = events.pageY;
  656. that.displacement.moveable = true;
  657. if (events2) {
  658. that.displacement.pageX2 = events2.pageX;
  659. that.displacement.pageY2 = events2.pageY;
  660. }
  661. that.displacement.originScale = that.displacement.scale || 1;
  662. // console.log(that.displacement);
  663. });
  664. document.addEventListener("touchmove", function (event) {
  665. if (!that.displacement.moveable) {
  666. return;
  667. }
  668. event.preventDefault();
  669. var touches = event.touches;
  670. var events = touches[0];
  671. var events2 = touches[1];
  672. // 双指移动
  673. if (events2) {
  674. // 第2个指头坐标在touchmove时候获取
  675. if (!that.displacement.pageX2) {
  676. that.displacement.pageX2 = events2.pageX;
  677. }
  678. if (!that.displacement.pageY2) {
  679. that.displacement.pageY2 = events2.pageY;
  680. }
  681. // 双指缩放比例计算
  682. var zoom =
  683. that.getDistance(
  684. {
  685. x: events.pageX,
  686. y: events.pageY
  687. },
  688. {
  689. x: events2.pageX,
  690. y: events2.pageY
  691. }
  692. ) /
  693. that.getDistance(
  694. {
  695. x: that.displacement.pageX,
  696. y: that.displacement.pageY
  697. },
  698. {
  699. x: that.displacement.pageX2,
  700. y: that.displacement.pageY2
  701. }
  702. );
  703. // 应用在元素上的缩放比例
  704. var newScale = that.displacement.originScale * zoom;
  705. // 最大缩放比例限制
  706. // if (newScale > 1) {
  707. // newScale = 1;
  708. // }
  709. // 记住使用的缩放值
  710. that.displacement.scale = newScale;
  711. this.currentImg.scale = newScale
  712. this.handleDrawImage()
  713. // 图像应用缩放效果
  714. // console.log(newScale);
  715. // matrix_box.style.transform = "scale(" + newScale + ")";
  716. // 设置旋转元素的基点位置
  717. // matrix_box.style.transformOrigin = "0px 0px 0px";
  718. }
  719. });
  720. });
  721. }
  722. },
  723. mounted() {
  724. this.$nextTick(() => {
  725. this.show = this.drawShow;
  726. this.handleInitCanvas();
  727. this.handleChangeToolType(1);
  728. // this.doubleFin();
  729. });
  730. },
  731. watch: {
  732. drawShow(val) {
  733. this.show = val;
  734. this.ctx_base = null;
  735. this.ctx_front = null;
  736. this.ctx_back = null;
  737. this.handleInitCanvas();
  738. if (this.$equipment()) {
  739. this.handleChangeToolType(0);
  740. this.tools = [
  741. {
  742. icon: "#icon-zhixian_huaban1",
  743. name: "移动",
  744. toolType: 0,
  745. }, {
  746. icon: "#icon-huabi_huaban1",
  747. name: "画笔",
  748. toolType: 1,
  749. },
  750. {
  751. icon: "#icon-wenzi_huaban1",
  752. name: "文字",
  753. toolType: 6,
  754. },
  755. ]
  756. } else {
  757. this.handleChangeToolType(1);
  758. this.tools = [{
  759. icon: "#icon-huabi_huaban1",
  760. name: "画笔",
  761. toolType: 1,
  762. },
  763. {
  764. icon: "#icon-wenzi_huaban1",
  765. name: "文字",
  766. toolType: 6,
  767. },
  768. ]
  769. }
  770. },
  771. },
  772. };
  773. </script>
  774. <!-- Add "scoped" attribute to limit CSS to this component only -->
  775. <style scoped lang="css">
  776. * {
  777. margin: 0;
  778. padding: 0;
  779. box-sizing: border-box;
  780. }
  781. .icon {
  782. width: 1em;
  783. height: 1em;
  784. vertical-align: -0.15em;
  785. fill: currentColor;
  786. overflow: hidden;
  787. }
  788. .imgDraw>>>.el-dialog {
  789. display: flex;
  790. flex-direction: column;
  791. align-items: center;
  792. }
  793. .imgDraw>>>.el-dialog>div {
  794. width: 100%;
  795. box-sizing: border-box;
  796. }
  797. .imgDraw>>>.el-dialog .el-dialog__header {
  798. padding: 0 20px;
  799. }
  800. .imgDraw>>>.el-dialog .el-dialog__header p {
  801. padding: 20px 0;
  802. border-bottom: 1px solid #bdbdbd;
  803. }
  804. .imgDraw>>>.el-dialog .el-dialog__body {
  805. padding: 10px 20px;
  806. flex: 1;
  807. height: 0;
  808. padding-top: 0;
  809. position: relative;
  810. overflow: hidden;
  811. }
  812. .imgDraw>>>.el-dialog .el-dialog__body .d_body {
  813. height: 100%;
  814. overflow-y: auto;
  815. padding-top: 20px;
  816. }
  817. .imgDraw>>>.el-dialog .el-dialog__body .d_body .board {
  818. position: relative;
  819. min-height: 100%;
  820. }
  821. .imgDraw>>>.el-dialog .el-dialog__body .d_body .board canvas {
  822. position: absolute;
  823. margin: 0 auto;
  824. left: 0;
  825. right: 0;
  826. top: 0;
  827. }
  828. .imgDraw>>>.el-dialog .el-dialog__body .d_body .board #ctx_front {
  829. z-index: 5;
  830. }
  831. .imgDraw>>>.el-dialog .el-dialog__body .d_body .board #ctx_back {
  832. z-index: 3;
  833. }
  834. .imgDraw>>>.el-dialog .el-dialog__body .d_body .board #ctx_base {
  835. z-index: 1;
  836. }
  837. .imgDraw>>>.el-dialog .el-dialog__body .d_body .board #text {
  838. position: absolute;
  839. z-index: -1;
  840. resize: none;
  841. outline: none;
  842. border: 1px dashed #eeeeee;
  843. overflow: hidden;
  844. background: transparent;
  845. line-height: 30px;
  846. display: none;
  847. }
  848. .imgDraw>>>.el-dialog .el-dialog__body .d_body .tools {
  849. width: 100%;
  850. position: absolute;
  851. display: flex;
  852. z-index: 5;
  853. background: #ffffff;
  854. transition: all 0.2s ease-in-out;
  855. }
  856. .imgDraw>>>.el-dialog .el-dialog__body .d_body .settings {
  857. top: 0;
  858. left: 50%;
  859. transform: translateX(-50%);
  860. z-index: 10;
  861. padding: 5px 10px;
  862. border-bottom-left-radius: 4px;
  863. border-bottom-right-radius: 4px;
  864. border: 1px solid #eeeeee;
  865. border-top: 0;
  866. width: auto;
  867. }
  868. .imgDraw>>>.el-dialog .el-dialog__body .d_body .settings .tool_item {
  869. display: flex;
  870. align-items: center;
  871. flex-wrap: nowrap;
  872. }
  873. .imgDraw>>>.el-dialog .el-dialog__body .d_body .settings .tool_item:not(:last-of-type) {
  874. margin-right: 25px;
  875. }
  876. .imgDraw>>>.el-dialog .el-dialog__body .d_body .settings .go_up {
  877. margin-right: 0 !important;
  878. }
  879. .imgDraw>>>.el-dialog .el-dialog__body .d_body .settings button {
  880. display: flex;
  881. align-items: center;
  882. justify-content: center;
  883. padding: 5px 10px;
  884. background: #ffffff;
  885. border: 1px solid #eeeeee;
  886. outline: none;
  887. cursor: pointer;
  888. position: relative;
  889. flex-wrap: nowrap;
  890. }
  891. .imgDraw>>>.el-dialog .el-dialog__body .d_body .settings button svg {
  892. color: #333333;
  893. font-size: 18px;
  894. margin-right: 5px;
  895. }
  896. .imgDraw>>>.el-dialog .el-dialog__body .d_body .settings button span {
  897. white-space: nowrap;
  898. }
  899. .imgDraw>>>.el-dialog .el-dialog__body .d_body .settings button>>>.el-color-picker {
  900. position: absolute;
  901. left: 0;
  902. top: 0;
  903. width: 100%;
  904. height: 100%;
  905. }
  906. .imgDraw>>>.el-dialog .el-dialog__body .d_body .settings button>>>.el-color-picker .el-color-picker__trigger {
  907. width: 100%;
  908. height: 100%;
  909. opacity: 0;
  910. filter: alpha(opacity=0);
  911. }
  912. .imgDraw>>>.el-dialog .el-dialog__body .d_body .settings .slide {
  913. width: 150px;
  914. display: flex;
  915. flex-direction: row;
  916. flex-wrap: nowrap;
  917. align-items: center;
  918. }
  919. .imgDraw>>>.el-dialog .el-dialog__body .d_body .settings .slide svg {
  920. font-size: 18px;
  921. margin-right: 5px;
  922. }
  923. .imgDraw>>>.el-dialog .el-dialog__body .d_body .settings .slide>>>.el-slider {
  924. flex: 1;
  925. width: 0;
  926. margin-left: 10px;
  927. }
  928. .imgDraw>>>.el-dialog .el-dialog__body .d_body .settings .slide>>>.el-slider .el-slider__button-wrapper .el-slider__button {
  929. width: 12px;
  930. height: 12px;
  931. }
  932. .imgDraw>>>.el-dialog .el-dialog__body .d_body .settings .pull {
  933. position: absolute;
  934. right: 20px;
  935. bottom: -45px;
  936. display: flex;
  937. flex-direction: column;
  938. align-items: center;
  939. justify-content: flex-start;
  940. margin-right: 0;
  941. }
  942. .imgDraw>>>.el-dialog .el-dialog__body .d_body .settings .pull .line {
  943. width: 2px;
  944. height: 30px;
  945. background: dodgerblue;
  946. }
  947. .imgDraw>>>.el-dialog .el-dialog__body .d_body .settings .pull .round {
  948. width: 15px;
  949. height: 15px;
  950. border-radius: 50%;
  951. background: dodgerblue;
  952. cursor: pointer;
  953. }
  954. .imgDraw>>>.el-dialog .el-dialog__body .d_body .bars {
  955. top: 100px;
  956. right: 30px;
  957. z-index: 10;
  958. padding: 15px;
  959. border-top-left-radius: 4px;
  960. border-bottom-left-radius: 4px;
  961. border: 1px solid #eeeeee;
  962. width: auto;
  963. display: flex;
  964. flex-direction: column;
  965. }
  966. .imgDraw>>>.el-dialog .el-dialog__body .d_body .bars .tool_item {
  967. cursor: pointer;
  968. }
  969. .imgDraw>>>.el-dialog .el-dialog__body .d_body .bars .tool_item:not(:last-of-type) {
  970. margin-bottom: 15px;
  971. border-bottom: 1px solid #dddddd;
  972. padding-bottom: 10px;
  973. }
  974. .imgDraw>>>.el-dialog .el-dialog__body .d_body .bars .tool_item svg {
  975. font-size: 24px;
  976. margin-right: 8px;
  977. }
  978. .imgDraw>>>.el-dialog .el-dialog__body .d_body .bars .tool_item span {
  979. font-size: 18px;
  980. }
  981. .imgDraw>>>.el-dialog .el-dialog__body .d_body .bars .tool_item:hover svg {
  982. color: dodgerblue;
  983. }
  984. .imgDraw>>>.el-dialog .el-dialog__body .d_body .bars .tool_item:hover span {
  985. color: dodgerblue;
  986. }
  987. .imgDraw>>>.el-dialog .el-dialog__body .d_body .bars .activeTool {
  988. border-color: dodgerblue !important;
  989. }
  990. .imgDraw>>>.el-dialog .el-dialog__body .d_body .bars .activeTool svg {
  991. color: dodgerblue;
  992. }
  993. .imgDraw>>>.el-dialog .el-dialog__body .d_body .bars .activeTool span {
  994. color: dodgerblue;
  995. }
  996. .imgDraw>>>.el-dialog .el-dialog__body .d_body .bars .arrow {
  997. width: 20px;
  998. height: 20px;
  999. text-align: center;
  1000. line-height: 20px;
  1001. border-radius: 50%;
  1002. border: 1px solid #606266;
  1003. color: #606266;
  1004. position: absolute;
  1005. left: -10px;
  1006. background: #ffffff;
  1007. top: 50%;
  1008. transform: translateY(-50%);
  1009. cursor: pointer;
  1010. }
  1011. .imgDraw>>>.el-dialog .el-dialog__body .d_body .bars .el-icon-s-tools {
  1012. left: -30px;
  1013. width: 30px;
  1014. height: 30px;
  1015. line-height: 30px;
  1016. font-size: 20px;
  1017. color: dodgerblue;
  1018. border-color: dodgerblue;
  1019. }
  1020. .imgDraw>>>.el-dialog .el-dialog__body .d_body .bars .el-icon-arrow-right {
  1021. left: calc(100% - 10px);
  1022. }
  1023. .imgDraw>>>.el-dialog .el-dialog__body .d_body .hideTools {
  1024. right: -100px;
  1025. }
  1026. .imgDraw>>>.el-dialog .el-dialog__body .d_body .noExpand {
  1027. top: -50px;
  1028. }
  1029. .imgDraw>>>.el-dialog .el-dialog__footer {
  1030. text-align: center !important;
  1031. }
  1032. .imgDraw>>>.el-dialog .el-dialog__footer span button {
  1033. padding-left: 40px;
  1034. padding-right: 40px;
  1035. }
  1036. .imgDraw>>>.el-dialog .el-dialog__footer span button:first-of-type {
  1037. margin-right: 50px;
  1038. }
  1039. .imgDraw>>>.el-dialog .el-dialog__footer span button:last-of-type {
  1040. margin-left: 50px;
  1041. }
  1042. div {
  1043. user-select: none;
  1044. }
  1045. </style>