TuiImageEditorVue3.vue 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458
  1. <template>
  2. <div ref="editor"></div>
  3. </template>
  4. <script setup>
  5. import {useTemplateRef, onMounted, onUnmounted} from 'vue'
  6. import ImageEditor from 'tui-image-editor';
  7. import 'tui-color-picker/dist/tui-color-picker.css';
  8. import 'tui-image-editor/dist/tui-image-editor.css';
  9. const props = defineProps({
  10. // includeUi: {
  11. // type: Boolean,
  12. // default: true,
  13. // },
  14. // /**
  15. // * @see https://nhn.github.io/tui.image-editor/latest/ImageEditor
  16. // */
  17. // options: {
  18. // type: Object,
  19. // default() {
  20. // return {
  21. // cssMaxWidth: 700,
  22. // cssMaxHeight: 900,
  23. // };
  24. // },
  25. // },
  26. // 图片地址
  27. image_url: String,
  28. });
  29. const includeUIOptions = {
  30. includeUI: {
  31. initMenu: 'filter',
  32. },
  33. };
  34. const editor = useTemplateRef('editor');
  35. // 编辑器实例
  36. let editorInstance = null;
  37. const addEventListener = () => {
  38. // Object.keys(this.$listeners).forEach((eventName) => {
  39. // editorInstance.on(eventName, (...args) => this.$emit(eventName, ...args));
  40. // });
  41. }
  42. const getRootElement = () => {
  43. return editor.value;
  44. }
  45. const invoke = (methodName, ...args) => {
  46. let result = null;
  47. if (editorInstance[methodName]) {
  48. result = editorInstance[methodName](...args);
  49. } else if (methodName.indexOf('.') > -1) {
  50. const func = getMethod(editorInstance, methodName);
  51. if (typeof func === 'function') {
  52. result = func(...args);
  53. }
  54. }
  55. return result;
  56. }
  57. const getMethod = (instance, methodName) => {
  58. const { first, rest } = parseDotMethodName(methodName);
  59. const isInstance = instance.constructor.name !== 'Object';
  60. const type = typeof instance[first];
  61. let obj;
  62. if (isInstance && type === 'function') {
  63. obj = instance[first].bind(instance);
  64. } else {
  65. obj = instance[first];
  66. }
  67. if (rest.length > 0) {
  68. return getMethod(obj, rest);
  69. }
  70. return obj;
  71. }
  72. const parseDotMethodName = (methodName) => {
  73. const firstDotIdx = methodName.indexOf('.');
  74. let firstMethodName = methodName;
  75. let restMethodName = '';
  76. if (firstDotIdx > -1) {
  77. firstMethodName = methodName.substring(0, firstDotIdx);
  78. restMethodName = methodName.substring(firstDotIdx + 1, methodName.length);
  79. }
  80. return {
  81. first: firstMethodName,
  82. rest: restMethodName,
  83. };
  84. }
  85. const zh_local = {
  86. Crop: "裁剪",
  87. Load: '替换图片',
  88. DeleteAll: "全部删除",
  89. Delete: "删除",
  90. Undo: "撤销",
  91. Redo: "反撤销",
  92. Reset: "重置",
  93. Flip: "镜像",
  94. Rotate: "旋转",
  95. Draw: "画",
  96. Shape: "形状标注",
  97. Icon: "图标标注",
  98. Text: "文字标注",
  99. Mask: "遮罩",
  100. Filter: "滤镜",
  101. Bold: "加粗",
  102. Italic: "斜体",
  103. Underline: "下划线",
  104. Left: "左对齐",
  105. Center: "居中",
  106. Right: "右对齐",
  107. Color: "颜色",
  108. "Text size": "字体大小",
  109. Custom: "自定义",
  110. Square: "正方形",
  111. Apply: "应用",
  112. Cancel: "取消",
  113. "Flip X": "X 轴",
  114. "Flip Y": "Y 轴",
  115. Range: "区间",
  116. Stroke: "描边",
  117. Fill: "填充",
  118. Circle: "圆",
  119. Triangle: "三角",
  120. Rectangle: "矩形",
  121. Free: "曲线",
  122. Straight: "直线",
  123. Arrow: "箭头",
  124. "Arrow-2": "箭头2",
  125. "Arrow-3": "箭头3",
  126. "Star-1": "星星1",
  127. "Star-2": "星星2",
  128. Polygon: "多边形",
  129. Location: "定位",
  130. Heart: "心形",
  131. Bubble: "气泡",
  132. "Custom icon": "自定义图标",
  133. "Load Mask Image": "加载蒙层图片",
  134. Grayscale: "灰度",
  135. Blur: "模糊",
  136. Sharpen: "锐化",
  137. Emboss: "浮雕",
  138. "Remove White": "除去白色",
  139. Distance: "距离",
  140. Brightness: "亮度",
  141. Noise: "噪音",
  142. "Color Filter": "彩色滤镜",
  143. Sepia: "棕色",
  144. Sepia2: "棕色2",
  145. Invert: "负片",
  146. Pixelate: "像素化",
  147. Threshold: "阈值",
  148. Tint: "色调",
  149. Multiply: "正片叠底",
  150. Blend: "混合色",
  151. Double: '双击',
  152. Download: '下载图片'
  153. };
  154. const theme = {
  155. // // image 坐上角度图片
  156. // 'common.bi.image': '', // 在这里换上你喜欢的logo图片
  157. // 'common.bisize.width': '0px',
  158. // 'common.bisize.height': '0px',
  159. // 'common.backgroundImage': 'none',
  160. // 'common.backgroundColor': '#f3f4f6',
  161. // 'common.border': '1px solid #444',
  162. // // header
  163. // 'header.backgroundImage': 'none',
  164. // 'header.backgroundColor': '#f3f4f6',
  165. // 'header.border': '0px',
  166. // // load button
  167. // 'loadButton.backgroundColor': '#fff',
  168. // 'loadButton.border': '1px solid #ddd',
  169. // 'loadButton.color': '#222',
  170. // 'loadButton.fontFamily': 'NotoSans, sans-serif',
  171. // 'loadButton.fontSize': '12px',
  172. // 'loadButton.display': 'none', // 可以直接隐藏掉
  173. // // download button
  174. // 'downloadButton.backgroundColor': '#fdba3b',
  175. // 'downloadButton.border': '1px solid #fdba3b',
  176. // 'downloadButton.color': '#fff',
  177. // 'downloadButton.fontFamily': 'NotoSans, sans-serif',
  178. // 'downloadButton.fontSize': '12px',
  179. // 'downloadButton.display': 'none', // 可以直接隐藏掉
  180. // // icons default
  181. // 'menu.normalIcon.color': '#8a8a8a',
  182. // 'menu.activeIcon.color': '#555555',
  183. // 'menu.disabledIcon.color': '#434343',
  184. // 'menu.hoverIcon.color': '#e9e9e9',
  185. // 'submenu.normalIcon.color': '#8a8a8a',
  186. // 'submenu.activeIcon.color': '#e9e9e9',
  187. // 'menu.iconSize.width': '24px',
  188. // 'menu.iconSize.height': '24px',
  189. // 'submenu.iconSize.width': '32px',
  190. // 'submenu.iconSize.height': '32px',
  191. // // submenu primary color
  192. // 'submenu.backgroundColor': '#1e1e1e',
  193. // 'submenu.partition.color': '#858585',
  194. // // submenu labels
  195. // 'submenu.normalLabel.color': '#858585',
  196. // 'submenu.normalLabel.fontWeight': 'lighter',
  197. // 'submenu.activeLabel.color': '#fff',
  198. // 'submenu.activeLabel.fontWeight': 'lighter',
  199. // // checkbox style
  200. // 'checkbox.border': '1px solid #ccc',
  201. // 'checkbox.backgroundColor': '#fff',
  202. // // rango style
  203. // 'range.pointer.color': '#fff',
  204. // 'range.bar.color': '#666',
  205. // 'range.subbar.color': '#d1d1d1',
  206. // 'range.disabledPointer.color': '#414141',
  207. // 'range.disabledBar.color': '#282828',
  208. // 'range.disabledSubbar.color': '#414141',
  209. // 'range.value.color': '#fff',
  210. // 'range.value.fontWeight': 'lighter',
  211. // 'range.value.fontSize': '11px',
  212. // 'range.value.border': '1px solid #353535',
  213. // 'range.value.backgroundColor': '#151515',
  214. // 'range.title.color': '#fff',
  215. // 'range.title.fontWeight': 'lighter',
  216. // // colorpicker style
  217. // 'colorpicker.button.border': '1px solid #1e1e1e',
  218. // 'colorpicker.title.color': '#fff',
  219. // -----------------------
  220. // // image 坐上角度图片
  221. // "common.bi.image": "", // 在这里换上你喜欢的logo图片
  222. // "common.bisize.width": "0px",
  223. // "common.bisize.height": "0px",
  224. // "common.backgroundImage": "none",
  225. // "common.backgroundColor": "#f3f4f6",
  226. // "common.border": "1px solid #444",
  227. // // header
  228. // "header.backgroundImage": "none",
  229. // "header.backgroundColor": "#f3f4f6",
  230. // "header.border": "0px",
  231. // "header.margin": "0px auto",
  232. // // load button
  233. // "loadButton.backgroundColor": "#fff",
  234. // "loadButton.border": "1px solid #ddd",
  235. // "loadButton.color": "#222",
  236. // "loadButton.fontFamily": "NotoSans, sans-serif",
  237. // "loadButton.fontSize": "12px",
  238. // // "loadButton.width": "150px",
  239. // // "loadButton.display": "none", // 可以直接隐藏掉
  240. // // download button
  241. // "downloadButton.backgroundColor": "#fdba3b",
  242. // "downloadButton.border": "1px solid #fdba3b",
  243. // "downloadButton.color": "#fff",
  244. // "downloadButton.fontFamily": "NotoSans, sans-serif",
  245. // "downloadButton.fontSize": "12px",
  246. // // "downloadButton.display": "none", // 可以直接隐藏掉
  247. // // icons default
  248. // "menu.normalIcon.color": "#8a8a8a",
  249. // "menu.activeIcon.color": "#555555",
  250. // "menu.disabledIcon.color": "#434343",
  251. // "menu.hoverIcon.color": "#e9e9e9",
  252. // "submenu.normalIcon.color": "#8a8a8a",
  253. // "submenu.activeIcon.color": "#e9e9e9",
  254. // "menu.iconSize.width": "24px",
  255. // "menu.iconSize.height": "24px",
  256. // "submenu.iconSize.width": "32px",
  257. // "submenu.iconSize.height": "32px",
  258. // // submenu primary color
  259. // "submenu.backgroundColor": "#1e1e1e",
  260. // "submenu.partition.color": "#858585",
  261. // // submenu labels
  262. // "submenu.normalLabel.color": "#858585",
  263. // "submenu.normalLabel.fontWeight": "lighter",
  264. // "submenu.activeLabel.color": "#fff",
  265. // "submenu.activeLabel.fontWeight": "lighter",
  266. // // checkbox style
  267. // "checkbox.border": "1px solid #ccc",
  268. // "checkbox.backgroundColor": "#fff",
  269. // // rango style
  270. // "range.pointer.color": "#fff",
  271. // "range.bar.color": "#666",
  272. // "range.subbar.color": "#d1d1d1",
  273. // "range.disabledPointer.color": "#414141",
  274. // "range.disabledBar.color": "#282828",
  275. // "range.disabledSubbar.color": "#414141",
  276. // "range.value.color": "#fff",
  277. // "range.value.fontWeight": "lighter",
  278. // "range.value.fontSize": "11px",
  279. // "range.value.border": "1px solid #353535",
  280. // "range.value.backgroundColor": "#151515",
  281. // "range.title.color": "#fff",
  282. // "range.title.fontWeight": "lighter",
  283. // // colorpicker style
  284. // "colorpicker.button.border": "1px solid #1e1e1e",
  285. // "colorpicker.title.color": "#fff",
  286. // -----------------------
  287. "common.bi.image": "", // 左上角logo图片
  288. "common.bisize.width": "0px",
  289. "common.bisize.height": "0px",
  290. "common.backgroundImage": "none",
  291. "common.backgroundColor": "#f3f4f6",
  292. "common.border": "1px solid #333",
  293. // header
  294. "header.backgroundImage": "none",
  295. "header.backgroundColor": "#f3f4f6",
  296. "header.border": "0px",
  297. // load button
  298. "loadButton.backgroundColor": "#fff",
  299. "loadButton.border": "1px solid #ddd",
  300. "loadButton.color": "#222",
  301. "loadButton.fontFamily": "NotoSans, sans-serif",
  302. "loadButton.fontSize": "12px",
  303. // "loadButton.display": "none", // 隐藏
  304. // download button
  305. "downloadButton.backgroundColor": "#fdba3b",
  306. "downloadButton.border": "1px solid #fdba3b",
  307. "downloadButton.color": "#fff",
  308. "downloadButton.fontFamily": "NotoSans, sans-serif",
  309. "downloadButton.fontSize": "12px",
  310. // "downloadButton.display": "none", // 隐藏
  311. // icons default
  312. "menu.normalIcon.color": "#8a8a8a",
  313. "menu.activeIcon.color": "#555555",
  314. "menu.disabledIcon.color": "#ccc",
  315. "menu.hoverIcon.color": "#e9e9e9",
  316. "submenu.normalIcon.color": "#8a8a8a",
  317. "submenu.activeIcon.color": "#e9e9e9",
  318. "menu.iconSize.width": "24px",
  319. "menu.iconSize.height": "24px",
  320. "submenu.iconSize.width": "32px",
  321. "submenu.iconSize.height": "32px",
  322. // submenu primary color
  323. "submenu.backgroundColor": "#1e1e1e",
  324. "submenu.partition.color": "#858585",
  325. // submenu labels
  326. "submenu.normalLabel.color": "#858585",
  327. "submenu.normalLabel.fontWeight": "lighter",
  328. "submenu.activeLabel.color": "#fff",
  329. "submenu.activeLabel.fontWeight": "lighter",
  330. // checkbox style
  331. "checkbox.border": "1px solid #ccc",
  332. "checkbox.backgroundColor": "#fff",
  333. // rango style
  334. "range.pointer.color": "#fff",
  335. "range.bar.color": "#666",
  336. "range.subbar.color": "#d1d1d1",
  337. "range.disabledPointer.color": "#414141",
  338. "range.disabledBar.color": "#282828",
  339. "range.disabledSubbar.color": "#414141",
  340. "range.value.color": "#fff",
  341. "range.value.fontWeight": "lighter",
  342. "range.value.fontSize": "11px",
  343. "range.value.border": "1px solid #353535",
  344. "range.value.backgroundColor": "#151515",
  345. "range.title.color": "#fff",
  346. "range.title.fontWeight": "lighter",
  347. // colorpicker style
  348. "colorpicker.button.border": "1px solid #1e1e1e",
  349. "colorpicker.title.color": "#fff",
  350. };
  351. onMounted(() => {
  352. // let options = props.options;
  353. // if (props.includeUi) {
  354. // options = Object.assign(includeUIOptions, props.options);
  355. // }
  356. // options.includeUI.locale = zh_local;
  357. // options.includeUI.theme = theme;
  358. const opts = {
  359. includeUI: {
  360. loadImage: {
  361. // path: "https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/c1d7a1feb60346449c1a64893888989a~tplv-k3u1fbpfcp-watermark.image",
  362. // path: 'http://localhost/src/assets/slider/logo.jpg',
  363. path: props.image_url,
  364. name: "image",
  365. },
  366. initMenu: "draw", // 默认打开的菜单项
  367. menuBarPosition: "bottom", // 菜单所在的位置
  368. locale: zh_local, // 本地化语言为中文
  369. theme: theme
  370. },
  371. cssMaxWidth: 1000, // canvas 最大宽度
  372. cssMaxHeight: 600, // canvas 最大高度
  373. }
  374. editorInstance = new ImageEditor(editor.value, opts);
  375. addEventListener();
  376. });
  377. onUnmounted(() => {
  378. // Object.keys(this.$listeners).forEach((eventName) => {
  379. // editorInstance.off(eventName);
  380. // });
  381. editorInstance.destroy();
  382. editorInstance = null;
  383. });
  384. </script>
  385. <style>
  386. .image-editor-body .tui-image-editor{
  387. top: 0 !important;
  388. }
  389. </style>