业务背景:
老师在线批改学员的作业,学员之前提交的是文件格式,word,pdf格式的,老师需要下载下来去批改然后再上传,比较麻烦,现在改为 学员提交作业的 图片,老师在线批改学员上传的图片,方便老师操作。
技术栈:
vue2
插件:
npm install tui-image-editor --save
"tui-image-editor": "^3.15.3", 目前使用的是这个版本
开搞
原版
魔改后样式
1.弄一个公共的组件,components/tuiImageEditor/index.vue
模板,由于我们的作业是一道题一道题的,所以一道题里面可能会有多张学员上传的图片,所以这个整个id 会是循环里面给的要动态的才行,一个id 只能渲染一个canvas,所以只好这样了。
下面的整个operation 是魔改的一部分 ,具体看后续
2.dom
<template>
<div class="boardBox">
<!-- 绘图组件容器DOM -->
<div :id="id" />
<div class="operation">
<div class="cancel">
<div
tooltip-content="撤销"
class="tie-btn-undo tui-image-editor-item help enabled"
@click="cancel"
>
<el-tooltip
class="item"
effect="dark"
content="撤销"
placement="right"
>
<img src="@/assets/correction/cancel.png" alt="">
</el-tooltip>
</div>
</div>
</div>
</div>
</template>
3.组件js 部分
<script>
import 'tui-image-editor/dist/tui-image-editor.css'
import 'tui-color-picker/dist/tui-color-picker.css'
import { locale_zh, customTheme } from './base.js'//汉化
import { aliOssUploadBase64Data } from '@/utils/uploadOss' // 阿里云直传oss
const ImageEditor = require('tui-image-editor') // 这个插件npm的话只能通过require引入
export default {
name: 'SignImage',
props: {
question: { // 问题数据
type: String,
default: ''
},
id: { // id
type: String,
default: ''
},
indexItem: { //index
type: [String, Number],
default: 0
}
},
data() {
return {
// 创建的画布对象
instance: null,
height: '',
isEdit: false
}
},
watch: {
instance: {
deep: true,
handler(val) { // 如果canvas渲染完的话 会得到这个newHeight
if (val?.ui?.imageSize?.newHeight) {
if (
!this.height &&
document.querySelector(`#${this.id}.tui-image-editor-container`)
) {
// 这里需要处理一下 因为 canvas 的最大宽度是704所以要计算一下比例
this.height = Math.ceil(
(val.ui.imageSize.newHeight / val.ui.imageSize.newWidth) * 704
)
// console.log('sdsds', val)
document.querySelector(
`#${this.id}.tui-image-editor-container`
).style.height = this.height + 'px'
}
}
},
immediate: true
}
},
// 页面创建完成后调用
mounted() {
this.init()
},
methods: {
init() {
// 创建tui-image-editor组件实例,后续操作需要用到this.instance这个对象
this.instance = new ImageEditor(document.querySelector(`#${this.id}`), {
usageStatistics: false, // 这个一定要写要不然会报错
includeUI: {
// 默认加载的图片
loadImage: {
// 图片路径
// path: this.question,
path: '你的图片地址',
// 图片的名字,可以省略
name: 'image'
},
// 默认开启绘图的功能,小屏幕情况下,直接打开菜单,会占用较大屏幕空间,不美观
// initMenu: 'draw',
initMenu: '',
// 支持的菜单
menu: [
// 'crop', // 裁切
'draw', // 添加绘画
'text', // 添加文本
// 'rotate', // 旋转
// 'flip', // 翻转
'shape', // 添加形状
'icon' // 添加图标
// 'mask', // 添加覆盖
// 'filter' // 添加滤镜
],
// 菜单位置在下面
menuBarPosition: 'left',
// 汉化
locale: locale_zh,
// 自定义样式(隐藏默认顶部栏目、按钮颜色。。。)
theme: customTheme
},
// 设置画布的最大宽高,能自动等比例缩放大图片到指定的宽高内
// TODO:可以监听当前页面的缩放,动态修改画布最大宽高以防止图片过大
cssMaxWidth: 704,
cssMaxHeight: 3000,
paint: {
color: '#ff0000', // 这里设置你想要的默认画笔颜色
width: 10 // 画笔宽度
// ... 其他画笔选项
},
selectionStyle: {}
})
this.drawColor('#ff0000') // 改变默认画笔的颜色
this.settingConfig() // 隐藏默认项
// 鼠标按下
this.instance.on('mousedown', (event, originPointer) => { //鼠标按下 隐藏操作二级菜单,优化用户交互
console.log('鼠标')
document
.querySelector(`#${this.id} .tui-image-editor-main`)
.classList.remove('tui-image-editor-menu-draw')
})
this.instance.on('undoStackChanged', (length) => { // 这个地方 判断用户是否对canvas 进行了操作
console.log('undo', length)
// 如果length> 0 即为已修改
if (length > 0) {
this.isEdit = true
} else if (length == 0) {
this.isEdit = false
}
})
},
settingConfig() {
this.$nextTick(() => {
document
.querySelector(`#${this.id} .tui-image-editor-main`)
.classList.remove('tui-image-editor-menu-draw')
})
},
drawColor(color) {
// 画笔颜色
this.instance.ui.draw.color = color
document.querySelector(
`#${this.id} .color-picker-value`
).style.backgroundColor = color
document.querySelector(
`#${this.id} .tui-colorpicker-palette-preview`
).style.backgroundColor = color
document.querySelector(
`#${this.id} .tui-colorpicker-palette-preview`
).style.color = color
document.querySelector(
`#${this.id} .tui-colorpicker-palette-hex`
).style.value = color
},
cancel() { // 这个就是上边自定义的撤销按钮,回滚也是同样意思redo
console.log('ddd', this.instance)
this.instance
.undo()
.then((res) => {
console.log('ss', res)
})
.catch((e) => {
console.log('d', e)
})
},
/** 保存编辑后图片 */
async handleCanvas2Img() { // 由于操作完之后得到的图片数据是base64格式的,所以先转成file对象,然后file的对象再直传阿里云oss, 如果接了阿里云直传的base64格式的话就不用转成file对象了
// 调用组件官方方法,获取整个编辑后图片的base64数据
console.log('dd', this.instance)
const base64String = this.instance.toDataURL()
const file = this.dataURLtoFile(base64String)
try {
const res = await aliOssUploadBase64Data({
name: this.getValue(this.question),
file
})
return Promise.resolve({ res, index: this.indexItem })
} catch (error) {
return Promise.reject(error)
}
},
// 将base64转换成file类型,用于给tui-image-editor组件的官方方法调用,计算获得当前图片的宽高
// ? this.instance.loadImageFromFile这个官方的方法会返回图片的宽高,但是传入的必须时file类型的文件
dataURLtoFile(dataurl) {
var arr = dataurl.split(',')
var mime = arr[0].match(/:(.*?);/)[1]
var bstr = atob(arr[1])
var n = bstr.length
var u8arr = new Uint8Array(n)
while (n--) {
u8arr[n] = bstr.charCodeAt(n)
}
return new File([u8arr], { type: mime })
},
}
</script>
4.上边操作撤销的按钮的样式,以及魔改的插件区域的样式,插件里面好多都是display:table,改起来不好改,所以磨了一两天
<style lang="scss" scoped>
.operation {
position: absolute;
left: 0;
top: calc(50% + 70px);
transform: translateY(50%);
width: 64px;
height: 64px;
display: flex;
justify-content: center;
z-index: 66;
.cancel div {
cursor: pointer;
img {
width: 24px;
height: 24px;
}
img:hover {
content: url('../../assets/correction/cancel-active.png');
}
}
}
.boardBox {
::v-deep .tui-image-editor-container .tui-image-editor-controls {
background-color: #ffffff;
}
::v-deep .tui-image-editor-submenu {
padding: 20px 0;
top: 50%;
transform: translateY(-50%);
.tui-image-editor-submenu-style {
background: #ffffff !important;
}
}
::v-deep .tui-image-editor-header {
display: none;
}
::v-deep .tui-image-editor-container {
max-height: 3000px;
}
::v-deep .tui-image-editor-main-container {
border: none !important;
background-color: #ffffff;
}
::v-deep .tui-image-editor-help-menu {
display: none;
}
::v-deep .tui-image-editor-container .tui-image-editor-range-wrap label {
color: #000;
font-weight: lighter;
}
::v-deep .tui-image-editor {
left: 0 !important;
}
::v-deep .tui-image-editor-container .tui-image-editor-main {
top: 0;
}
::v-deep .tui-image-editor-wrap {
background: #ffffff;
overflow: hidden;
}
::v-deep .tui-image-editor-controls {
position: sticky;
top: 74px;
}
}
</style>
5.汉化的base.js
export const locale_zh = {
ZoomIn: '放大',
ZoomOut: '缩小',
Hand: '手掌',
History: '历史',
Resize: '调整宽高',
Crop: '裁剪',
DeleteAll: '全部删除',
Delete: '删除',
Undo: '撤销',
Redo: '反撤销',
Reset: '重置',
Flip: '镜像',
Rotate: '旋转',
Draw: '画',
Shape: '形状标注',
Icon: '图标标注',
Text: '文字标注',
Mask: '遮罩',
Filter: '滤镜',
Bold: '加粗',
Italic: '斜体',
Underline: '下划线',
Left: '左对齐',
Center: '居中',
Right: '右对齐',
Color: '颜色',
'Text size': '字体大小',
Custom: '自定义',
Square: '正方形',
Apply: '应用',
Cancel: '取消',
'Flip X': 'X 轴',
'Flip Y': 'Y 轴',
Range: '区间',
Stroke: '描边',
Fill: '填充',
Circle: '圆',
Triangle: '三角',
Rectangle: '矩形',
Free: '曲线',
Straight: '直线',
Arrow: '箭头',
'Arrow-2': '箭头2',
'Arrow-3': '箭头3',
'Star-1': '星星1',
'Star-2': '星星2',
Polygon: '多边形',
Location: '定位',
Heart: '心形',
Bubble: '气泡',
'Custom icon': '自定义图标',
'Load Mask Image': '加载蒙层图片',
Grayscale: '灰度',
Blur: '模糊',
Sharpen: '锐化',
Emboss: '浮雕',
'Remove White': '除去白色',
Distance: '距离',
Brightness: '亮度',
Noise: '噪音',
'Color Filter': '彩色滤镜',
Sepia: '棕色',
Sepia2: '棕色2',
Invert: '负片',
Pixelate: '像素化',
Threshold: '阈值',
Tint: '色调',
Multiply: '正片叠底',
Blend: '混合色',
Width: '宽度',
Height: '高度',
'Lock Aspect Ratio': '锁定宽高比例'
}
export const customTheme = {
'common.bi.image': '', // 左上角logo图片
'common.bisize.width': '0px',
'common.bisize.height': '0px',
'common.backgroundImage': 'none',
'common.backgroundColor': '#f3f4f6',
'common.border': '1px solid #333',
// header
'header.backgroundImage': 'none',
'header.backgroundColor': '#f3f4f6',
'header.border': '0px',
// load button
'loadButton.backgroundColor': '#fff',
'loadButton.border': '1px solid #ddd',
'loadButton.color': '#222',
'loadButton.fontFamily': 'NotoSans, sans-serif',
'loadButton.fontSize': '12px',
'loadButton.display': 'none', // 隐藏
// download button
'downloadButton.backgroundColor': '#fdba3b',
'downloadButton.border': '1px solid #fdba3b',
'downloadButton.color': '#fff',
'downloadButton.fontFamily': 'NotoSans, sans-serif',
'downloadButton.fontSize': '12px',
'downloadButton.display': 'none', // 隐藏
// menu
'menu.backgroundColor': '#fff',
// icons default
'menu.normalIcon.color': '#8a8a8a',
'menu.activeIcon.color': '#555555',
'menu.disabledIcon.color': '#ccc',
'menu.hoverIcon.color': '#e9e9e9',
'submenu.normalIcon.color': '#000',
'submenu.activeIcon.color': '#656565',
'menu.iconSize.width': '24px',
'menu.iconSize.height': '24px',
'submenu.iconSize.width': '32px',
'submenu.iconSize.height': '32px',
// submenu primary color
'submenu.backgroundColor': '#1e1e1e',
'submenu.partition.color': '#858585',
// submenu labels
'submenu.normalLabel.color': '#000000',
'submenu.normalLabel.fontWeight': 'lighter',
'submenu.activeLabel.color': '#a1a1a1',
'submenu.activeLabel.fontWeight': 'lighter',
// checkbox style
'checkbox.border': '1px solid #ccc',
'checkbox.backgroundColor': '#000',
// rango style
'range.pointer.color': '#000',
'range.bar.color': '#666',
'range.subbar.color': '#d1d1d1',
'range.disabledPointer.color': '#414141',
'range.disabledBar.color': '#282828',
'range.disabledSubbar.color': '#414141',
'range.value.color': '#ffffff',
'range.value.fontWeight': 'lighter',
'range.value.fontSize': '11px',
'range.value.border': '1px solid #353535',
'range.value.backgroundColor': '#151515',
'range.title.color': '#000',
'range.title.fontWeight': 'lighter',
// colorpicker style
'colorpicker.button.border': '1px solid #1e1e1e',
'colorpicker.title.color': '#000'
}
6.父组件使用
组件就完成了,然后根据业务再来取数据就行了
标签:submenu,魔改,color,image,tui,range,editor From: https://blog.csdn.net/wu1198949718/article/details/140131282