The picture

Tinymce offers a wealth of photo uploading, management and modification functions. You only need to configure the plug-in and some parameters.

Image upload

Reference image plug-in, add images_upload_handler configuration item, image popup left navigation will have an upload option, after clicking, we can complete the commonly used image upload function

<template>
	<div class="default-tinymce">
		<textarea id="editor"></textarea>
	</div>
</template>
<script>
import Tinymce from 'tinymce'
import Upload from 'src/utils/upload'

export default {
  name: 'DefaultTinymce',
  mounted () {
    this.upload = new Upload()
    let self = this
    Tinymce.init({
      selector: '#editor'.plugins: 'image'.async images_upload_handler (blobInfo, success, fail) {
          const file = blobInfo.blob()
          try {
            const url = self.YB.businessURL(await self.upload.post(file))
            success(url)
          } catch (e) {
            fail(e.message || 'Upload failed, please try again'}}})}}</script>

Copy the code

Image management

Tinymce offers similar features to both personal and public galleries. It’s also easy to use, just configure it.

We don’t have this requirement for the moment, but we may need it later.

One is a pull-down selection, where image_list is configured. Note that its values can be arrays, functions, or interfaces.

tinymce.init({
  selector: "textarea".// change this value according to your HTML
  plugins: "image".image_list: [{title: 'Dog'.value: 'mydog.jpg'},
    {title: 'Cat'.value: 'mycat.gif'}}]); tinymce.init({selector: "textarea".// change this value according to your HTML
  plugins: "image".image_list: "/mylist.php"
});

tinymce.init({
  selector: "textarea".// change this value according to your HTML
  plugins: "image".image_list: function(success) {
    success([
      {title: 'Dog'.value: 'mydog.jpg'},
      {title: 'Cat'.value: 'mycat.gif'}]); }});Copy the code

The other is a button that triggers the file_picker_callback method, which has a callback function as an argument, and can be used to create a personal library.

tinymce.init({
  selector: 'textarea'.// change this value according to your HTML
  file_picker_callback: function(callback, value, meta) {
    // This is pseudocode
    // Open a popover
    openDialog()  
    // Display a list of images
    showImageList()
    // Select the image and confirm the selection
    const url = submitActiveImage()
    // Close the popover to return to the image
    callback(url)
  }
});
Copy the code

Photo editors

Image editing is divided into two parts,

One part is to edit image attributes, such as border, margin, width, etc., which is actually the configuration of the plug-in image.

Part of it is editing the image itself, such as cropping and color mixing. This part is based on the configuration of the image Tools plug-in

image

image_caption
  • The default valuefalse
  • Possible values[true/false]

Add a description below the picture. When enabled, there is an option to display the title in the popover panel.

config = {
  image_caption: true
}
Copy the code

This generates a new structure


<figure class="image" contenteditable="false" data-mce-selected="1">
    <img src="https://imgtest.120yibao.com/test/base/obu1oj9a5fhy4ftku49.jpeg" alt="sdf" width="650" height="639">
    <figcaption contenteditable="true">At the bottom of the picture</figcaption>
</figure>
Copy the code
image_class_list
  • Value typesstring

You can drop – down to add the corresponding class to the image


config = {
  image_class_list: [{title: 'no'.value: ' ' },
    { title: 'Adaptive screen width'.value: 'adaptive-screen-width'}}]Copy the code
image_advtab
  • The default valuefalse
  • Possible values[true/false]

Icon edit popover has a property edit TAB, including margin (margin), border (border) edit. After modification, the image’s style property value will be overwritten.

image_description
  • The default valuetrue
  • Possible values[true/false]

Corresponds to the Alt attribute of the IMG tag

image_dimensions
  • The default valuetrue
  • Possible values[true/false]

Whether the width and height of the image can be set through input in the popover

image Tools

This plugin edits the raw data of the image.

Automatically upload after editing

Operations that involve modifying the original data of an image (such as cropping, flipping, etc.) require re-uploading the image. It can be re-uploaded with the server interface, or the front-end can indirectly call the images_upload_handler method to upload.

Let’s look at front-end uploads


function handleImageUrlToBlob (url, callback) {
   function imageToCanvas (src, cb) {
     const canvas = document.createElement('canvas')
     const ctx = canvas.getContext('2d')
     const img = new Image()
     img.src = src + '? t=2'
     img.crossOrigin = ' '
     img.onload = function () {
       canvas.width = img.width
       canvas.height = img.height
       ctx.drawImage(img, 0.0)
       cb(canvas)
     }
   }

   function dataURLToBlob (dataURL) {
     const arr = dataURL.split(', ')
     const mime = arr[0].match(/ : (. *?) ; /) [1]
     const bStr = atob(arr[1])
     let n = bStr.length
     const u8arr = new Uint8Array(n)
     while (n--) {
       u8arr[n] = bStr.charCodeAt(n)
     }
     return new Blob([u8arr], { type: mime })
   }

   function canvasToDataURL (canvas, format, quality) {
     return canvas.toDataURL(format || 'image/jpeg', quality || 1.0)
   }

   imageToCanvas(url, function (canvas) {
     callback(dataURLToBlob(canvasToDataURL(canvas)))
   })
 }

tinymce.init({
  // These two items are required when using the ImageTools plugin and are only useful when using server uploads. But the front end uses some other method to get the image (imageTools_fetch_image), so it writes arbitrary values
  imagetools_cors_hosts: [],
  imagetools_proxy: 'just a string and do nothing'.// Get the image after modifying the image
  imagetools_fetch_image: image= > {
    return new tinymce.util.Promise(function (resolve) {
      // The image needs to be converted to BLOB format
      handleImageUrlToBlob(image.src, resolve)
    })
  },
})

Copy the code

After modifying the image, the editor automatically uploads the image if it loses focus and generates a new URL to replace the previous one.

Edit the feature list configuration

Imagetools_toolbar list of configurable editing features

tinymce.init({
  selector: "textarea".toolbar: "image".plugins: "image imagetools".imagetools_toolbar: "rotateleft rotateright | flipv fliph | editimage imageoptions"
});
Copy the code

formatting

Sometimes you need to do some formatting, such as changing font sizes, converting content to titles, or simply clearing all styles of selected content. There is no need to import plug-ins, just configure them.

tinymce.init({
  selector: "textarea"./** * "block" style formatting */
  block_formats: 'paragraph = p; 1 = h1 title; Title = 2 h2; Title = 3 h3; Title = 4 h4; Title = the h5; Title = h6; './** * The default value of the toolbar paragraph drop-down component */
  style_formats: [
      { title: 'Headings'.items: [{title: 'Heading 1'.format: 'h1' },
          { title: 'Heading 2'.format: 'h2' },
          { title: 'Heading 3'.format: 'h3' },
          { title: 'Heading 4'.format: 'h4' },
          { title: 'Heading 5'.format: 'h5' },
          { title: 'Heading 6'.format: 'h6'}]}, {title: 'Inline'.items: [{title: 'Bold'.format: 'bold' },
          { title: 'Italic'.format: 'italic' },
          { title: 'Underline'.format: 'underline' },
          { title: 'Strikethrough'.format: 'strikethrough' },
          { title: 'Superscript'.format: 'superscript' },
          { title: 'Subscript'.format: 'subscript' },
          { title: 'Code'.format: 'code'}]}, {title: 'Blocks'.items: [{title: 'Paragraph'.format: 'p' },
          { title: 'Blockquote'.format: 'blockquote' },
          { title: 'Div'.format: 'div' },
          { title: 'Pre'.format: 'pre'}]}, {title: 'Align'.items: [{title: 'Left'.format: 'alignleft' },
          { title: 'Center'.format: 'aligncenter' },
          { title: 'Right'.format: 'alignright' },
          { title: 'Justify'.format: 'alignjustify'}}]]./** * List of font size options */
  fontsize_formats: '12px 14px 16px 18px 24px 36px 48px'./** * format */
  formats: {
    // Clear the format
    removeformat: [
      {
        selector: 'b,strong,em,i,font,u,strike,sub,sup,dfn,code,samp,kbd,var,cite,mark,q,del,ins'.remove: 'all'.split: true.block_expand: true.expand: false.deep: true
      },
      {
        selector: 'span'.attributes: ['style'.'class'].remove: 'empty'.split: true.expand: false.deep: true },
      {
        selector: The '*'.attributes: ['style'.'class'].split: false.expand: false.deep: true}}}]);Copy the code

Convert the outer chain picture

If there is a picture in the pasted content, and the picture is an external chain picture. You may need to convert these images to your own server address, which requires the backend to coordinate: give the backend the URL of these images, the backend returns your own URL, and the backend uploads the image.

If the image in the pasted content is base64, the images_upload_handler image upload hook configured previously is triggered to automatically upload the image.

The implementation of this piece is more complex, I will not post the code, just say the general idea:

  1. inpaste_postprocessMethod to label the outer chain images
  2. Then listen for the editor’s content insertion event (mceInsertContent), in the callback method of the event, the tagged outer chain picture is transformed.

It should be noted that, because some image providers will protect the image, the server may not be able to transmit every image you send to its own server, need to agree with the back-end processing method.

Encapsulate as Vue components

First, we implement bidirectional binding for Vue: Update the ViewModel as the editor content changes; Update the contents of the editor as the ViewModel changes.

The implementation is simple, we can listen for things that can change the contents of the editor, and use vm.$watch to listen for changes in values.

<template>
	<div class="editor-wrap">
		<textarea v-model="value" class="editor-textarea"></textarea>
	</div>
</template>
<script>
export default {
  name: 'TinymceEditor'.model: {
    prop: 'value'.event: 'change'
  },
  props: {
    value: {
      type: String.required: true.default: ' '}},async mounted () {
    try {
      const self = this
      const editor = await Tinymce.init({
          selector: '.editor-textarea'.// The editor instance initializes the completed callback
          init_instance_callback: editor= > {
              // Two-way data binding
              self.$nextTick((a)= > {
                  let currentContent = ' '
                  // Bind data bidirectionally
                  self.$watch('value', (val, prevVal) => {
                    if (editor && typeof val === 'string'&& val ! == currentContent && val ! == prevVal) { editor.setContent(val) currentContent = val } }) editor.on('change keyup undo redo', () => {
                    currentContent = editor.getContent()
                    self.$emit('change', currentContent)
                  })
              })
          }
        })
        this.editor = editor[0]}catch (e) {
      this.$error(e)
    }
  }
}
</script>

Copy the code

Tinymce – The first rich text Editor in the universe? [3]

reference

  1. Tinymce official documentation