提示:图片编辑器tui-image-editor
文章目录
- 前言
- 一、安装tui-image-editor
- 二、新建components/ImageEditor.vue
- 三、修改App.vue
- 四、效果
- [五、遇到问题 this.getResolve is not a function](#五、遇到问题 this.getResolve is not a function)
- 总结
前言
需求:图片编辑器tui-image-editor
一、安装tui-image-editor
c
npm install tui-image-editor --save
二、新建components/ImageEditor.vue
ImageEditor.vue
c
<template>
<div class="image_editor_box" ref="dcRef">
<div id="tui_image_editor"></div>
<el-button class="image_save_btn" type="primary" size="small" @click="saveImage">保存</el-button>
</div>
</template>
<script>
import 'tui-image-editor/dist/tui-image-editor.css'
import 'tui-color-picker/dist/tui-color-picker.css'
import ImageEditor from 'tui-image-editor'
const locale = {
//菜单
ZoomIn: '放大',
ZoomOut: '缩小',
Hand: '抓手工具',
History: '历史',
Undo: '撤销',
Redo: '恢复',
Reset: '重置',
Delete: '删除',
DeleteAll: '全部删除',
//工具栏
//尺寸调整
Resize: '尺寸',
Width: '宽度',
Height: '高度',
'Lock Aspect Ratio': '锁定宽高比例',
Apply: '应用',
Cancel: '取消',
//镜像
Flip: '镜像',
'Flip X': 'X 轴',
'Flip Y': 'Y 轴',
//蒙版
Mask: '蒙版',
'Load Mask Image': '上传蒙版图片',
//裁剪
Crop: '裁剪',
Square: '正方形',
// 旋转
Rotate: '旋转',
Range: '区间',
//画笔
Draw: '画笔',
Free: '曲线',
Straight: '直线',
Color: '颜色',
//图形
Shape: '图形',
Rectangle: '矩形',
Circle: '圆形',
Triangle: '三角形',
Fill: '填充',
Stroke: '描边',
//图标
Icon: '图标',
Arrow: '箭头',
'Arrow-2': '箭头2',
'Arrow-3': '箭头3',
'Star-1': '五角星',
'Star-2': '多角形',
Polygon: '多边形',
Location: '定位',
Heart: '心形',
Bubble: '气泡',
'Custom icon': '自定义图标',
//文字
Text: '文字',
Bold: '加粗',
Italic: '斜体',
Underline: '下划线',
Left: '左对齐',
Center: '居中',
Right: '右对齐',
'Text size': '字体大小',
//滤镜
Filter: '滤镜',
Grayscale: '灰度',
Sepia: '棕色',
Blur: '模糊',
Emboss: '浮雕',
Invert: '底片',
Sepia2: '棕色2',
Sharpen: '锐化',
'Remove White': '除去白色',
Distance: '距离',
Brightness: '亮度',
Noise: '铜板雕刻',
Pixelate: '马赛克',
'Color Filter': '彩色滤镜',
Threshold: '阈值',
Tint: '色调',
Multiply: '正片叠底',
Blend: '混合色',
Custom: '自定义',
load: '上传',
download:'下载',
}
const IThemeConfig = {
//图标
'common.bi.image': 'https://t7.baidu.com/it/u=1595072465,3644073269&fm=193&f=GIF',
'common.bisize.width': '30px',
'common.bisize.height': '30px',
//编辑器背景
// 'common.backgroundImage': 'https://t7.baidu.com/it/u=1595072465,3644073269&fm=193&f=GIF',
'common.backgroundColor': '#fff',
'common.border': '1px solid #eee',
// 菜单栏样式
// 'header.backgroundImage': 'https://t7.baidu.com/it/u=1595072465,3644073269&fm=193&f=GIF',
'header.backgroundColor': 'transparent',
'header.border': '0 solid #000',
// 上传按钮
'loadButton.backgroundColor': '#ecf5ff',
'loadButton.border': '1px solid #409EFF',
'loadButton.color': '#409EFF',
'loadButton.fontFamily': "'Noto Sans', sans-serif",
'loadButton.fontSize': '12px',
// 下载按钮
'downloadButton.backgroundColor': '#409EFF',
'downloadButton.border': '1px solid #409EFF',
'downloadButton.color': '#fff',
'downloadButton.fontFamily': "'Noto Sans', sans-serif",
'downloadButton.fontSize': '12px',
// 工具栏icon
'menu.normalIcon.color': '#7A8799',
'menu.activeIcon.color': '#409EFF',
'menu.disabledIcon.color': '#A2AEBF',
'menu.hoverIcon.color': '#323D4D',
'menu.iconSize.width': '24px',
'menu.iconSize.height': '24px',
// 工具栏子菜单
'submenu.normalIcon.color': '#7A8799',
'submenu.activeIcon.color': '#323D4D',
// 工具栏子菜单icon
'submenu.iconSize.width': '24px',
'submenu.iconSize.height': '24px',
// 工具栏子菜单bg
'submenu.backgroundColor': '#eee',
// 工具栏子菜单分割线
'submenu.partition.color': '#7A8799',
//工具栏子菜单文字
'submenu.normalLabel.color': '#7A8799',
'submenu.normalLabel.fontWeight': '400',
'submenu.activeLabel.color': '#323D4D',
'submenu.activeLabel.fontWeight': '400',
// 工具栏子菜单多选框
'checkbox.border': '1px solid #7A8799',
'checkbox.backgroundColor': '#fff',
// 工具栏子菜单进度条--滑块
'range.pointer.color': '#409EFF',
// 工具栏子菜单进度条--底色
'range.bar.color': '#A2AEBF',
// 工具栏子菜单进度条--进度
'range.subbar.color': '#409EFF',
// 工具栏子菜单进度条--禁用
'range.disabledPointer.color': '#A2AEBF',
'range.disabledBar.color': '#A2AEBF',
'range.disabledSubbar.color': '#7A8799',
// 工具栏子菜单进度条--值
'range.color': '#7A8799',
'range.value.color': '#323D4D',
'range.value.fontWeight': '400',
'range.value.fontSize': '11px',
'range.value.border': '1px solid #A2AEBF',
'range.value.backgroundColor': '#fff',
'range.title.fontWeight': '400',
// 颜色选择器
'colorpicker.button.border': '1px solid #A2AEBF',
'colorpicker.title.color': '#7A8799',
};
export default {
props:{
defaultImg:{
type:Object,
default:()=>{return {}},
},
defaultColorArr:{
type:Array,
default:()=>{return []},
}
},
data() {
return {
imgEditor: null
}
},
watch:{
defaultImg(){
this.imgEditor&&this.imgEditor.loadImageFromURL(this.defaultImg.path,this.defaultImg.name);
}
},
mounted() {
this.init(this.defaultImg);
},
methods: {
//初始化ImageEditor
init(defaultImg) {
this.imgEditor = new ImageEditor(document.getElementById('tui_image_editor'), {
includeUI: {
//加载图片
loadImage: { path:defaultImg.path, name: defaultImg.name },
//尺寸 裁剪 旋转 画笔 图形 图标 文字 镜像 滤镜 蒙版
menu: ['resize', 'crop', 'rotate', 'draw', 'shape', 'icon', 'text', 'flip', 'filter', 'mask'], //不初始化 filter mask
// 默认工具
initMenu: '',
// 工具栏位置
menuBarPosition: 'bottom',
// 语言
locale: locale,
// 主题
theme: IThemeConfig
},
// 最大宽度
cssMaxWidth: (this.$refs.dcRef.clientWidth||1000)-80,
// 最大高度
cssMaxHeight: (this.$refs.dcRef.clientHeight||600)-64-48-80,
uiSize: {
width: '1000px',
height: '700px'
},
selectionStyle: {
cornerSize: 20,
rotatingPointOffset: 70
},
picker:{}
});
//Load按钮文案改成上传
const load = document.querySelector('.tui-image-editor-header-buttons>div');
load.innerHTML = load.innerHTML.replace('Load','上传');
//Download按钮文案改成下载
const download = document.querySelector('.tui-image-editor-header-buttons .tui-image-editor-download-btn');
download.innerHTML = "下载";
},
// 保存批注的图片
saveImage() {
let base64Str = this.imgEditor.toDataURL();
let blob = this.base64ToBlob(base64Str);
console.log(base64Str,blob);
this.$emit('saveEditImgSrc',base64Str);
},
//base64转换成blob
base64ToBlob(base64Str) {
let arr = base64Str.split(",");
let type = arr[0].match(/:(.*?);/)[1];
let bstr = atob(arr[1]);
let n = bstr.length;
let u8arr = new Uint8Array(n);
while (n--) {
u8arr[n] = bstr.charCodeAt(n);
}
return new Blob([u8arr], { type });
}
}
}
</script>
<style lang="scss" scoped>
.image_editor_box {
height: 100%;
width: 100%;
position: relative;
.image_save_btn {
position: absolute;
width: 120px;
height: 40px;
right: 260px;
top: 9px;
background-color: #409EFF;
border: 1px solid #409EFF;
color: #fff;
font-family: 'Noto Sans', sans-serif;
font-size: 12px;
border-radius: 40px;
}
}
</style>
<style>
/** 隐藏图标和上传下载按钮 **/
.image_editor_box .tui-image-editor-header-logo,.image_editor_box .tui-image-editor-header-buttons{
/* display: none!important; */
}
/* 调整图片显示位置 */
.image_editor_box .tui-image-editor-main{
top:48px!important;
height:auto!important;
}
.image_editor_box .tui-image-editor-container .tui-image-editor-range-wrap label{
color: #7A8799;
}
.image_editor_box .tui-image-editor-container .tui-image-editor-checkbox label > span {
color: #7A8799;
}
.image_editor_box .tui-image-editor-container .tui-image-editor-controls{
background-color: #fcfcfc;
border:1px solid #eee;
border-top:0;
}
.image_editor_box .tui-image-editor-help-menu.top{
border:1px solid #eee;
background: #fcfcfc;
}
</style>
三、修改App.vue
App.vue
c
<template>
<div id="app">
<div class="img_mark_box">
<div class="img_mark_l">
<div class="img_mark_img" v-for="item,index in imgArr" :key="index" @click="changeImg(item)"><img :src="item.path" alt=""></div>
</div>
<div class="img_mark_m"><ImageEditor :defaultImg="activeImg" @saveEditImgSrc="saveEditImgSrc"></ImageEditor></div>
<div class="img_mark_r"><img :src="editImgSrc" alt=""></div>
</div>
</div>
</template>
<script>
import ImageEditor from "./components/ImageEditor";
export default {
name: 'App',
components:{ImageEditor},
data(){
return {
activeImg:'',
editImgSrc:'',
imgArr:[
{
path:require('@/assets/test.jpg'),
name:'图片1'
},
{
path:`data:image/jpeg;base64,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`,
name:'图片2'
},
{
path:'https://t7.baidu.com/it/u=1819248061,230866778&fm=193&f=GIF',
name:'图片3'
},
{
path:'https://t7.baidu.com/it/u=1595072465,3644073269&fm=193&f=GIF',
name:'图片4'
},
{
path:'https://t7.baidu.com/it/u=825057118,3516313570&fm=193&f=GIF',
name:'图片5'
},
{
path:'https://t7.baidu.com/it/u=602106375,407124525&fm=193&f=GIF',
name:'图片6'
},
{
path:'https://t7.baidu.com/it/u=55748064,2074988836&fm=193&f=GIF',
name:'图片7'
},
],
}
},
created(){
this.activeImg = this.imgArr[0];
},
methods:{
changeImg(item){
this.activeImg = item;
},
saveEditImgSrc(src){
this.editImgSrc = src;
},
}
};
</script>
<style scoped >
.img_mark_box {
position: absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
padding: 0 200px 0 300px;
box-sizing: border-box;
}
.img_mark_l,.img_mark_r{
position: absolute;
top: 0;
bottom: 0;
}
.img_mark_l{
left: 0;
width: 300px;
box-sizing: border-box;
padding: 20px 10px;
}
.img_mark_img{
height: 80px;
box-sizing: border-box;
vertical-align: middle;
margin-bottom: 10px;
overflow: hidden;
border: 1px solid #eee;
border-radius: 8px;
cursor: pointer;
}
.img_mark_img>img{
width: 100%;
height: 100%;
}
.img_mark_r{
right: 0;
width: 200px;
box-sizing: border-box;
padding: 20px 10px;
}
.img_mark_r>img{
width: 100%;
}
.img_mark_m{
width: 100%;
height: 100%;
}
</style>
四、效果
五、遇到问题 this.getResolve is not a function
问题:
sass-loader 版本过高,导致tui-image-editor的css无法解析
解决方案:
c
npm install sass-loader@7.3.1 --save-dev
报错内容:
c
Module build failed: TypeError: this.getResolve is not a function
at Object.loader (D:\node_modules\sass-loader\dist\index.js:52:26)
@ ./node_modules/vue-style-loader!./node_modules/css-loader?{"sourceMap":true}!./node_modules/vue-loader/lib/style-compiler?{"vue":true,"id":"data-v-4913b0a8","scoped":true,"hasInlineConfig":false}!D:/node_modules/sass-loader/dist/cjs.js?{"sourceMap":true}!./node_modules/vue-loader/lib/selector.js?type=styles&index=0!./src/components/ImageEditor.vue 4:14-385 13:3-17:5 14:22-393
@ ./src/components/ImageEditor.vue
@ ./node_modules/babel-loader/lib!./node_modules/vue-loader/lib/selector.js?type=script&index=0!./src/App.vue
@ ./src/App.vue
@ ./src/main.js
@ multi (webpack)-dev-server/client?http://localhost:8080 webpack/hot/dev-server ./src/main.js
总结
踩坑路漫漫长@~@