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
- 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
- Listening for canvas events
fabric
Provides 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
- Generates a single frame
- Mass production
preview
Use CSS’s Transform to zoom in and out and drag the canvas
Zoom in on
- amplification
2. To reduce the
3. The reduction