preface

Recently, users put forward a new requirement that teachers can correct students’ picture homework, which needs to rotate, zoom, cut, doodle, mark, add text and so on. Sounds like a lot of hair to lose. Is there a powerful plugin that can do all of this so THAT I can spend more time trying to stop women from doing it on Singles’ Day? Of course there is.

Results show

graffiti

tailoring

mark

rotating

A filter

Isn’t it powerful! And I’m not going to show you all of them. So what are you waiting for, use it with me

The installation

npm i tui-image-editor
// or
yarn add tui-image-editor
Copy the code

use

Rapid experience

Copy the following code to introduce the plug-in into your own project.

<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".// Open the menu item by default
            menuBarPosition: "bottom".// The location of the menu
          },
          cssMaxWidth: 1000.// Canvas maximum width
          cssMaxHeight: 600.// Canvas maximum height});document.getElementsByClassName("tui-image-editor-main") [0].style.top = "45px"; // The distance between the image and the top toolbar,}}};</script>

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

You can see live image editing tools appear, isn’t it simple:

internationalization

Because it is developed by foreigners, the default text description is English, here we first Chinese:

const locale_zh = {
  ZoomIn: "Enlarge".ZoomOut: "Narrow".Hand: "Hand".History: 'history'.Resize: 'Adjust width and height'.Crop: "Cut".DeleteAll: "Delete all".Delete: "Delete".Undo: "Cancel".Redo: "Reverse undo".Reset: "Reset".Flip: "Mirror image".Rotate: "Rotation".Draw: "Painting".Shape: "Shape labeling".Icon: "Icon labeling".Text: "Text labeling".Mask: "Mask".Filter: "Filter".Bold: "Bold".Italic: "Italics".Underline: "Underline".Left: "Left justified".Center: "Center".Right: "Align right".Color: "Color"."Text size": "Font size".Custom: "Custom".Square: "Square".Apply: "Application".Cancel: "Cancel"."Flip X": "X"."Flip Y": "Y".Range: "Interval".Stroke: "Stroke".Fill: "Fill".Circle: "Circle".Triangle: "Triangle".Rectangle: "Rectangle".Free: "Curve".Straight: "Linear".Arrow: "Arrow"."Arrow-2": "Arrow 2"."Arrow-3": "Arrow 3"."Star-1": "The stars 1"."Star-2": "The stars 2".Polygon: "Polygon".Location: "Positioning".Heart: "Heart".Bubble: "Bubble"."Custom icon": "Custom icon"."Load Mask Image": "Load the mask image".Grayscale: "Gray".Blur: "Fuzzy".Sharpen: "Sharpen".Emboss: "Relief"."Remove White": "Remove the white".distance: "Distance".Brightness: "Brightness".Noise: "Noise"."Color Filter": "Color Filter".Sepia: "Brown".Sepia2: "Brown 2".Invert: "Negative".Pixelate: "Pixelated".Threshold: "Threshold".Tint: "Color".Multiply: Multiply.Blend: "Mixed colors".Width: "Width".Height: "Height"."Lock Aspect Ratio": "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".// Open the menu item by default
      menuBarPosition: "bottom".// The location of the menu
      locale: locale_zh, // The localization language is Chinese
    },
    cssMaxWidth: 1000.// Canvas maximum width
    cssMaxHeight: 600.// Canvas maximum height});Copy the code

The effect is as follows:

Custom styles

The default style is dark. If you want to change it to white, or change the size and color of the buttons, you can use custom styles.

const customTheme = {
  "common.bi.image": "".// Top left logo image
  "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"./ / hide

  // download button
  "downloadButton.backgroundColor": "#fdba3b"."downloadButton.border": "1px solid #fdba3b"."downloadButton.color": "#fff"."downloadButton.fontFamily": "NotoSans, sans-serif"."downloadButton.fontSize": "12px"."downloadButton.display": "none"./ / hide

  // 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".// Open the menu item by default
      menuBarPosition: "bottom".// The location of the menu
      locale: locale_zh, // The localization language is Chinese
      theme: customTheme, // Custom style
    },
    cssMaxWidth: 1000.// Canvas maximum width
    cssMaxHeight: 600.// Canvas maximum height});Copy the code

The effect is as follows:

Button to optimize

By customizing the styles, we can see that the Load and Download buttons in the upper right corner have been hidden, then we can hide the other buttons that are not needed (depending on business needs) and add a button to save the image.

<template>
  <div class="drawing-container">
    <div id="tui-image-editor"></div>
    <el-button class="save" type="primary" size="small" @click="save">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"].// Bottom menu button list hides mirror flip and mask mask
          initMenu: "draw".// Open the menu item by default
          menuBarPosition: "bottom".// The location of the menu
          locale: locale_zh, // The localization language is Chinese
          theme: customTheme, // Custom style
        },
        cssMaxWidth: 1000.// Canvas maximum width
        cssMaxHeight: 600.// Canvas maximum height});document.getElementsByClassName("tui-image-editor-main") [0].style.top ="45px"; // Adjust the image display position
    document.getElementsByClassName("tie-btn-reset tui-image-editor-item help") [0].style.display = "none";  // Hide the top reset button
  },
  // Save the image and upload it
  save() {
    const base64String = this.instance.toDataURL(); / / base64 file
    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 files
    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>
Copy the code

The effect is as follows:

You can see that the reset button at the top and the mirror and mask buttons at the bottom are gone. In the upper right corner is our own save button, which you can click to get base64 files and BLOb files.

The complete code

<template>
  <div class="drawing-container">
    <div id="tui-image-editor"></div>
    <el-button class="save" type="primary" size="small" @click="save">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: 'zoom in'.ZoomOut: 'narrow'.Hand: 'palm'.History: 'history'.Resize: 'Adjust width and height'.Crop: 'cut'.DeleteAll: 'Delete all'.Delete: 'delete'.Undo: 'undo'.Redo: 'Reverse undo'.Reset: 'reset'.Flip: 'mirror'.Rotate: 'rotation'.Draw: 'painting'.Shape: 'Shape labeling'.Icon: 'Icon labeling'.Text: 'Text labeling'.Mask: 'mask'.Filter: 'filters'.Bold: 'bold'.Italic: 'italics'.Underline: 'underline'.Left: 'Left aligned'.Center: 'center'.Right: 'Right aligned'.Color: The 'color'.'Text size': 'Font size'.Custom: 'Custom'.Square: 'Square'.Apply: 'application'.Cancel: 'cancel'.'Flip X': 'X'.'Flip Y': 'Y'.Range: 'interval'.Stroke: 'stroke'.Fill: 'fill'.Circle: 'circle'.Triangle: 'triangle'.Rectangle: 'the rectangle'.Free: 'curve'.Straight: 'straight'.Arrow: 'arrows'.'Arrow-2': Arrows' 2 '.'Arrow-3': 'arrow 3'.'Star-1': The stars' 1 '.'Star-2': The stars' 2 '.Polygon: 'Polygon'.Location: 'positioning'.Heart: 'heart'.Bubble: 'bubbles'.'Custom icon': 'Custom icon'.'Load Mask Image': 'Load the mask image'.Grayscale: 'gray'.Blur: 'fuzzy'.Sharpen: The 'sharpen'.Emboss: 'relief'.'Remove White': 'Remove the white'.distance: 'distance'.Brightness: 'brightness'.Noise: 'noise'.'Color Filter': 'Color Filter'.Sepia: 'brown'.Sepia2: 'brown 2'.Invert: 'negative'.Pixelate: 'Pixelated'.Threshold: 'threshold'.Tint: 'tone'.Multiply: 'Multiply'.Blend: 'Mixed colors'.Width: 'width'.Height: 'high'.'Lock Aspect Ratio': 'Lock aspect ratio'
}

const customTheme = {
  "common.bi.image": "".// Top left logo image
  "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"./ / hide

  // download button
  "downloadButton.backgroundColor": "#fdba3b"."downloadButton.border": "1px solid #fdba3b"."downloadButton.color": "#fff"."downloadButton.fontFamily": "NotoSans, sans-serif"."downloadButton.fontSize": "12px"."downloadButton.display": "none"./ / hide

  // 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'].// Bottom menu button list hides mirror flip and mask mask
          initMenu: 'draw'.// Open the menu item by default
          menuBarPosition: 'bottom'.// The location of the menu
          locale: locale_zh, // The localization language is Chinese
          theme: customTheme // Custom style
        },
        cssMaxWidth: 1000.// Canvas maximum width
        cssMaxHeight: 600 // Canvas maximum height
      })
      document.getElementsByClassName('tui-image-editor-main') [0].style.top = '45px' // Adjust the image display position
      document.getElementsByClassName(
        'tie-btn-reset tui-image-editor-item help') [0].style.display = 'none' // Hide the top reset button
    },
    // Save the image and upload it
    save() {
      const base64String = this.instance.toDataURL() / / base64 file
      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 files
      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>
Copy the code

conclusion

The above is the basic usage method of tui. image-Editor. Compared with other plug-ins, tui. image-Editor has the advantages of powerful function, simple and easy to use.

The plugin works, but I also found a small bug that when I zoom in on an image, drag the display position with my palm, and hit the reset button, the image will probably disappear. There are two solutions, one is to change the source code, before reset, call the resetZoom method, restore the zoom ratio column; The second option is to make a reset button, click it and call this.init to re-render.

Give a rose, the hand has lingering fragrance. If feel useful, move the small hand that gets rich, dot praise ~😜

For more apis and demos, please refer to:

Github address: github.com/nhn/tui.ima…

Nhn.github. IO /tui.image-e…

Other plug-ins

Each is my pro test, carefully selected, one in a hundred. Give you more time to touch 🐟

Vue 1 minutes to achieve the right menu, lazy Gospel

How does Vue quickly implement avatar cropping? It’s easier than you think

How to insert emojis, Word documents, and mathematical formulas into rich text?

1 minute to achieve drag, let you stroke more freely