乐趣区

关于前端:一款强大到没朋友的图片编辑插件爱了爱了

前言

最近用户提出了一个新的需要,老师能够批改学生的图片作业,须要对图片进行旋转、缩放、裁剪、涂鸦、标注、增加文本等。乍一听,又要掉不少头发。有没有功能强大的插件实现以上性能,让我有更多的工夫去阻止女票双十一剁手呢?答案当然是有的。

成果展现

涂鸦

裁剪

标注

旋转

滤镜

是不是很弱小!还有泛滥性能我就不一一展现了。那么还等什么,跟我一起用起来吧~

装置

npm i tui-image-editor
// or
yarn add tui-image-editor

应用

疾速体验

复制以下代码,将插件引入到本人的我的项目中。

<template>
  <div class="drawing-container">
    <div id="tui-image-editor"></div>
  </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";
export default {data() {
    return {instance: null,};
  },
  mounted() {this.init();
  },
  methods: {init() {
      this.instance = new ImageEditor(document.querySelector("#tui-image-editor"),
        {
          includeUI: {
            loadImage: {
              path: "https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/c1d7a1feb60346449c1a64893888989a~tplv-k3u1fbpfcp-watermark.image",
              name: "image",
            },
            initMenu: "draw", // 默认关上的菜单项
            menuBarPosition: "bottom", // 菜单所在的地位
          },
          cssMaxWidth: 1000, // canvas 最大宽度
          cssMaxHeight: 600, // canvas 最大高度
        }
      );
      document.getElementsByClassName("tui-image-editor-main")[0].style.top = "45px"; // 图片距顶部工具栏的间隔
    },
  },
};
</script>

<style lang="scss" scoped>
.drawing-container {height: 900px;}
</style>

能够看到活生生的图片编辑工具就呈现了,是不是很简略:

国际化

因为是老外开发的,默认的文字描述都是英文,这里咱们先汉化一下:

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": "锁定宽高比例",
};

this.instance = new ImageEditor(document.querySelector("#tui-image-editor"),
  {
    includeUI: {
      loadImage: {
        path: "https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/c1d7a1feb60346449c1a64893888989a~tplv-k3u1fbpfcp-watermark.image",
        name: "image",
      },
      initMenu: "draw", // 默认关上的菜单项
      menuBarPosition: "bottom", // 菜单所在的地位
      locale: locale_zh, // 本地化语言为中文
    },
    cssMaxWidth: 1000, // canvas 最大宽度
    cssMaxHeight: 600, // canvas 最大高度
  }
);

成果如下:

自定义款式

默认格调为暗黑系,如果想改成白底,或者想扭转按钮的大小、色彩等款式,能够应用自定义款式。

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", // 暗藏

  // icons default
  "menu.normalIcon.color": "#8a8a8a",
  "menu.activeIcon.color": "#555555",
  "menu.disabledIcon.color": "#ccc",
  "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",
};

this.instance = new ImageEditor(document.querySelector("#tui-image-editor"),
  {
    includeUI: {
      loadImage: {
        path: "https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/c1d7a1feb60346449c1a64893888989a~tplv-k3u1fbpfcp-watermark.image",
        name: "image",
      },
      initMenu: "draw", // 默认关上的菜单项
      menuBarPosition: "bottom", // 菜单所在的地位
      locale: locale_zh, // 本地化语言为中文
      theme: customTheme, // 自定义款式
    },
    cssMaxWidth: 1000, // canvas 最大宽度
    cssMaxHeight: 600, // canvas 最大高度
  }
);

成果如下:

按钮优化

通过自定义款式,咱们看到右上角的 Load 和 Download 按钮曾经被暗藏了,接下来咱们再暗藏掉其余用不上的按钮(依据业务须要),并增加一个保留图片的按钮。

<template>
  <div class="drawing-container">
    <div id="tui-image-editor"></div>
    <el-button class="save" type="primary" size="small" @click="save"> 保留 </el-button>
  </div>
</template>

// ...
methods: {init() {
    this.instance = new ImageEditor(document.querySelector("#tui-image-editor"),
      {
        includeUI: {
          loadImage: {
            path: "https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/c1d7a1feb60346449c1a64893888989a~tplv-k3u1fbpfcp-watermark.image",
            name: "image",
          },
          menu: ["resize", "crop", "rotate", "draw", "shape", "icon", "text", "filter"], // 底部菜单按钮列表 暗藏镜像 flip 和遮罩 mask
          initMenu: "draw", // 默认关上的菜单项
          menuBarPosition: "bottom", // 菜单所在的地位
          locale: locale_zh, // 本地化语言为中文
          theme: customTheme, // 自定义款式
        },
        cssMaxWidth: 1000, // canvas 最大宽度
        cssMaxHeight: 600, // canvas 最大高度
      }
    );
    document.getElementsByClassName("tui-image-editor-main")[0].style.top ="45px"; // 调整图片显示地位
    document.getElementsByClassName("tie-btn-reset tui-image-editor-item help") [0].style.display = "none";  // 暗藏顶部重置按钮
  },
  // 保留图片,并上传
  save() {const base64String = this.instance.toDataURL(); // base64 文件
    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"}); // blob 文件
    const form = new FormData();
    form.append("image", blob);
    // upload file
  },
}

<style lang="scss" scoped>
.drawing-container {
  height: 900px;
  position: relative;
  .save {
    position: absolute;
    right: 50px;
    top: 15px;
  }
}
</style>

成果如下:

能够看到顶部的重置按钮,以及底部的镜像和遮罩按钮都曾经不见了。右上角多了一个咱们本人的保留按钮,点击按钮,能够获取到 base64 文件和 blob 文件。

残缺代码

<template>
  <div class="drawing-container">
    <div id="tui-image-editor"></div>
    <el-button class="save" type="primary" size="small" @click="save"> 保留 </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_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': '锁定宽高比例'
}

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", // 暗藏

  // icons default
  "menu.normalIcon.color": "#8a8a8a",
  "menu.activeIcon.color": "#555555",
  "menu.disabledIcon.color": "#ccc",
  "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 {data() {
    return {instance: null}
  },
  mounted() {this.init()
  },
  methods: {init() {this.instance = new ImageEditor(document.querySelector('#tui-image-editor'), {
        includeUI: {
          loadImage: {
            path: 'https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/c1d7a1feb60346449c1a64893888989a~tplv-k3u1fbpfcp-watermark.image',
            name: 'image'
          },
          menu: ['resize', 'crop', 'rotate', 'draw', 'shape', 'icon', 'text', 'filter'], // 底部菜单按钮列表 暗藏镜像 flip 和遮罩 mask
          initMenu: 'draw', // 默认关上的菜单项
          menuBarPosition: 'bottom', // 菜单所在的地位
          locale: locale_zh, // 本地化语言为中文
          theme: customTheme // 自定义款式
        },
        cssMaxWidth: 1000, // canvas 最大宽度
        cssMaxHeight: 600 // canvas 最大高度
      })
      document.getElementsByClassName('tui-image-editor-main')[0].style.top = '45px' // 调整图片显示地位
      document.getElementsByClassName('tie-btn-reset tui-image-editor-item help')[0].style.display = 'none' // 暗藏顶部重置按钮
    },
    // 保留图片,并上传
    save() {const base64String = this.instance.toDataURL() // base64 文件
      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'}) // blob 文件
      const form = new FormData()
      form.append('image', blob)
      // upload file
    }
  }
}
</script>

<style lang="scss" scoped>
.drawing-container {
  height: 900px;
  position: relative;
  .save {
    position: absolute;
    right: 50px;
    top: 15px;
  }
}
</style>

总结

以上就是 tui.image-editor 的根本应用办法,相比其余插件,tui.image-editor 的劣势是功能强大,简略易上手。

插件诚然好用,但自己也发现一个小 bug,当放大图片,用手掌拖动显示地位,再点击重置按钮时,图片很可能就隐没不见了。解决办法有两个,一是改源码,在重置之前,先调用 resetZoom 办法,还原缩放比列;二是本人做一个重置按钮,点击之后调用 this.init 办法从新进行渲染。

赠人玫瑰,手有余香。如果感觉有用,就动动发财的小手,点个赞把~😜

更多 API 及 Demo 请参考:

github 地址:https://github.com/nhn/tui.im…

API 及 Examples 地址:http://nhn.github.io/tui.imag…

其余插件

每一款都是自己亲测,精挑细选、凤毛麟角。让你有更多工夫去摸🐟

Vue 1 分钟实现右键菜单,懒人的福音

Vue 如何疾速实现头像裁剪?办法比你设想的简略

如何在富文本中插入表情,word 文档,及数学公式?

1 分钟实现拖拽,让你划水更自在

退出移动版