Requirements gathering

The intent of the components, and to do this is to mark recognition based on AI group, send a picture and some pictures on the coordinates and returns the corresponding recognition as a result, the front have to do is based on a picture, and draw out the corresponding mark on the image frame, and the bubbles in the corresponding coordinates and wide high transmitted to the backend, which can identify this is the most basic needs. When drawing on pictures, the first thing that comes to mind is canvas. The powerful function of Cancas allows us to do whatever we want on pictures. The native CanvasAPI is numerous and complicated, and it is not easy to get started. Provides an object-oriented approach to writing Canva, provides an interactive object model on top of the native Canvas, and allows rich manipulation on the canvas through concise APIS. Fabric was chosen as the base framework.

Fabric. Js is introduced

Fabric is API encapsulation based on Canvas. It can draw some basic graphics such as rectangle, circle, ellipse and text, and also supports brush customization. The advantage of Fabric is that it can well encapsulate the generated Canvas canvas, including adjusting the canvas and objects on the canvas. Listening for various events on the canvas and object makes the canvas interaction logic easy to use. Fabric’s official website lists various parameters and APIS of fabric in detail. Because fabric.js is a foreign framework, the document is in English, and there are few related examples, so it is recommended to use the source code

function

Build the canvas

  1. Generate the base canvas from the image

First the component receives the image link from the outside

props:{
    imgData: String  // Image link
}
Copy the code

Watch listens for imageData changes and generates the canvas

watch:{
    imageData(val){
        if(val){
            this.fabricCanvas() // Generate canvas}}}Copy the code

The fabricCanvas event primarily initializes the fabric and sets the picture to the background picture of the canvas for later addition of annotation boxes to the canvas

<template>
    <div id="canvax-box">
        <canvas id="label-canvas" :width="width" :height="height">
    </div>
</template>
Copy the code
<script>
 export default{
     methods: {fabricCanvas(){
             if(this.fabricObj){ // If the canvas already exists, empty it and redraw
                 this.fabricObj.clear()
             } else {
                 this.fabricObj = new fabric.Canvas('lavel-canvas', {// Set the canvas's initial properties here
                 uniformScaling: false.// Scale equally
                 enableRetinaScaling: false.selection: false // Disable group selection}}let Shape
             const image = new Image()
             image.src = this.imageData
             image.setAttribute('crossOrigin'.'anonymous') // Allow cross-domain access
             image.onload = () = > {
                 // Set the canvas width and height to the original width and height of the image
                 this.width = image.width 
                 this.height = image.height
                 this.fabricObj.setWidth(this.width)
                 this.fabricObj.setHeight(this.height)
                 // Place the image in an external container
                 let boxWidth = document.getElementById('canvas-box').offsetWidth
                 let boxHeight = document.getElementById('canvas-box').offsetHeight
                 let scaleX = boxWidth / image.width
                 let scaleY = boxHeight / image.height
                 // Determine the scaling factor
                 this.scale = scaleX > scaleY ? scaleX : scaleY
                 document.querySelector('.canvas-container').style.width = this.width * this.scale + 'px'
                 document.querySelector('.canvas-container').style.height = this.height * this.scale + 'px'
                 document.querySelector('#label-canvas').style.width = this.width * this.scale + 'px'
                 document.querySelector('#label-canvas').style.height = this.height * this.scale + 'px'
                 document.querySelector('.upper-canvas').style.width = this.width * this.scale + 'px'
                 document.querySelector('.upper-canvas').style.height = this.height * this.scale + 'px'
                 
                 Shape = new fabric.Image(image)
                 this.fabric.setBackgroundImage(Shape,
                     this.fabricObj.renderAll.bind(this.fabricObj),
                     {
                         opaity: 1.angle: 0})this.$nextTick(() = >{
                     this.fabricObj.renderAll() // Re-render the canvas
                 })
             }
         }
     }
 }
</script>
Copy the code
  1. Listening for canvas eventsfabricProvides a series of events to help us do a good job of manipulating the canvas

The following events are used this time

watch:{
 imageData(val){
     if(val){
         this.fabricCanvas() // Generate canvas
         this.fabricObjEvent() // Listen for the canvas event}}}Copy the code

The canvas operation

With the frame

Annotation frame is mainly used in the above mouse:down: brush down; Mouse: move frame; Mouse: Up The brush lift event

Adjust the frame

Before adjusting the frame, first set the canvas to selectableIf you want to change the default selected style of the frame, you can modify the corresponding parameters of the frame

Adjusting the frame is mainly used aboveobject:moving: Object movement;object:modified: Object adjustment;

handleObjectMoving(){

// Prevents objects from moving outside the canvas
      let padding = 0; // The width of the space between the content and the canvas
      var obj = e.target;
      if (obj.currentHeight > obj.canvas.height - padding * 2 ||
        obj.currentWidth > obj.canvas.width - padding * 2) {
        return;
      }
      obj.setCoords();
      if (obj.getBoundingRect().top < padding || obj.getBoundingRect().left < padding) {
        obj.top = Math.max(obj.top, obj.top - obj.getBoundingRect().top + padding);
        obj.left = Math.max(obj.left, obj.left - obj.getBoundingRect().left + padding);
      }
      if (obj.getBoundingRect().top + obj.getBoundingRect().height > obj.canvas.height - padding || obj.getBoundingRect().left + obj.getBoundingRect().width > obj.canvas.width - padding) {
        obj.top = Math.min(
          obj.top,
          obj.canvas.height - obj.getBoundingRect().height + obj.top - obj.getBoundingRect().top - padding
        );
        obj.left = Math.min( obj.left, obj.canvas.width - obj.getBoundingRect().width + obj.left - obj.getBoundingRect().left - padding ); }}Copy the code
handleObjectModified(e){
this.$emit('objectModified',e.target)
}
Copy the code

The selected frame

When the frame is selected, a selection event is thrown

On ('selected',(e)=>{this.$emit('objectSelected', e.target)})Copy the code

Remove the frame

Call the Remove event of the Fabric

this.fabricObj.remove(item)
Copy the code

Clear all frames

clearAllMark(){
    const objects = this.fabricObj.getObjects()
    for(let i in objects){
        this.fabricObj.remove(i)
    }
    this.$emit('clearAllMark')}Copy the code

Generate frames according to coordinates

  1. Generates a single frame

  1. Mass production

preview

Use CSS’s Transform to zoom in and out and drag the canvas

Zoom in on

  1. amplification

2. To reduce the

3. The reduction

Drag and drop