While the previous article outlined the process of building the annotation tool using Vue + fabric.js, this article will fill in some of the details and fabric tramps
1. Mouse from right to left frame
Following on from the previous description, the process of using fabric to draw annotation boxes on canvas is mainly as follows:
- Listen for the mouse down on the canvas
mouse:down
Event and saves the coordinates when the mouse is pressed as the starting point of the annotation box (mouseFrom
); - Listen for the mouse movement of the canvas
mouse:move
Event, in the process of mouse movement, draw on the canvas with the starting point in the first step as the upper left corner, and the coordinates of mouse movement as the lower right corner (mouseTo
Rect of); - Listen for the mouse lift of the canvas
mouse:up
Event, the annotation box is drawn when the mouse is lifted;
Thus, the generation code of the annotation box in the second step is
rect = new fabric.Rect({
left: mouseFrom.x,
top: mouseFrom.y,
width: mouseTo.x - mouseFrom.x,
height: mouseTo.y - mouseFrom.y
})
Copy the code
However, there is a hidden bug in this setting. When the mouse moves from left to right, the annotation frame works normally, but when the mouse moves from right to left, it is found that the annotation frame cannot move with the mouse as expected, but keeps moving to the rightFor the above scenario, one solution is as follows
X and mouseto. x, mousefrom. y and mouseto. y, using the smaller value as the coordinate of the top left corner of the annotation box (left and top). X-mousefrom. x is the absolute value of mouseto.x-mousefrom. x is the width of the annotation box, and mouseto.y-mousefrom. y is the absolute value of the annotation box.
let x = Math.min(mouseFrom.x, mouseTo.x)
let y = Math.min(mouseFrom.y, mouseTo.y)
let width = Math.abs(mouseTo.x-mouseFrom.x)
let height = Math.abs(mouseTo.y-mouseFrom.y)
rect = new fabric.Rect({
left: x,
top: y,
width: width,
height: height
})
Copy the code
In this way, the upper left point of the annotation box is the relatively small value. Although recT is still drawn from left to right, it is visually drawn from right to left as the mouse moves
2. The annotation box overflows the canvas
- Annotation boxes overflow the canvas during drawing
Then move the annotation box with the mouse as mentioned in the previous step. When the mouse is inside the canvas, the annotation box is normally drawn. However, when the mouse is out of the canvas, the values of mouseFrom and mouseTo are still changing, but the annotation box that overflows the canvas cannot be displayed normally. You need to limit the mouseFrom and mouseTo values so that the start and end of the annotation box remain inside the canvas.
limitPoint(x,y){
if(x < 0) x = 0
if(y < 0) y = 0
This.fabricobj.getwidth () gets the width of the canvas for a canvas created using Fabric
if(x > this.fabricObj.getWidth()) x = this.fabricObj.getWidth()
// this.fabricobj.getheight () gets the height of the canvas
if(y > this.fabricObj.getHeight()) y = this.fabricObj.getHeight()
}
Copy the code
- Overflow of canvas while moving annotation box
canvas.on('object:moving'.(e) = > {
// 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
3. Box shift in the selected state caused by screen resolution
In the process of development, I encountered such a bug. At first, when I selected the labeling box on the external monitor, it was normal, but when I accidentally dragged it to my computer screen, a strange scene happened, the selected box did not correspond to the original labeling box, and then dragged it back to the external monitor, it was normal again
The eight control points of the checked box in the checked state are not properly attached to the checked box
See this problem, it is really a headache, clearly nothing moved, why there is such a bug? Comparing each field of the selected annotation box on the console screen of the external monitor and my own computer screen, I found that zoomX and zoomY are 1 on the external monitor and 1.25 on my own computer screen. I suspected that zoomX and zoomY caused the annotation box deviation, and then went to study the source code. Find the assignment logic for zoomX and zoomY when creating the annotation box RECT
fabric
Is through thedrawControls()
Function to draw the selected control point, where the part of the red box is found to be settransform
“, followed by suspicion of canvasgetRetinalScaling()
Affected thezoomX
andzoomY
findgetRetinalScaling()
The discovery is based on_isRetinaScaling()
Delta function to decide what to takefabric.devicePixelRatio
The default is still 1. I don’t understandfabric.devicePixelRatio
What is it? Just keep lookingfabric.devicePixelRatio
The definition of window.devicePixelRatio
To this, suddenly realized, check their own computer allocation rate Settings, sure enough is 125%, with the above printed out of recTzoomX
andzoomY
So let’s try to change the resolution to 100% and findzoomX
andzoomY
The value changes to 1, and the control points in the selected state are displayed as normal
After sorting out the cause of the bug, it naturally occurred to me that the key to solving the bug was not to letwindow.devicePixelRatio
Becomes the scaling factor of the control point, and the problem goes back to thatgetRetinalScaling()
If the_isRetinaScaling()
False, so whatever the screen resolution is,getRetinalScaling()
If the value is 1, the control points will be normal.And then keep looking_isRetinaScaling()
The value ofFabric canvas found to have oneenableRetinaScaling
Parameter. The default value is true
Single look at the document, really confused, but through the source code, a good understanding of the meaning of the parameters, sigh, the document or with the source code to watch better!
4. In the selected state, the adjustment box zooms in and out at equal proportions
After development, the product such a bug, adjust the bubbles to drag the up and down or so four angles only scaling, such as product is expected to free to zoom with the mouse, through the document, and didn’t find the corresponding Settings, it is only to find the source, the process of looking for here is not wordy, in short, by reading the source code from bottom to top, It is found that the canvas of fabric has a uniformScaling property that controls the uniformScaling of the annotation box, and the default value is true. After setting it to false, the bug is solved
5. Set the width of the annotation box according to different picture resolutions
Due to the large difference in image resolution, if the annotation box is set with the same width, the rendering effect will be greatly different. Therefore, the annotation box width is dynamically set according to the image resolution (Scale is the ratio of the width and height of the picture to the width and height of the canvas container in the canvas creation stage in the previous article).
<div id="canvax-box">
<canvas id="label-canvas" :width="width" :height="height">
</div>
</template>
Copy the code
<script>
export default{
methods: {fabricCanvas(){...// 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
...
Copy the code