In recent projects, a text editor needs to be implemented through Canvas. In most scenarios, it is not necessary to implement an editor through Canvas. Only those who need to use the drawing function of Canvas to achieve the text effect that div/ CSS cannot simulate, you need to use Canvas to achieve text editing and rendering. In addition, using Canvas for text editing is not optimal or even recommended because of frequent canvas redrawing. This article describes how to implement a simple text editor using Canvas.

  • Requirement scenario for canvas text editor
  • How to implement a Canvas simple text editor
  • Editor function optimization

Source code reference address: address: github.com/fortheallli…

Originally posted on my Github: github.com/fortheallli…


I. Demand scenario of Canvas text editor

First, again, the first point: in most scenarios, you probably don’t need a Canvas to implement a text editor. There are only two conditions in which you need to use Canvas for text editing:

  • Canvas patterns, including text, special effects such as overall animation and transitions, scenes that need real-time editing (while editing and rendering)
  • For some special text effects that CANNOT be simulated by CSS, canvas is needed to supplement text rendering effects

As an example of using canvas text editor, fabric.js is a tool to simplify canvas drawing, provides powerful vector drawing function, and can easily draw different patterns in the local area of canvas, which is called model. Different models can interact with each other and so on.

In fabric.js, it is often necessary to animate a local model. If the model is a text, we will call the text model for short, and simulate it with div, which is called div text editing. So you have to do two mappings.

Text Model Render result — > DIV Text Edit (simulated render result) — > Edit — > Text Modal render result (reverse reproduce)

If these two mappings are complicated, there will obviously be a certain amount of transformation work, which is not fatal. However, the text editing method of this transformation cannot be edited in real time, and can only be rendered in canvas after the editing is completed.

In addition, we know that CSS can fully simulate most text rendering effects, but there are some text rendering effects that CSS cannot simulate, such as:




Text 1



Text 2

In this scenario, canvas must be used to realize text editor for complex rendered text in order to realize text editing and preview while editing.

Let’s take a brief look at the effect of fabric.js Chinese text editing:




Text editing effects

You can visit the FabricJS official website to see this example of text editing. Press F12 to see that this text editor is not implemented through DOM simulation, but through Canvas.

How to implement a simple text editor

(1) How to simulate the cursor

First of all, text editing is realized through Canvas, mainly using Canvas fillText to draw text. In the context of text editing, the first thing to deal with is the problem of the cursor. The method in this paper does not simulate the blinking effect of the cursor. In this paper, the simple text editor, can realize the function of the cursor by “|”.

Such as:

I am a little bird |Copy the code

Is a js string STR = “I am a little bird |”, we use a vertical bar “|” to simulate the cursor

This simple set, as long as we change the position of the |, redrawn can achieve the similar cursor movement in a text editor.

If there is only one line of text, here we can save the cursor position is one-dimensional, but the text editor of our scene is multi-line text, so we need to save the cursor position is also two-dimensional, to determine which line and column the cursor is in.

             this.focusIndex = [x,y]
Copy the code

Saved after the location of the cursor, we can call fillText method drawing a line of text, if changing the cursor, we in the profession string to insert “|”, finally draw a result, was completely simulate the realization of the cursor in the text editor.

(2) How to deal with the mouse click to switch the cursor of the text editor

To realize the function of clicking the mouse to switch the cursor of the text editor, we need to measure the position of each text on the screen in multi-line text. The key to calculate the position is how to calculate the width and height of the text drawn on canvas.

  • Text width on Canvas: Text width can be measured using measureText on canvas

  • Height of text in Canvas: There is no method to measure the height of text in canvas, but the height of text in Canva is realized in the same way as that rendered in DIV/CSS. We can render text of the same font in div to measure its height, which is consistent with the height of text rendered in canvas.

The following is a method to compare the height of text in Canvas by measuring the height of text in DIV:

var FontMetrics = function(family, size) {
      this._family = family || (family = "Monaco, 'Courier New', Courier, monospace");
      this._size = parseInt(size) || (size = 12);
    
      // Preparing container
      var line = document.createElement('div'),
          body = document.body;
      line.style.position = 'absolute';
      line.style.whiteSpace = 'nowrap';
      line.style.font = size + 'px ' + family;
      body.appendChild(line);
    
      // Now we can measure width and height of the letter
      line.innerHTML = 'm'; // It doesn't matter what text goes here
      this._width = line.offsetWidth;
      this._height = line.offsetHeight;
    
      // Now creating 1px sized item that will be aligned to baseline
      // to calculate baseline shift
      var span = document.createElement('span');
      span.style.display = 'inline-block';
      span.style.overflow = 'hidden';
      span.style.width = '1px';
      span.style.height = '1px';
      line.appendChild(span);
    
      // Baseline is important for positioning text on canvas
      this._baseline = span.offsetTop + span.offsetHeight;
    
      document.body.removeChild(line);
    };
    
    FontMetrics.prototype.getSize = function() {
      return this._size;
    };
Copy the code

From this we know how to calculate the width and height of each text, and thus the position of each text.

(3) Coordinate conversion

Another important point in drawing text on Canvas is coordinate transformation. How to convert CSS coordinate transformation and canvas drawing coordinate transformation needs to understand the difference between Canvas drawing coordinate and Canvas CSS coordinate. The transformation formula is as follows

let ratio  = canvas.width / cancas.style.width
let updateClientX  = ratio * clientX
Copy the code

(4) Handle enter, space, up, down, left and right keys

In addition to switching the cursor position by clicking the mouse, you can also update the cursor position by pressing up, down, or left:

If (this. IsFocus && e.k ey = = = 'ArrowUp') {/ / boundary judgment if (this. FocusIndex [0] > 0) {}}}Copy the code

The cursor position can be moved according to the location key, especially the need to deal with boundary conditions, such as moving to the last column of a row, and then moving to a new line.

In addition to this, there is also the return line feed Enter and delete BackSpace key processing here is not one example.

(5) Processing of text typing

To solve this problem, we need to introduce a textArea node and change the position of the textArea node to be consistent with the position of the text cursor. We need to set zIndex to overlay the Canvas textArea:

This.textarealocation = () => {// Find out where the cursor is and make it absolutely located canvas.style.zIndex = 100; canvas.style.position = 'absolute' that.TextArea.style.position = 'absolute'; that.TextArea.style.zIndex = -1000; that.TextArea.style.opacity = 0; let y = this.focusIndex[0] let x = this.focusIndex[1] let cur = this.localArr[y][x] that.TextArea.style.left = cur.x + 'px' that.TextArea.style.top = cur.y.start + 'px'; }Copy the code

When clicking on the Canvas text edit area:

  textArea.focus()
Copy the code

When I click outside of the text area,

  textArea.blur()
Copy the code

When typing text, listen for the textArea input event to get the textArea input value and render it in the canvas. If you need a text editor to input Chinese, you need to add comPOSItionStart and COMPOSItionEnd events that listen to the textArea on the basis of the textArea input event.

The judgment logic here is:

If the compositionStart event is triggered, it indicates a Chinese input. In the CompositionEnd event, you can get the complete value entered after completing the Chinese input method. Otherwise, it is an English input.

The complete code is as follows:

this.TextArea.addEventListener('compositionstart',function(e){ that.inputStatus = 'CHINESE_TYPING'; },false); this.TextArea.addEventListener('input',function(e){ if (that.inputStatus === 'CHINESE_TYPING') { return; } // process English input},false); This. The TextArea. AddEventListener (' compositionend ', function (e) {if (that) inputStatus = = = 'CHINESE_TYPING') {/ / e.d ata processing Chinese input . }},false);Copy the code

At this point, we have basically a finished, simple text editor. The specific effects are as follows:

Simple text editor source address: github.com/fortheallli…