Graphical editor development: the most basic but complex selection tool

Hello everyone, I am the front-end watermelon brother.

For a graphic design software, what is its most basic tool? Select a tool .

But this selection tool is quite complicated. This time I am here to talk to you about some of the twists and turns of selecting tools.

For the graphic design tool I'm developing:

https://github.com/F-star/suika

Online experience:

https://blog.fstars.wang/app/suika/

radio

The most basic is to select a single graphic.

The cursor stays on the graph, press the left mouse button, and the graph is selected. This is a simple scene with a single graphic selected.

Note that it must be mousedown, not click. I will say why later.

At the code layer, we will use the " graphic picking " algorithm to determine which graphic's click area the cursor falls on, and pay attention to considering the situation of hiding, locking, and grouping.

If you are interested in the details of graphics picking, you can read this article of mine:

" How to implement graphic picking on Canvas?" "

Hidden and locked graphics will be ignored. If you click on an element under the group, all elements in the entire group must be selected.

Clear the selected graphics collection (called selectSet temporarily), and then add this graphics into it.

selectSet.clear()
selectSet.add(targetEl)

The selected collection saves the selected graphics, which can save ids or graphics objects.

In the rendering layer, the outline of the selected graphics will be highlighted so that the user can perceive it.

In addition, there will be a rectangular selection box with control points on it, allowing the user to scale and rotate the graphic.

The check box is the bounding box of the graphic, usually an OBB bounding box with rotation .

If you click on a blank area, the selectSet should be cleared.

insert image description here

multiple choice

Sometimes we want to select multiple graphics.

The usual way is to hold down the Shift key and click on a shape.

At the same time, it is also necessary to support deselection : for a graphic that was originally selected, I hold down Shift and then

The core logic of the code is:

If this graphic is not in selectSet, add it; if this graphic is in selectSet, remove it .

if (event.shiftKey) {
    
    
  if (selectSet.has(targetEl)) {
    
    
    selectSet.delete(targetEl)
  } else {
    
    
    selectSet.add(targetEl)
  }
}

Multiple graphics have been selected. In addition to highlighting their outlines, we also need to wrap all the selected graphics with a larger rectangular selection box .

A small point: if it is the logic of canceling the selection, the selectSet needs to be updated after the mouse is released. Because it is necessary to prevent conflicts with the horizontal and vertical dragging that will be described later.

insert image description here

box selection

Box selection provides the ability to select a large number of graphics in a specific area at one time .

Press and drag the mouse in the blank area, and then release it to construct a rectangle, which we call " selection area ".

insert image description here

The selection rectangle will perform collision detection judgment with the graphics to determine which graphics are selected by the frame.

There are three options for collision detection:

  1. The selection rectangle and the bounding box of the selected graphics belong to the contain relationship ;
  2. The selection rectangle and the bounding box of the selected graphics belong to the intersect relationship ;
  3. Without using a bounding box, accurately determine whether there is a real pixel intersection ;

Personally, I recommend the intersection judgment scheme, and figma also chooses this scheme.

If you are interested in the details of collision detection, you can read my previous article:

" Graphics Editor - How does the rectangular selection realize the selection of multiple graphics? "

" Geometric Algorithms: Rectangular Collision and Containment Detection Algorithms "

Box selection can be combined with multiple selection . That is, you can hold down the Shift key, and then go to marquee selection.

Its effect is the same as holding Shift to select graphics one by one.

Core code implementation:

if (!event.shiftKey) {
    
    
  selectSet.clear();
}

for (const el of elementsInScence) {
    
    
  // 判断是否碰撞,这个方法
  if (isRectIntersect(selectionBox, el)) {
    
    
    // 普通框选
    if (!event.shiftKey) {
    
    
      selectSet.add(el);
    }
    // 连续和框选的组合
    else {
    
    
      if (selectSet.has(el)) {
    
    
        selectSet.delete(el);
      } else {
    
    
        selectSet.add(el);
      }
    }
  }
}

move

The selection tool is mainly used for selection. After selection, a very common operation is: to move the selected element .

So that's why it's also sometimes called a mobile tool .

Mobile interaction process:

  1. The cursor stays on the graph that has been selected, press and hold the mouse;
  2. Then drag the mouse, the selected graphic moves with the cursor;
  3. Release the mouse to indicate moving to the target position, and the movement ends.

insert image description here

Code core implementation:

  1. Record the position and starting position of the graph at this time before moving;
  2. Calculate the relative displacement when dragging, and update the position of the graphics;
  3. Reset state when released, and record to history.
// 图形移动前位置
let elStartCoords = [];
// 鼠标按下事件的光标位置,计算偏移量时作为基准
let startCoord = {
    
     x: undefined, y: undefined };

const onStart = (e) => {
    
    
  // 记录初始坐标
  elStartCoords = elements.map((el) => ({
    
     x: el.x, y: el.y }));
  startCoord.x = e.clientX;
  startCoord.y = e.clientY;
};

const onDrag = (e) => {
    
    
  // 计算偏移量,更新坐标
  const dx = e.clientX - startCoord.x;
  const dy = e.clientY - startCoord.y;
  elements.forEach((el, i) => {
    
    
    el.x = elStartCoords[i].x + dx;
    el.y = elStartCoords[i].y + dy;
  });
};

const onEnd = () => {
    
    
  // 重置状态
  elStartCoords = [];
  startCoord = {
    
     x: undefined, y: undefined };
};

Hold down the Shift key to move vertically and horizontally

Suppose we have made several aligned graphics. When we move one of the graphics, we hope to maintain the original alignment.

This is where it is useful to restrict movement to horizontal or vertical directions.

This ability is usually enabled by holding Shift while dragging .

insert image description here

Main points:

  1. In the middle of dragging and dropping, I never hold down Shift to press and hold it. To respond immediately, a keyboard event listener should be added in the code implementation instead of mouse movement events, because if you don’t move the mouse, the selected element will not be updated.
  2. Compare the size of dx and dy. If dx is large, it moves horizontally; if dy is large, it moves vertically. In this way, the graph can be as close as possible to the crosshair (horizontal line + vertical line)

snap to pixel grid

Align to the grid, when it is turned on, let the picture stick to the grid line as much as possible when the graphics are moving.

insert image description here

The method is to take the remainder of the coordinates of the upper left corner of the bounding box (AABB) of one or more graphics to obtain a position on the grid line, and use this position to update the selected graphics.

Scalability: Control Points

Selecting graphics is to operate on them.

The realization of these operations must be implemented through control points .

The common ones are:

  • Scale control points , on the 4 corners of the graphic selection box;

  • Rotate the control point , drag it to set the rotation of the graph, rotate the control point;

  • To set the gradient fill color for the graphic, you need to specify the color and position of the two colors, and the required gradient control points ;

The following is a zoom and rotate demonstration of figma, the editor I developed is not yet fully implemented.

insert image description here

In addition, different graphics drawing tools may have their unique operation methods , which require you to design according to the characteristics of graphics.

Take a look at Figma's special control point logic for different graphics.

insert image description here

Therefore, in the design of the selection tool module, it is necessary to provide the ability to register various types of graphic control point logic .

In the "picture picking", the control point should also be taken into consideration, whether the cursor is on the control point or not.

If the point is on the control point, the dragging logic will follow the logic of the control point instead of the basic logic of the selection tool.

other

There are also some enhancements to consider:

  • Double-click to enter the editing mode, and perform some more complex operations, such as turning any point into a Bezier curve.
  • When moving, use lines to display the distance from other graphic points (such as the midpoint, the 4 points at the corners of the selection box), and snap to it when it is very close.

end

To sum up, selecting tools is the most basic function of a graphic design software.

Its function is to select graphics and operate on them to update the properties of the specified graphics .

The most basic operation is movement, followed by enhanced operations through control points.

The two basic capabilities for control point manipulation are rotation and scaling. Then we will implement different control point logic according to different types of graphics.

It is said to be a kind of tool, but its actual positioning is more of the underlying infrastructure.

I am the front-end watermelon brother, welcome to follow me and learn more graphics development knowledge.

Guess you like

Origin blog.csdn.net/fe_watermelon/article/details/131177244