47343e52a0cefbcfc962705c2b94b4c54b041660.svn-base 36 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804
  1. <!-- -->
  2. <template>
  3. <el-dialog title="编辑图片"
  4. style="padding:10px 0px 0px 0px;margin-top:0 !important;"
  5. class="myDialog"
  6. width="90%"
  7. top="0"
  8. :modal-append-to-body="false"
  9. :append-to-body="false"
  10. :close-on-click-modal="false"
  11. @close="closeWindow"
  12. :visible.sync="dialogFormVisible">
  13. <el-container class="outSide">
  14. <!-- <el-header style="height:100%;">
  15. <el-form :inline="true" class="demo-form-inline" style="text-align:center;height:40px;">
  16. <el-form-item label="画布宽度" style="margin-bottom: 0px;margin-right: 0px;">
  17. <el-input style="width:80px;" v-model="changeCanvasWidth" placeholder="宽"></el-input>
  18. </el-form-item>
  19. <i class="el-icon-close" style="padding:10px;"></i>
  20. <el-form-item style="margin-bottom: 0px;margin-right: 0px;">
  21. <el-input style="width:80px;" v-model="changeCanvasHeight" placeholder="高"></el-input>
  22. </el-form-item>
  23. <el-form-item style="margin-bottom: 0px;margin-right: 0px;">
  24. <el-button type="text" icon="el-icon-refresh" @click="changeCanvasSize"></el-button>
  25. </el-form-item>
  26. </el-form>
  27. </el-header> -->
  28. <el-main style="padding:0 3px 10px 3px;" >
  29. <!-- 绘制基础图层操作都是在这个里面进行的 -->
  30. <canvas id="canvas" style="margin: 0 auto;" :width="this.canvasWidth" :height="this.canvasHeight" ></canvas>
  31. <!-- 隐藏的图层====>是给剪切用的 -->
  32. <canvas v-if="false" id="canvas_crop"></canvas>
  33. </el-main>
  34. <el-footer style="padding:10px 3px;width: 30%;margin: 0 auto;height: 100%;">
  35. <el-card style="padding: 10px;" class="myCard">
  36. <!-- <div style="width:32px;height:32px;" class="cut_pic" @click="clipImage"></div> -->
  37. <el-row>
  38. <el-col :span="13">
  39. <el-popover
  40. style="min-width:95px;width:95px;z-index:20000;!important"
  41. placement="bottom"
  42. width="50"
  43. trigger="click">
  44. <div>
  45. <el-button type="text" v-if="showCutButton" @click="chooseCutArea">框选裁剪区域</el-button>
  46. <el-button type="text" v-if="!showCutButton" @click="clipImage">裁剪</el-button>
  47. </div>
  48. <div style="width:32px;height:32px;" slot="reference" class="cut_pic"></div>
  49. </el-popover><!--剪裁图层-->
  50. <!-- <div style="width:32px;height:32px;" class="turn_left" @click="flip"></div> -->
  51. <el-popover
  52. placement="bottom"
  53. width="400"
  54. trigger="click">
  55. <div>
  56. <ChooseLocalPic
  57. @addPicUrl="addPicUrlEvent">
  58. </ChooseLocalPic>
  59. </div>
  60. <div style="width:32px;height:32px;" slot="reference" class="add_pic_button"></div>
  61. </el-popover><!--添加图片-->
  62. <el-popover
  63. placement="bottom"
  64. width="400"
  65. trigger="click">
  66. <div>
  67. <ColorPicker
  68. @colorPickerFor_text="colorPickerFor_textEvent">
  69. </ColorPicker>
  70. </div>
  71. <div style="width:32px;height:32px;" slot="reference" class="add_text_button"></div>
  72. </el-popover><!--添加文字-->
  73. <el-tooltip class="item" effect="dark" content="重做" placement="bottom-start">
  74. <div id="redo" style="width:32px;height:32px;" class="refresh" @click="redoEvent"></div><!--全部重做-->
  75. </el-tooltip>
  76. <el-tooltip class="item" effect="dark" content="删除元素" placement="bottom-start">
  77. <div style="width:32px;height:32px;" class="deleteSelect" @click="deleteSelect"></div><!--删除选中图层-->
  78. </el-tooltip>
  79. <el-tooltip class="item" effect="dark" content="撤销操作" placement="bottom-start">
  80. <div style="width:32px;height:32px;" id="undo" class="undo" @click="unDoEvent"></div><!--返回上一步操作-->
  81. </el-tooltip>
  82. </el-col>
  83. <el-col :span="11" style="text-align:right;">
  84. <el-button style="" type="primary" icon="el-icon-download" @click="unloadEvent">下载到本地</el-button>
  85. <el-button style="" type="primary" icon="el-icon-upload2" @click="changeImageTo64baseCode">保存</el-button>
  86. </el-col>
  87. </el-row>
  88. </el-card>
  89. </el-footer>
  90. </el-container>
  91. </el-dialog>
  92. </template>
  93. <script>
  94. //这里可以导入其他文件(比如:组件,工具js,第三方插件js,json文件,图片文件等等)
  95. //例如:import 《组件名称》 from '《组件路径》';
  96. //例如:import {函数名称1,函数名称2} from '《组件路径》';
  97. /*
  98. @ 模块名称:现在编辑图片
  99. @ 介绍:使用fabric插件初步实现图片编辑功能
  100. @ author:zhangyz
  101. @ version:0.0.1
  102. @ to-do list:
  103. @ 传递参数:picUrl(父组件的图片地址)
  104. */
  105. export default {
  106. //import引入的组件需要注入到对象中才能使用
  107. props:['picUrl'],
  108. components: {
  109. //1、直接挂载
  110. //组件名称1,
  111. //组件名称2,
  112. //2、懒加载
  113. //组件名称1: resolve => {require(['组件名称1.vue'], resolve);}
  114. ColorPicker: resolve => {
  115. require(["./ColorPicker.vue"], resolve);
  116. },
  117. ChooseLocalPic: resolve => {
  118. require(["./ChooseLocalPic.vue"], resolve);
  119. },
  120. },
  121. data() {
  122. return {
  123. //这里存放数据
  124. changeCanvasWidth:"",
  125. changeCanvasHeight:"",
  126. dialogFormVisible:true,
  127. canvasChangeSizeShow:true,
  128. elMain:false,
  129. dataURL:"",
  130. backgroudColor:'background: red;',
  131. canvasWidth:parseInt(window.innerWidth / 1.5),
  132. canvasHeight:parseInt(window.innerHeight / 1.5),
  133. a: [],
  134. cardPublic:null,
  135. rectPublic:null,
  136. textbox_Public:null,
  137. textLocation_left:50,
  138. textLocation_top:50,
  139. text_color:'red',
  140. obj_Pub:{},
  141. num: 1,
  142. pos: [0, 0],
  143. rvg:null,
  144. mousex:0,
  145. mousey:0,
  146. crop:false,
  147. disabled:false,
  148. object:null,
  149. objectCut:null,
  150. lastActive:null,
  151. el:null,
  152. selection_object_left: 0,
  153. selection_object_top: 0,
  154. isCropping: false,
  155. buttonConType: true,
  156. showCutButton: true,
  157. config : {
  158. canvasState : [],
  159. currentStateIndex : -1,
  160. redoStatus : false, //撤销状态
  161. undoStatus : false, // 重做状态
  162. undoFinishedStatus : 1,
  163. redoFinishedStatus : 1,
  164. undoButton : this.$refs.undo,
  165. redoButton : this.$refs.redo,
  166. }
  167. };
  168. },
  169. computed: {
  170. //监听属性 类似于data概念
  171. },
  172. watch: {
  173. //监控data中的数据变化
  174. picUrl(n,o){
  175. this.picUrl = n;
  176. this.initPicTools();
  177. this.canvasDataChange(); //监听canvas 事件
  178. }
  179. },
  180. methods: {
  181. //对话框初始化方法,代替mounted
  182. dialogMouted(){
  183. this.cardPublic = {};
  184. this.changeCanvasWidth = this.canvasWidth;
  185. this.changeCanvasHeight = this.canvasHeight;
  186. this.initPicTools();
  187. this.canvasDataChange(); //监听canvas 事件
  188. },
  189. changeCanvasSize(){
  190. this.canvasChangeSizeShow = false;
  191. this.canvasWidth = this.changeCanvasWidth;
  192. this.canvasHeight = this.changeCanvasHeight;
  193. this.canvasChangeSizeShow = true;
  194. },
  195. base64ToBlob ({b64data = '', contentType = '', sliceSize = 512} = {}) {
  196. return new Promise((resolve, reject) => {
  197. // 使用 atob() 方法将数据解码
  198. let byteCharacters = atob(b64data);
  199. let byteArrays = [];
  200. for (let offset = 0; offset < byteCharacters.length; offset += sliceSize) {
  201. let slice = byteCharacters.slice(offset, offset + sliceSize);
  202. let byteNumbers = [];
  203. for (let i = 0; i < slice.length; i++) {
  204. byteNumbers.push(slice.charCodeAt(i));
  205. }
  206. // 8 位无符号整数值的类型化数组。内容将初始化为 0。
  207. // 如果无法分配请求数目的字节,则将引发异常。
  208. byteArrays.push(new Uint8Array(byteNumbers));
  209. }
  210. let result = new Blob(byteArrays, {
  211. type: contentType
  212. })
  213. result = Object.assign(result,{
  214. // jartto: 这里一定要处理一下 URL.createObjectURL
  215. preview: URL.createObjectURL(result),
  216. name: `图片示例.png`
  217. });
  218. resolve(result)
  219. })
  220. },
  221. //方法集合
  222. //初始化要修改的图片
  223. // getBase64Image(img) {
  224. // var canvas = document.createElement("canvas");
  225. // canvas.width = img.width;
  226. // canvas.height = img.height;
  227. // var ctx = canvas.getContext("2d");
  228. // ctx.drawImage(img, 0, 0, img.width, img.height);
  229. // var ext = img.src.substring(img.src.lastIndexOf(".")+1).toLowerCase();
  230. // canvas.setAttribute('crossOrigin', 'Anonymous');
  231. // var dataURL = canvas.toDataURL("image/"+ext);
  232. // return dataURL;
  233. // },
  234. //根据图片的URL转为64位码
  235. getBase64(url, callback){
  236. //通过构造函数来创建的 img 实例,在赋予 src 值后就会立刻下载图片,相比 createElement() 创建 <img> 省去了 append(),也就避免了文档冗余和污染
  237. var _self = this;
  238. _self.dataURL = _self.cardPublic.toDataURL("image/png"); //转换图片为dataURL
  239. callback?callback(_self.dataURL):null; //调用回调函数
  240. // var Img = new Image();
  241. // _self.dataURL="";
  242. // Img.src=url;
  243. // Img.setAttribute('crossOrigin', 'anonymous');
  244. // Img.onload=function(){ //要先确保图片完整获取到,这是个异步事件
  245. // var canvas = document.createElement("canvas"), //创建canvas元素
  246. // width = Img.width, //确保canvas的尺寸和图片一样
  247. // height = Img.height;
  248. // _self.cardPublic.width = width;
  249. // _self.cardPublic.height = height;
  250. // _self.cardPublic.getContext("2d").drawImage(Img,0,0,width,height); //将图片绘制到canvas中
  251. // _self.dataURL = canvas.toDataURL("image/png"); //转换图片为dataURL
  252. // callback?callback(_self.dataURL):null; //调用回调函数
  253. // };
  254. },
  255. //上传图片动作
  256. changeImageTo64baseCode(){
  257. var _self = this;
  258. var imgLink = _self.picUrl;
  259. this.cardPublic.backgroundColor = "transparent";
  260. // var tempImage = new Image();
  261. // tempImage.src = imgLink;
  262. // tempImage.setAttribute('crossOrigin', 'Anonymous');
  263. // tempImage.onload = function(){
  264. // var base64 = _self.getBase64Image(tempImage);
  265. // console.log(base64);
  266. // }
  267. this.getBase64(imgLink,(dataURL)=>{
  268. console.log(dataURL);
  269. var formData = new FormData();
  270. formData.append("file", _self.dataURLtoFile(dataURL,"zyz.png"));
  271. _self.$emit('editPicData',formData);
  272. let base64 = dataURL.split(',')[1]
  273. _self.base64ToBlob({b64data: base64, contentType: 'image/png'}).then(res => {
  274. console.log('blob', res)
  275. })
  276. });
  277. _self.object = {};
  278. _self.cardPublic = {};
  279. _self.canvasWidth = 12;
  280. _self.picUrl = "";
  281. },
  282. //将6位码转为图片
  283. dataURLtoFile(dataurl, filename) {//将base64转换为文件
  284. var arr = dataurl.split(','), mime = arr[0].match(/:(.*?);/)[1],
  285. bstr = atob(arr[1]), n = bstr.length, u8arr = new Uint8Array(n);
  286. while(n--){
  287. u8arr[n] = bstr.charCodeAt(n);
  288. }
  289. return new File([u8arr], filename, {type:mime});
  290. },
  291. initPicTools(){
  292. var _self = this;
  293. //const canvas = new fabric.Canvas('canvas');
  294. this.cardPublic = new fabric.Canvas('canvas');//把 card对象给cardPublic供全局使用
  295. this.cardPublic.backgroundColor = "#848484";
  296. this.cardPublic.renderAll();
  297. // fabric.Image.fromURL('../src/assets/QQ20190913-000717@2x.png', (img) => {
  298. // img.set({
  299. // // 通过scale来设置图片大小,这里设置和画布一样大
  300. // scaleX: card.width / img.width,
  301. // scaleY: card.height / img.height,
  302. // });
  303. // // 设置背景
  304. // card.setBackgroundImage(img, card.renderAll.bind(card));
  305. // card.renderAll();
  306. // });
  307. // var rvg = document.getElementById('canvas').getBoundingClientRect();
  308. // _self.pos[0] = rvg.left;
  309. // _self.pos[1] = rvg.top;
  310. // let el = new fabric.Rect({
  311. // fill: 'transparent',
  312. // originX: 'left',
  313. // originY: 'top',
  314. // stroke: '#ccc',
  315. // strokeDashArray: [2, 2],
  316. // opacity: 1,
  317. // width: 1,
  318. // height: 1
  319. // });
  320. // el.visible = false;
  321. // _self.cardPublic.add(el);
  322. // let proxyPicUrl = _self.picUrl;
  323. // let proxyPicUrl = _self.picUrl.replace('10.1.102.136:8100','10.1.102.136:8080');
  324. // let proxyPicUrl = _self.picUrl.replace('10.1.102.136:8100','localhost:8085/api');
  325. let proxyPicUrl ="";
  326. console.log(window.location.href.split("/")[2])
  327. if(window.location.href.split("/")[2] == "10.1.102.64:8080"){
  328. console.log(_self.picUrl)
  329. proxyPicUrl = _self.picUrl.replace('10.1.102.240:8200','pdc.goldenwater.com.cn');
  330. }
  331. else if(window.location.href.split("/")[2] == "pdc.goldenwater.com.cn"){
  332. proxyPicUrl = _self.picUrl;
  333. }
  334. else{
  335. proxyPicUrl = _self.picUrl.replace('10.1.102.240:8200',window.location.href.split("/")[2] + "/pdcApi");
  336. }
  337. fabric.util.loadImage(proxyPicUrl, function (img) {
  338. // fabric.util.loadImage(_self.picUrl, function (img) {
  339. img.setAttribute('crossOrigin', 'anonymous');
  340. _self.object = new fabric.Image(img);
  341. _self.object.set({
  342. borderColor: 'red',
  343. // left: 100,
  344. // top: 100,
  345. scaleX: _self.cardPublic.width / img.width,
  346. scaleY: _self.cardPublic.height / img.height,
  347. selectable: true
  348. });
  349. //_self.cardPublic.setWidth(_self.object.width)
  350. //_self.cardPublic.setHeight(_self.object.height)
  351. _self.object.hasRotatingPoint = true;
  352. // _self.object.scaleX = canvas.width / _self.object.width;
  353. // _self.object.scaleX = canvas.height / _self.object.height;
  354. _self.cardPublic.add(_self.object);
  355. _self.cardPublic.renderAll();
  356. });
  357. },
  358. //添加文字
  359. addText(){
  360. var _self = this;
  361. this.obj_Pub['textbox' + _self.num] = new fabric.Textbox('添加文字', {
  362. left: _self.textLocation_left,
  363. top: _self.textLocation_top,
  364. width: 150,
  365. fontSize: 20, // 字体大小
  366. fontWeight: 800, // 字体粗细
  367. fill: _self.text_color, // 字体颜色
  368. // fontStyle: 'italic', // 斜体
  369. // fontFamily: 'Delicious', // 设置字体
  370. // stroke: 'green', // 描边颜色
  371. // strokeWidth: 3, // 描边宽度
  372. hasControls: false,
  373. borderColor: 'orange',
  374. editingBorderColor: 'blue' // 点击文字进入编辑状态时的边框颜色
  375. });
  376. this.cardPublic.add(this.obj_Pub['textbox' + _self.num]);
  377. _self.num ++;//多次添加文字
  378. // this._self.textLocation_top += 50;//多次添加文字纵向位置
  379. },
  380. //字体颜色组件冒上来的颜色值
  381. colorPickerFor_textEvent(item){
  382. this.text_color = item.color;
  383. this.addText();
  384. },
  385. //图片选择冒上来的图片URL
  386. addPicUrlEvent(item){
  387. // this.picUrl = item;
  388. // alert(this.picUrl)
  389. // 方式二(通过图片路径添加)
  390. var _self = this;
  391. for (var i = 0; i < item.length; i++) {
  392. fabric.Image.fromURL(item[i].url, function (img) {
  393. img.hasControls = true,
  394. img.borderColor = 'orange',
  395. img.scaleX = (_self.cardPublic.width / img.width) / 2,
  396. img.scaleY = (_self.cardPublic.height / img.height) / 2
  397. _self.cardPublic.add(img);
  398. });
  399. }
  400. // fabric.Image.fromURL(item, (img) => {
  401. // img.set({
  402. // scaleX: (this.cardPublic.width / img.width) / 2,
  403. // scaleY: (this.cardPublic.height / img.height) / 2,
  404. // hasControls: true, // 是否开启图层的控件
  405. // borderColor: 'orange', // 图层控件边框的颜色
  406. // });
  407. // // 添加对象后, 如下图
  408. // this.cardPublic.add(img);
  409. // });
  410. },
  411. //旋转
  412. flip(){
  413. // 顺时针90°旋转
  414. const currAngle = this.cardPublic.getActiveObject().angle; // 当前图层的角度
  415. const angle = currAngle === 360 ? 90 :currAngle + 90;
  416. this.cardPublic.getActiveObject().angle = angle;
  417. // 如果是通过滑块的方式控制旋转
  418. // this.selectedObj.rotate(slideValue);
  419. // 所有图层的操作之后,都需要调用这个方法
  420. this.cardPublic.renderAll()
  421. // const currAngle = this.cardPublic.getActiveObject();
  422. // const angle = currAngle === 360 ? 90 :currAngle - 90;
  423. // currAngle.rotate(angle);
  424. // const currAngle = this.cardPublic.backgroundImage.angle; // 当前图层的角度
  425. // this.cardPublic.backgroundImage.rotate(angle);
  426. // this.cardPublic.requestRenderAll();
  427. },
  428. //剪裁图层 =====>剪裁方法
  429. cropImage(png, left, top, height, width) {
  430. var _self = this;
  431. //将图片放进一个新的canvas中,经裁剪后导出一个新的图片。
  432. //如果用户选框大于原图片,则将选框缩至原图片大小
  433. if (top < png.top) {
  434. height = height - (png.top - top);
  435. top = png.top;
  436. }
  437. if (left < png.left) {
  438. width = width - (png.left - left);
  439. left = png.left;
  440. }
  441. if (top + height > png.top + png.height * png.scaleY)
  442. height = png.top + png.height * png.scaleY - top;
  443. if (left + width > png.left + png.width * png.scaleX)
  444. width = png.left + png.width * png.scaleX - left;
  445. var canvas_crop = new fabric.Canvas("canvas_crop");
  446. fabric.Image.fromURL(_self.cardPublic.toDataURL('png'), function(img) {
  447. img.set('left', -left);
  448. img.set('top', -top);
  449. canvas_crop.add(img)
  450. canvas_crop.setHeight(height);
  451. canvas_crop.setWidth(width);
  452. canvas_crop.renderAll();
  453. fabric.Image.fromURL(canvas_crop.toDataURL('png'), function(croppedImg) {
  454. croppedImg.set('left', left);
  455. croppedImg.set('top', top);
  456. //下一步是底图的特殊处理,判断是否是底图,然后在新的图层上添加红色边框,禁止剪裁动作
  457. if(_self.objectCut.hasOwnProperty("borderColor")&&_self.objectCut.borderColor == "red"){
  458. croppedImg.set('borderColor', 'red');
  459. }
  460. _self.cardPublic.add(croppedImg).renderAll();
  461. });
  462. });
  463. },
  464. //剪裁图层 =====>确定剪裁动作
  465. clipImage(){
  466. var _self = this;
  467. if (!_self.isCropping) {
  468. alert("请先框选裁剪区域。");
  469. return;
  470. }
  471. var left = _self.el.left - _self.objectCut.left;
  472. var top = _self.el.top - _self.objectCut.top;
  473. left *= 1;
  474. top *= 1;
  475. var width = _self.el.width * 1;
  476. var height = _self.el.height * 1;
  477. //将当前帧导出到一个新的canvas中并执行裁剪,在此期间暂停记录历史记录。
  478. this.cropImage(_self.objectCut, _self.el.left, _self.el.top, parseInt(_self.el.scaleY * height), parseInt(width * _self.el.scaleX));
  479. // for (var i = 0; i < $("#layers li").length; i++) {
  480. // canvas.item(i).selectable = true;
  481. // }
  482. // disabled = true;
  483. _self.cardPublic.remove(_self.objectCut);
  484. _self.cardPublic.remove(_self.cardPublic.getActiveObject());
  485. _self.lastActive = _self.objectCut;
  486. _self.cardPublic.renderAll();
  487. _self.isCropping = false;
  488. _self.showCutButton = true;
  489. },
  490. //剪裁图层 =====>选择要剪裁的图层
  491. chooseCutArea(){
  492. var _self = this;
  493. _self.cardPublic.remove(_self.el);
  494. if (_self.cardPublic.getActiveObject()) {
  495. if (_self.cardPublic.getActiveObject().type == 'sprite') {
  496. alert("所选对象不可裁剪。");
  497. return;
  498. }
  499. _self.objectCut = _self.cardPublic.getActiveObject();
  500. if (_self.lastActive !== _self.objectCut) {
  501. console.log('different object');
  502. } else {
  503. console.log('same object');
  504. }
  505. if (_self.lastActive && _self.lastActive !== _self.objectCut) {
  506. _self.lastActive.clipTo = null;
  507. }
  508. //生成一个和待裁剪元素相同大小的矩形用于框选裁剪区域
  509. _self.el = new fabric.Rect({
  510. fill: 'rgba(0,0,0,0)',
  511. originX: 'left',
  512. originY: 'top',
  513. stroke: '#ccc',
  514. //strokeDashArray: [2, 2],
  515. strokWidth: 5,
  516. //opacity: 1,
  517. width: 1,
  518. height: 1,
  519. borderColor: '#36fd00',
  520. cornerColor: 'green',
  521. hasRotatingPoint: false,
  522. selectable: true
  523. });
  524. _self.el.left = _self.cardPublic.getActiveObject().left;
  525. _self.selection_object_left = _self.cardPublic.getActiveObject().left;
  526. _self.selection_object_top = _self.cardPublic.getActiveObject().top;
  527. _self.el.top = _self.cardPublic.getActiveObject().top;
  528. _self.el.width = _self.cardPublic.getActiveObject().width * _self.cardPublic.getActiveObject().scaleX;
  529. _self.el.height = _self.cardPublic.getActiveObject().height * _self.cardPublic.getActiveObject().scaleY;
  530. _self.cardPublic.add(_self.el);
  531. _self.cardPublic.setActiveObject(_self.el);
  532. // for (var i = 0; i < $("#layers li").length; i++) {
  533. // _self.cardPublic.item(i).selectable = false;
  534. // }
  535. _self.showCutButton = false;
  536. } else {
  537. alert("请选中一个元素。");
  538. }
  539. _self.isCropping = true;
  540. },
  541. //删除所选元素
  542. deleteSelect(){
  543. let selectObj = this.cardPublic.getActiveObject(); // 返回当前画布中被选中的图层
  544. if(!selectObj){
  545. alert('请先点击选择元素');
  546. return;
  547. }else{
  548. if(selectObj.hasOwnProperty("borderColor")&&selectObj.borderColor == "red"){
  549. this.$message({
  550. type: 'warning',
  551. message: '底图不可删除!'
  552. });
  553. }else{
  554. this.cardPublic.remove(selectObj) // 传入需要移除的object
  555. this.cardPublic.renderAll()
  556. }
  557. }
  558. },
  559. //初始化记录画图状态为撤销操作准备
  560. canvasDataChange(){
  561. let _self = this;
  562. this.cardPublic.on('object:modified', function(){
  563. _self.updateCanvasState();
  564. });
  565. this.cardPublic.on('object:added', function(){
  566. _self.updateCanvasState()
  567. });
  568. this.cardPublic.on('object:removed', function(){
  569. _self.updateCanvasState()
  570. });
  571. this.cardPublic.on('object:rotating', function(){
  572. _self.updateCanvasState()
  573. });
  574. },
  575. //更新画布状态
  576. updateCanvasState() {
  577. var _self = this;
  578. if(this.config.undoStatus == false && this.config.redoStatus == false){
  579. var jsonData = _self.cardPublic.toJSON();
  580. var canvasAsJson = JSON.stringify(jsonData);
  581. if(_self.config.currentStateIndex < _self.config.canvasState.length-1){
  582. var indexToBeInserted = _self.config.currentStateIndex+1;
  583. _self.config.canvasState[indexToBeInserted] = canvasAsJson;
  584. var numberOfElementsToRetain = indexToBeInserted+1;
  585. _self.config.canvasState = _self.config.canvasState.splice(0,numberOfElementsToRetain);
  586. }else{
  587. _self.config.canvasState.push(canvasAsJson);
  588. }
  589. _self.config.currentStateIndex = _self.config.canvasState.length-1;
  590. if((_self.config.currentStateIndex == _self.config.canvasState.length-1) && _self.config.currentStateIndex != -1){
  591. // _self.config.redoButton.disabled= true
  592. }
  593. }
  594. },
  595. //撤销操作事件
  596. unDoEvent(){
  597. let _self =this;
  598. if(this.config.undoFinishedStatus){
  599. if(this.config.currentStateIndex == -1){
  600. this.config.undoStatus = false;
  601. }else{
  602. if (this.config.canvasState.length >= 1) {
  603. this.config.undoFinishedStatus = 0;
  604. if(this.config.currentStateIndex != 0){
  605. this.config.undoStatus = true;
  606. this.cardPublic.loadFromJSON(this.config.canvasState[this.config.currentStateIndex-1],function(){
  607. var jsonData = JSON.parse(_self.config.canvasState[_self.config.currentStateIndex-1]);
  608. _self.cardPublic.renderAll();
  609. _self.config.undoStatus = false;
  610. _self.config.currentStateIndex -= 1;
  611. if(_self.config.currentStateIndex !== _self.config.canvasState.length-1){
  612. }
  613. _self.config.undoFinishedStatus = 1;
  614. });
  615. }
  616. else if(_self.config.currentStateIndex == 0){
  617. if(_self.cardPublic._objects.length == 1){
  618. _self.$message({
  619. type: 'warning',
  620. message: '无可撤销动作!'
  621. });
  622. }else{
  623. _self.cardPublic.clear();
  624. _self.config.undoFinishedStatus = 1;
  625. _self.config.currentStateIndex -= 1;
  626. }
  627. }
  628. }
  629. }
  630. }
  631. },
  632. unloadEvent(event){
  633. this.cardPublic.backgroundColor = "transparent";
  634. // 图片导出为 png 格式
  635. var type = 'png';
  636. // 返回一个包含JPG图片的<img>元素
  637. var img_png_src = this.cardPublic.toDataURL("image/png"); //将画板保存为图片格式的函数
  638. // 加工image data,替换mime type
  639. var imgData = img_png_src.replace(this._fixType(type),'image/octet-stream');
  640. // 下载后的问题名
  641. var filename = '个人画板_' + (new Date()).getTime() + '.' + type;
  642. // download
  643. this.saveFile(imgData,filename);
  644. },
  645. /**
  646. * 在本地进行文件保存
  647. * @param {String} data 要保存到本地的图片数据
  648. * @param {String} filename 文件名
  649. */
  650. saveFile(data, filename){
  651. var save_link = document.createElementNS('http://www.w3.org/1999/xhtml', 'a');
  652. save_link.href = data;
  653. save_link.download = filename;
  654. var event = document.createEvent('MouseEvents');
  655. event.initMouseEvent('click', true, false, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
  656. save_link.dispatchEvent(event);
  657. },
  658. /**
  659. * 获取mimeType
  660. * @param {String} type the old mime-type
  661. * @return the new mime-type
  662. */
  663. _fixType(type) {
  664. type = type.toLowerCase().replace(/jpg/i, 'jpeg');
  665. var r = type.match(/png|jpeg|bmp|gif/)[0];
  666. return 'image/' + r;
  667. },
  668. //清除所有添加的对象
  669. redoEvent(){
  670. // this.cardPublic._objects.length = 0 ;
  671. // for(let i = 0; i < this.cardPublic._objects.length;){
  672. // this.cardPublic.remove(this.obj_Pub['textbox' + (this.cardPublic._objects.length)]);
  673. // }
  674. this.cardPublic._objects.splice(1,this.cardPublic._objects.length - 1);//保留this.cardPublic._objects中第一个元素,此元素为要修改的图片底层画布。
  675. this.obj_Pub={}//将添加的对象置空
  676. this.num = 1;//对象名称编号回归1
  677. this._self.textLocation_top = 50;//文字对象位置还原
  678. this.cardPublic.renderAll();
  679. },
  680. closeWindow() {
  681. this.$emit("cancelHandler");
  682. },
  683. },
  684. beforeCreate() {}, //生命周期 - 组件实例化之前执行的函数
  685. created() {
  686. }, //生命周期 - 组件实例化完毕,但页面还未显示(可以访问当前this实例)
  687. beforeMount() {}, //生命周期 - 组件挂载前,页面仍未显示,但虚拟DOM已经OK
  688. mounted() {
  689. this.elMain = true;
  690. this.$nextTick(() => {
  691. this.dialogMouted();
  692. });
  693. }, //生命周期 - 挂载完成,此方法执行后页面内容显示(可以访问DOM元素)
  694. beforeUpdate() {}, //生命周期 - 组件更新之前,页面仍未更新
  695. updated() {}, //生命周期 - 组件更新之后,此方法执行后页面更新
  696. beforeDestroy() {}, //生命周期 - 销毁之前
  697. destroyed() {}, //生命周期 - 销毁完成
  698. activated() {} //如果页面有keep-alive缓存功能,这个函数会触发
  699. };
  700. </script>
  701. <style lang='scss'>
  702. //@import url(); 引入公共css类
  703. .myCard .el-card__body{
  704. padding: 0;
  705. }
  706. .outSide .el-popover{
  707. min-width: 95px;
  708. z-index: 20000;
  709. }
  710. @mixin buttonBase{
  711. float: left;
  712. margin-left:10px;
  713. }
  714. .outSide{
  715. }
  716. .refresh{
  717. @include buttonBase;
  718. background: url("../../assets/img/refresh.png");
  719. cursor: pointer;
  720. }
  721. .deleteSelect{
  722. @include buttonBase;
  723. background: url("../../assets/img/deletePicEdit.png");
  724. cursor: pointer;
  725. }
  726. .add_text_button{
  727. @include buttonBase;
  728. background: url("../../assets/img/textIcon.png");
  729. cursor: pointer;
  730. }
  731. .add_pic_button{
  732. @include buttonBase;
  733. background: url("../../assets/img/addPic.png");
  734. cursor: pointer;
  735. }
  736. .cut_pic{
  737. @include buttonBase;
  738. background: url("../../assets/img/cut.png");
  739. cursor: pointer;
  740. }
  741. .turn_left{
  742. @include buttonBase;
  743. background: url("../../assets/img/turnleft.png");
  744. cursor: pointer;
  745. }
  746. .undo{
  747. @include buttonBase;
  748. background: url("../../assets/img/undo.png");
  749. cursor: pointer;
  750. }
  751. .unload{
  752. @include buttonBase;
  753. background: url("../../assets/img/downLoadToLocal.png");
  754. cursor: pointer;
  755. }
  756. .save{
  757. @include buttonBase;
  758. background: url("../../assets/img/save.png");
  759. cursor: pointer;
  760. }
  761. .myDialog .el-dialog__body{
  762. padding: 10px 0px;
  763. }
  764. .canvas-container{
  765. margin:0 auto;
  766. }
  767. </style>