index.vue 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373
  1. <template>
  2. <div>
  3. <div class="tool-box" style="display: flex;justify-content: space-between;align-items: center;">
  4. <div>
  5. <p style="margin: 0;font-size: 13px;color: #FF6969;">点击绘制按钮开始绘制(黑边框内不可绘制,需在图片内绘制),单击鼠标左键即是当前点绘制完成可绘制下一个点, 双击鼠标左键即本次绘制完成。</p>
  6. </div>
  7. <div>
  8. <el-button
  9. size="mini"
  10. :type="isDrawing ? 'warning' : 'primary'"
  11. @click="startDraw"
  12. >绘制区域</el-button
  13. >
  14. <el-button
  15. size="mini"
  16. type="danger"
  17. :disabled="isDrawing"
  18. @click="clearAll"
  19. >全部清除</el-button
  20. >
  21. <el-button
  22. size="mini"
  23. type="success"
  24. :disabled="isDrawing"
  25. @click="savePoints"
  26. >保存</el-button
  27. >
  28. </div>
  29. </div>
  30. <div class="canvas-wrap">
  31. <canvas id="imgCanvas" ref="canvaxbox"></canvas>
  32. <!--用来和鼠标进行交互操作的canvas-->
  33. <canvas
  34. id="drawCanvas"
  35. ref="canvas"
  36. :style="{ cursor: isDrawing ? 'crosshair' : 'default' }"
  37. >
  38. </canvas>
  39. <!--存储已生成的点线,避免被清空-->
  40. <canvas id="saveCanvas" ref="canvasSave"></canvas>
  41. </div>
  42. </div>
  43. </template>
  44. <script>
  45. export default {
  46. data() {
  47. return {
  48. imgUrl: "",
  49. isDrawing: false, // 是否正在绘制
  50. ratio: 1,
  51. imgWidth: 3020,
  52. imgHeight: 1080,
  53. wrapWidth: 600,
  54. wrapHeight: 300,
  55. canvasWidth: 600,
  56. canvasHeight: 300,
  57. drawingPoints: [],
  58. drawedPoints: [],
  59. imgCanvas: null,
  60. imgCtx: null,
  61. drawCanvas: null,
  62. drawCtx: null,
  63. saveCanvas: null,
  64. saveCtx: null,
  65. submitData: [
  66. // {"polygon":{"x1":0,"y1":0,"x2":1920,"y2":0,"x3":1920,"y3":1080,"x4":0,"y4":1080}},
  67. // {
  68. // polygon: {
  69. // x1: 700,
  70. // y1: 273,
  71. // x2: 975,
  72. // y2: 278,
  73. // x3: 1107,
  74. // y3: 368,
  75. // x4: 718,
  76. // y4: 354,
  77. // },
  78. // },
  79. // {
  80. // polygon: {
  81. // x1: 49,
  82. // y1: 32,
  83. // x2: 183,
  84. // y2: 35,
  85. // x3: 181,
  86. // y3: 100,
  87. // x4: 55,
  88. // y4: 97,
  89. // },
  90. // },
  91. // {
  92. // polygon: {
  93. // x1: 433,
  94. // y1: 250,
  95. // x2: 706,
  96. // y2: 253,
  97. // x3: 707,
  98. // y3: 392,
  99. // x4: 435,
  100. // y4: 393,
  101. // },
  102. // },
  103. // {
  104. // polygon: {
  105. // x1: 45,
  106. // y1: 539,
  107. // x2: 193,
  108. // y2: 538,
  109. // x3: 192,
  110. // y3: 622,
  111. // x4: 41,
  112. // y4: 623,
  113. // x5: 42,
  114. // y5: 623,
  115. // },
  116. // },
  117. ],
  118. // [
  119. // {"polygon": {"x1":700,"y1":273,"x2":975,"y2":278,"x3":1107,"y3":368,"x4":718,"y4":354}}
  120. // ]
  121. };
  122. },
  123. created() {
  124. console.log(JSON.parse(this.$route.query.src))
  125. },
  126. mounted() {
  127. this.$nextTick(() => {
  128. this.initCanvas();
  129. this.getImage();
  130. });
  131. // setTimeout(() => {
  132. // }, 500);
  133. },
  134. methods: {
  135. initCanvas() {
  136. // 初始化canvas画布
  137. let canvasWrap = document.getElementsByClassName("canvas-wrap");
  138. this.wrapWidth = canvasWrap[0].clientWidth;
  139. this.wrapHeight = canvasWrap[0].clientHeight;
  140. this.imgCanvas = document.getElementById("imgCanvas");
  141. this.imgCtx = this.imgCanvas.getContext("2d");
  142. // 绘制canvas
  143. this.drawCanvas = document.getElementById("drawCanvas");
  144. this.drawCtx = this.drawCanvas.getContext("2d");
  145. // 保存绘制区域 saveCanvas
  146. this.saveCanvas = document.getElementById("saveCanvas");
  147. this.saveCtx = this.saveCanvas.getContext("2d");
  148. // this.initImgCanvas()
  149. },
  150. initImgCanvas() {
  151. // 计算宽高比
  152. let ww = this.wrapWidth; // 画布宽度
  153. let wh = this.wrapHeight; // 画布高度
  154. let iw = this.imgWidth; // 图片宽度
  155. let ih = this.imgHeight; // 图片高度
  156. if (iw / ih < ww / wh) {
  157. // 以高为主
  158. this.ratio = ih / wh;
  159. this.canvasHeight = wh;
  160. this.canvasWidth = (wh * iw) / ih;
  161. } else {
  162. // 以宽为主
  163. this.ratio = iw / ww;
  164. this.canvasWidth = ww;
  165. this.canvasHeight = (ww * ih) / iw;
  166. }
  167. // 初始化画布大小
  168. this.imgCanvas.width = this.canvasWidth;
  169. this.imgCanvas.height = this.canvasHeight;
  170. this.drawCanvas.width = this.canvasWidth;
  171. this.drawCanvas.height = this.canvasHeight;
  172. this.saveCanvas.width = this.canvasWidth;
  173. this.saveCanvas.height = this.canvasHeight;
  174. // 图片加载绘制
  175. let img = document.createElement("img");
  176. img.src = this.imgUrl;
  177. img.onload = () => {
  178. console.log("图片已加载");
  179. this.imgCtx.drawImage(img, 0, 0, this.canvasWidth, this.canvasHeight);
  180. this.renderDatas(); // 渲染原有数据
  181. };
  182. },
  183. startDraw() {
  184. // 绘制区域
  185. if (this.isDrawing) return;
  186. this.isDrawing = true;
  187. // 绘制逻辑
  188. this.drawCanvas.addEventListener("click", this.drawImageClickFn);
  189. this.drawCanvas.addEventListener("dblclick", this.drawImageDblClickFn);
  190. this.drawCanvas.addEventListener("mousemove", this.drawImageMoveFn);
  191. },
  192. clearAll() {
  193. // 清空所有绘制区域
  194. this.saveCtx.clearRect(0, 0, this.canvasWidth, this.canvasHeight);
  195. this.drawedPoints = [];
  196. },
  197. getImage() {
  198. // 这里请求接口 ...
  199. // this.imgUrl = "https://w.wallhaven.cc/full/6d/wallhaven-6d5k6x.jpg";
  200. // this.imgWidth = 1920;
  201. // this.imgHeight = 1080;
  202. this.imgUrl =JSON.parse(this.$route.query.src)
  203. this.imgWidth = 660;
  204. this.imgHeight = 380;
  205. this.imgUrl && this.initImgCanvas();
  206. },
  207. drawImageClickFn(e) {
  208. let drawCtx = this.drawCtx;
  209. if (e.offsetX || e.layerX) {
  210. let pointX = e.offsetX == undefined ? e.layerX : e.offsetX;
  211. let pointY = e.offsetY == undefined ? e.layerY : e.offsetY;
  212. let lastPoint = this.drawingPoints[this.drawingPoints.length - 1] || [];
  213. if (lastPoint[0] !== pointX || lastPoint[1] !== pointY) {
  214. this.drawingPoints.push([pointX, pointY]);
  215. }
  216. }
  217. },
  218. drawImageMoveFn(e) {
  219. let drawCtx = this.drawCtx;
  220. if (e.offsetX || e.layerX) {
  221. let pointX = e.offsetX == undefined ? e.layerX : e.offsetX;
  222. let pointY = e.offsetY == undefined ? e.layerY : e.offsetY;
  223. // 绘制
  224. drawCtx.clearRect(0, 0, this.canvasWidth, this.canvasHeight);
  225. // 绘制点
  226. drawCtx.fillStyle = "blue";
  227. this.drawingPoints.forEach((item, i) => {
  228. drawCtx.beginPath();
  229. drawCtx.arc(...item, 6, 0, 180);
  230. drawCtx.fill(); //填充
  231. });
  232. // 绘制动态区域
  233. drawCtx.save();
  234. drawCtx.beginPath();
  235. this.drawingPoints.forEach((item, i) => {
  236. drawCtx.lineTo(...item);
  237. });
  238. drawCtx.lineTo(pointX, pointY);
  239. drawCtx.lineWidth = "3";
  240. drawCtx.strokeStyle = "blue";
  241. drawCtx.fillStyle = "rgba(255, 0, 0, 0.3)";
  242. drawCtx.stroke();
  243. drawCtx.fill(); //填充
  244. drawCtx.restore();
  245. }
  246. },
  247. drawImageDblClickFn(e) {
  248. let drawCtx = this.drawCtx;
  249. let saveCtx = this.saveCtx;
  250. if (e.offsetX || e.layerX) {
  251. let pointX = e.offsetX == undefined ? e.layerX : e.offsetX;
  252. let pointY = e.offsetY == undefined ? e.layerY : e.offsetY;
  253. let lastPoint = this.drawingPoints[this.drawingPoints.length - 1] || [];
  254. if (lastPoint[0] !== pointX || lastPoint[1] !== pointY) {
  255. this.drawingPoints.push([pointX, pointY]);
  256. }
  257. }
  258. // 清空绘制图层
  259. drawCtx.clearRect(0, 0, this.canvasWidth, this.canvasHeight);
  260. // 绘制区域至保存图层
  261. this.drawSaveArea(this.drawingPoints);
  262. this.drawedPoints.push(this.drawingPoints);
  263. this.drawingPoints = [];
  264. this.isDrawing = false;
  265. // 绘制结束逻辑
  266. this.drawCanvas.removeEventListener("click", this.drawImageClickFn);
  267. this.drawCanvas.removeEventListener("dblclick", this.drawImageDblClickFn);
  268. this.drawCanvas.removeEventListener("mousemove", this.drawImageMoveFn);
  269. },
  270. drawSaveArea(points) {
  271. // console.log(points, "points");
  272. if (points.length === 0) return;
  273. this.saveCtx.save();
  274. this.saveCtx.beginPath();
  275. points.forEach((item, i) => {
  276. this.saveCtx.lineTo(...item);
  277. });
  278. this.saveCtx.closePath();
  279. this.saveCtx.lineWidth = "2";
  280. this.saveCtx.fillStyle = "rgba(255,0, 255, 0.3)";
  281. this.saveCtx.strokeStyle = "red";
  282. this.saveCtx.stroke();
  283. this.saveCtx.fill(); //填充
  284. this.saveCtx.restore();
  285. },
  286. savePoints() {
  287. // 将画布坐标数据转换成提交数据
  288. let objectPoints = [];
  289. // "object": [{"polygon": {"x1":700,"y1":273,"x2":975,"y2":278,"x3":1107,"y3":368,"x4":718,"y4":354} }]
  290. objectPoints = this.drawedPoints.map((area) => {
  291. let polygon = {};
  292. area.forEach((point, i) => {
  293. polygon[`x${i + 1}`] = Math.round(point[0] * this.ratio);
  294. polygon[`y${i + 1}`] = Math.round(point[1] * this.ratio);
  295. });
  296. // console.log(polygon)
  297. return {
  298. polygon: polygon,
  299. };
  300. });
  301. this.submitData = objectPoints;
  302. console.log("最终提交数据", objectPoints);
  303. this.$router.go(-1)
  304. },
  305. renderDatas() {
  306. // 将提交数据数据转换成画布坐标
  307. this.drawedPoints = this.submitData.map((item) => {
  308. let polygon = item.polygon;
  309. let points = [];
  310. for (let i = 1; i < Object.keys(polygon).length / 2 + 1; i++) {
  311. if (!isNaN(polygon[`x${i}`]) && !isNaN(polygon[`y${i}`])) {
  312. points.push([
  313. polygon[`x${i}`] / this.ratio,
  314. polygon[`y${i}`] / this.ratio,
  315. ]);
  316. }
  317. }
  318. this.drawSaveArea(points);
  319. return points;
  320. });
  321. },
  322. },
  323. };
  324. </script>
  325. <style lang="scss" scoped>
  326. .tool-box {
  327. width: 98%;
  328. height: 40px;
  329. padding: 5px 30px;
  330. margin: 20px auto 0;
  331. box-sizing: border-box;
  332. text-align: right;
  333. }
  334. .canvas-wrap {
  335. // width: 80vw;
  336. // height: 45vw;
  337. width:88%;
  338. height: 33.75vw;
  339. margin: 0px auto;
  340. background-color: #000; //#fff;
  341. border: 3px;
  342. border-color: #333;
  343. position: relative;
  344. }
  345. #imgCanvas,
  346. #drawCanvas,
  347. #saveCanvas {
  348. background: rgba(255, 0, 255, 0);
  349. position: absolute;
  350. top: 50%;
  351. left: 50%;
  352. transform: translate(-50%, -50%);
  353. }
  354. #drawCanvas {
  355. z-index: 2;
  356. }
  357. </style>