首页 > 其他分享 >魔改 tui-image-editor 一切为了业务

魔改 tui-image-editor 一切为了业务

时间:2024-07-19 11:27:36浏览次数:9  
标签:submenu 魔改 color image tui range editor

业务背景:

老师在线批改学员的作业,学员之前提交的是文件格式,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

相关文章

  • 使用ElementUI和element-china-area-data库实现省市区三级联动组件封装
    使用ElementUI和element-china-area-data库实现省市区三级联动组件封装在前端开发中,省市区三级联动是一个常见的需求。今天我们将使用Vue.js和ElementUI组件库,结合element-china-area-data库,来实现一个省市区三级联动的组件。这个组件不仅可以提高用户体验,还能大大简化我们的代码......
  • SwiftUI Release 引入的辅助焦点管理
    文章目录前言使用@FocusState属性包装器高级技巧:专用辅助技术可聚焦字段的高级用法优化体验运行截图总结前言SwiftUIRelease引入了强大的新功能,其中之一是辅助焦点管理。这个新功能使得在SwiftUI中处理辅助技术(如VoiceOver和SwitchControl)的焦点状态变得......
  • EmEditor v24.2.1 汉化版
    EmEditor文本编辑器是一款功能强大且非常好用的文本编辑器!它启动速度快,可以完全代替Windows自带的记事本,足以胜任日常的文本编辑工作。良好地支持Unicode和中文字符,还支持20多种编程语言的语法突出显示。并且支持的语法种类可以不断的扩充。具有选择文本列块的功能(按ALT键拖动鼠......
  • MarkText A simple and elegant markdown editor, available for Linux, macOS and Wi
    1、这个工具挺不错的,先上一张图,来自github页面截图:2、这个工具是开源的项目,开源地址:https://github.com/marktext官网地址:www.marktext.cc/三个平台都有:可以直接点上面的按钮,找到自己所用电脑的平台,就可以下载。也可以转到Githubreleasepage下载。3、安装:点击【安......
  • Sleeping Cup #1 Editorials
    A-Sleepingpairs很明显,能删的要立刻删,它们会阻碍交换。一共要删除\(n\)列,这需要\(n\)点体力。由于删除时总要保证两列字符一致,故两列X的个数要相等。设两列X的个数原来相差\(b\)个,则交换一行XZ(或ZX)会使得某一列减少一个X,而另一列增加一个X,差值减少\(2\),故这......
  • elementui的el-cascader-panel在jsx里如何自定义label和props属性
    render(){return(<el-cascader-panelonChange={(val)=>{this.handleFormatChange(val,'format','dataColumns',indexInMap)}}props={{renderLabel:(params)=>{......
  • ElementUI 本身没有提供年份范围选择组件,但可以通过封装两个年份选择器来实现类似的功
    ElementUI本身没有提供年份范围选择组件,但可以通过封装两个年份选择器来实现类似的功能。以下是一个使用Vue2和ElementUI实现年份范围选择器的示例代码: <script>exportdefault{name:'YearRangePicker',//接收父组件传入的年份范围数据props:{value:{......
  • 如何在 SwiftUI 中熟练使用 visualEffect 修饰符
    文章目录前言介绍visualEffect什么是视觉效果?visualEffect修饰符视觉效果完整的代码总结前言在WWDC23中,SwiftUI引入了一个名为visualEffect的新视图修饰符。此修饰符允许我们通过访问特定视图的布局信息来附加一组可动画化的视觉效果。下面我们将学习如何......
  • 在 SwiftUI 中的作用域动画
    文章目录前言简单示例动画视图修饰符使用多个可动画属性使用ViewBuilder总结前言从一开始,动画就是SwiftUI最强大的功能之一。你可以在SwiftUI中快速构建流畅的动画。唯一的缺点是每当我们需要运行多步动画或将动画范围限定到视图层次结构的特定部分时,我们如......
  • swiftUI
    SwiftUI是苹果推出的一种现代化方式,用于创建跨所有Apple平台的用户界面。它通过声明性语法简化了UI的开发流程。下面是一个基本的SwiftUI示例,展示了如何使用SwiftUI构建一个简单的"HelloWorld"应用。示例步骤1.创建一个新的SwiftUI项目打开Xcode,选择"新建项目......