简介

ToastUI Image Editor 是一个基于 HTML5 Canvas 的图片编辑器,它使用起来非常简单,而且内置了丰富的图片编辑功能。它支持原生 JavaScript、Vue 组件 和  React 组件三种使用方式。

官网地址 https://nhn.github.io/tui.image-editor/latest/ 

git地址    http://nhn.github.io/tui.image-editor/latest/ImageEditor#loadImageFromURL

安装

cnpm  i tui-image-editor --save

引入

import 'tui-image-editor/dist/tui-image-editor.css'
import 'tui-color-picker/dist/tui-color-picker.css'
const ImageEditor = require('tui-image-editor')

使用

添加配置前

// 使用编辑组件
this.instance = new ImageEditor(
  document.querySelector('#tui-image-editor'),
  {
    includeUI: { // 表示使用它内置的 UI 控件
      loadImage: {
        path: url,// 照片路径
        name: this.imageName //图片名称
      }, // 默认加载的图片
      initMenu: 'draw', // 表示编辑器加载后,第一个被选中的操作菜单功能
      menu: [
        'crop', // 裁切
        'flip', // 翻转
        'rotate', // 旋转
        'draw', // 添加绘画
        'shape', // 添加形状
        'icon', // 添加图标
        'text', // 添加文本
        'mask', // 添加覆盖
        'filter' // 添加滤镜
      ], // 支持的菜单
      locale: localeZh, // 国际化对照字段
      theme: customTheme,// 自定义的主题配置
      usageStatistics: false, 
      menuBarPosition: 'left'// 菜单位置栏的位置,有四个选项可选:'top', 'bottom', 'left', 'right'
    },
    cssMaxWidth: 1100, // 编辑器 Canvas 的最大宽度
    cssMaxHeight: 500, // 编辑器 Canvas 的最大高度
    selectionStyle: {
      cornerSize: 20,
      rotatingPointOffset: 70
    }
  })

可以汉化标签,对应的翻译对照字段如下

  const localeZh = {
    // override default English locale to your custom
    Crop: '裁剪',
    ZoomIn: '放大',
    ZoomOut: '缩小',
    Hand: '拖拽',
    History: '历史记录',
    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: '混合色'
    // etc...
  }

自定义的主题配置如下

  const customTheme = {
    // image 左上角度图片
    'common.bi.image': '', // 在这里换上你喜欢的logo图片
    'common.bisize.width': '0px',
    'common.bisize.height': '0px',
    'common.backgroundImage': 'none',
    'common.backgroundColor': '#f3f4f6',
    'common.border': '1px solid #444',

    // header
    'header.backgroundImage': 'none',
    'header.backgroundColor': '#f3f4f6',
    'header.border': '0px',
    'header.display': 'none',

    // 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', // 可以直接隐藏掉

    // icons default
    'menu.normalIcon.color': '#8a8a8a',
    'menu.activeIcon.color': '#555555',
    'menu.disabledIcon.color': '#434343',
    'menu.hoverIcon.color': '#e9e9e9',
    'submenu.normalIcon.color': '#8a8a8a',
    'submenu.activeIcon.color': '#e9e9e9',

    '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': '#858585',
    'submenu.normalLabel.fontWeight': 'lighter',
    'submenu.activeLabel.color': '#fff',
    'submenu.activeLabel.fontWeight': 'lighter',

    // checkbox style
    'checkbox.border': '1px solid #ccc',
    'checkbox.backgroundColor': '#fff',

    // rango style
    'range.pointer.color': '#fff',
    'range.bar.color': '#666',
    'range.subbar.color': '#d1d1d1',

    'range.disabledPointer.color': '#414141',
    'range.disabledBar.color': '#282828',
    'range.disabledSubbar.color': '#414141',

    'range.value.color': '#fff',
    'range.value.fontWeight': 'lighter',
    'range.value.fontSize': '11px',
    'range.value.border': '1px solid #353535',
    'range.value.backgroundColor': '#151515',
    'range.title.color': '#fff',
    'range.title.fontWeight': 'lighter',

    // colorpicker style
    'colorpicker.button.border': '1px solid #1e1e1e',
    'colorpicker.title.color': '#fff'
  }

图片位置置顶

 // 图片位置指定
document.getElementsByClassName('tui-image-editor-main')[0].style.top = 0
 // 隐藏删除按钮
document.getElementsByClassName('tie-btn-delete tui-image-editor-item help')[0].style.display = 'none'

加载一个图片

this.instance.loadImageFromURL('http://url/testImage.png', 'lena').then(result => {
     console.log('old : ' + result.oldWidth + ', ' + result.oldHeight);
     console.log('new : ' + result.newWidth + ', ' + result.newHeight);
});
// 根据url加载一个新的图片

添加保存功能


 // 保存图片
uploadImg () {
  const base64String = this.instance.toDataURL()
  const data = window.atob(base64String.split(',')[1])
  const ia = new Uint8Array(data.length)
  for (let i = 0; i < data.length; i++) {
    ia[i] = data.charCodeAt(i)
  }
  // 将图片转换成二进制形式发送给后台
  const blob = new Blob([ia], { type: 'image/png' })
  const fd = new FormData()
  fd.append('file', blob)
  // 获取上传需要的参数
  // 调用上传接口
  let params = { name: this.imageName, rid: this.entity.rid, file: fd }
  this.loading.isLoading = true
  this.serverApi({
    loading: this.loading,
    params: params,
    interface: this.$https.uploadEditedImage,
    tips: true,
    success: () => {
        console.log('保存成功')
    }
  })
},

最终效果

整个代码文件如下(其中有封装的方法和组件部分)

<template>
  <div>
    <info-dialog
      v-if="isShowDialog"
      id="previewImage"
      v-loading="loading.isLoading"
      :title="$t('messages.previewImage')"
      width="80%"
      height="calc(100vh - 0px)"
      :isShowDialog="isShowDialog"
      :dialogOperates="dialogOperates"
      @closeDialog="closeDialog"
      @confirmDialog="closeDialog"
    >
      <div
        v-if="isShowEditImage"
        class="container"
      >
        <div
          id="tui-image-editor"
        />
      </div>
      <el-image
        v-for="(item, index) in urls"
        :key="index"
        style="width: 80px; height: 80px;padding: 10px"
        fit="fill"
        :src="item"
        @click="editImage(item)"
      />
      <!--      <el-carousel
        type="card"
        arrow="always"
        :loop="false"
        :initial-index="1"
        indicator-position="none"
        :autoplay="false"
      >
        <el-carousel-item
          v-for="(items, index) in item.userInfo1"
          :key="index"
        >
          <div class="div2">
            <div>
              <div
                v-show="item.state===0 || item.state===1"
                style="position: absolute;"
              >
                <img
                  src="http://192.168.1.81:9000/sink/image/2018-0.tif_0.80?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=minio%2F20210312%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20210312T031818Z&X-Amz-Expires=604800&X-Amz-SignedHeaders=host&X-Amz-Signature=272e8a2bd75d22cf4c9fdb3f55fdc2fd7f0cbd0610b34e07da5cfa7a02addf43"
                  class="deleteStyle"
                  @click="deleteImg(items,item)"
                >
              </div>
              <img
                :src="items.b_img.url"
                class="headImgStyle"
              >
            </div>
            <div class="nickname">
              {{ items.b_nickname }}
            </div>
          </div>
        </el-carousel-item>
      </el-carousel>-->
    </info-dialog>
  </div>
</template>
<script>
  import 'tui-image-editor/dist/tui-image-editor.css'
  import 'tui-color-picker/dist/tui-color-picker.css'

  const ImageEditor = require('tui-image-editor')
  const localeZh = {
    // override default English locale to your custom
    Crop: '裁剪',
    ZoomIn: '放大',
    ZoomOut: '缩小',
    Hand: '拖拽',
    History: '历史记录',
    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: '混合色'
    // etc...
  }
  const customTheme = {
    // image 左上角度图片
    'common.bi.image': '', // 在这里换上你喜欢的logo图片
    'common.bisize.width': '0px',
    'common.bisize.height': '0px',
    'common.backgroundImage': 'none',
    'common.backgroundColor': '#f3f4f6',
    'common.border': '1px solid #444',

    // header
    'header.backgroundImage': 'none',
    'header.backgroundColor': '#f3f4f6',
    'header.border': '0px',
    'header.display': 'none',

    // 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', // 可以直接隐藏掉

    // icons default
    'menu.normalIcon.color': '#8a8a8a',
    'menu.activeIcon.color': '#555555',
    'menu.disabledIcon.color': '#434343',
    'menu.hoverIcon.color': '#e9e9e9',
    'submenu.normalIcon.color': '#8a8a8a',
    'submenu.activeIcon.color': '#e9e9e9',

    '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': '#858585',
    'submenu.normalLabel.fontWeight': 'lighter',
    'submenu.activeLabel.color': '#fff',
    'submenu.activeLabel.fontWeight': 'lighter',

    // checkbox style
    'checkbox.border': '1px solid #ccc',
    'checkbox.backgroundColor': '#fff',

    // rango style
    'range.pointer.color': '#fff',
    'range.bar.color': '#666',
    'range.subbar.color': '#d1d1d1',

    'range.disabledPointer.color': '#414141',
    'range.disabledBar.color': '#282828',
    'range.disabledSubbar.color': '#414141',

    'range.value.color': '#fff',
    'range.value.fontWeight': 'lighter',
    'range.value.fontSize': '11px',
    'range.value.border': '1px solid #353535',
    'range.value.backgroundColor': '#151515',
    'range.title.color': '#fff',
    'range.title.fontWeight': 'lighter',

    // colorpicker style
    'colorpicker.button.border': '1px solid #1e1e1e',
    'colorpicker.title.color': '#fff'
  }
  export default {
    name: 'PreviewImage',
    data () {
      return {
        imageName: '', // 保存图片时候的名称
        entity: {}, // 当前数据
        instance: null, // 编辑图片实例
        isShowEditImage: false,
        loading: { isLoading: false },
        urls: [], // 图片路径
        isShowDialog: false, // 显示弹框
        dialogOperates: [{
          label: 'save',
          type: 'primary',
          plain: true,
          method: () => {
            this.uploadImg()
          }
        }, {
          label: 'cancel',
          type: 'primary',
          plain: true,
          method: () => {
            this.closeDialog('history')
          }
        }]
      }
    },
    methods: {
      // 保存图片
      uploadImg () {
        const base64String = this.instance.toDataURL()
        const data = window.atob(base64String.split(',')[1])
        const ia = new Uint8Array(data.length)
        for (let i = 0; i < data.length; i++) {
          ia[i] = data.charCodeAt(i)
        }
        const blob = new Blob([ia], { type: 'image/png' })
        const fd = new FormData()
        fd.append('file', blob)
        // 获取上传需要的参数
        // 调用上传接口
        let params = { name: this.imageName, rid: this.entity.rid, file: fd }
        this.loading.isLoading = true
        this.serverApi({
          loading: this.loading,
          params: params,
          interface: this.$https.uploadEditedImage,
          tips: true,
          success: () => {
          }
        })
      },
      // 编辑图片
      editImage (url) {
        // 根据图片路径获取图片名称
        let route = url.split('?')[0]
        let name = route.split('/')
        this.imageName = name[name.length - 1]
        // 显示图片
        this.isShowEditImage = true
        // 使用编辑组件
        this.instance = new ImageEditor(
          document.querySelector('#tui-image-editor'),
          {
            includeUI: { // 表示使用它内置的 UI 控件
              loadImage: {
                path: url,
                name: this.imageName
              }, // 默认加载的图片
              initMenu: 'draw', // 表示编辑器加载后,第一个被选中的操作菜单功能
              menu: [
                'crop', // 裁切
                'flip', // 翻转
                'rotate', // 旋转
                'draw', // 添加绘画
                'shape', // 添加形状
                'icon', // 添加图标
                'text', // 添加文本
                'mask', // 添加覆盖
                'filter' // 添加滤镜
              ], // 支持的菜单
              locale: localeZh,
              theme: customTheme,
              usageStatistics: false,
              menuBarPosition: 'left'// 菜单位置栏的位置,有四个选项可选:'top', 'bottom', 'left', 'right'
            },
            cssMaxWidth: 1100, // 编辑器 Canvas 的最大宽度
            cssMaxHeight: 500, // 编辑器 Canvas 的最大高度
            selectionStyle: {
              cornerSize: 20,
              rotatingPointOffset: 70
            }
          })
        document.getElementsByClassName('tui-image-editor-main')[0].style.top = 0
        // 隐藏删除按钮
        document.getElementsByClassName('tie-btn-delete tui-image-editor-item help')[0].style.display = 'none'
      },
      // 显示弹框
      showDialog (rid) {
        this.isShowEditImage = false
        this.loading.isLoading = true
        // 获取指定rid数据信息
        this.serverApi({
          loading: this.loading,
          params: rid,
          interface: this.$https.overviewImage,
          success: (res) => {
            this.isShowDialog = true
            this.entity = res
            // 显示查询结果的url
            this.urls = res?.urls.map(item => {
              return this.decrypt(item)
            })
            this.editImage(this.urls[0])
          }
        })
      },
      // 关闭弹框
      closeDialog () {
        this.isShowDialog = false
      }
    }
  }
</script>
<style lang="scss" scoped>
  #previewImage {
    .container {
      height: calc(100vh - 400px);
    }
  }
</style>

 

Logo

前往低代码交流专区

更多推荐