canvas Drag Graphic Effect Implementation

Foreword: Recently, I have some interest in canvas. I was naturally curious about this kind of visual thing, so I invested some time to play. I realized a canvas-based graphics dragging effect by imitating a case on the Internet. I mainly learned their ideas, the code is partly self-implemented, so I walked some pits of canvas.

Canvas Graphics Drag Effect Display

The main functions implemented are:

  1. When the mouse clicks, a red checkbox appears, and when the mouse releases the checkbox, it disappears.
  2. When the mouse moves, the picture moves with it.
  3. Pictures can only move within a limited amount of space and cannot move out of boundaries.

Implementation logic

Mouse Drag Logic

A little personal understanding: mouse events or other browser events are independent, but in development users are usually a combination of multiple events.
It is necessary to combine events to filter out some invalid operations by using the appropriate variables.

For example, the user executes on a page
Drawing operation: This process must include: mouse press, mouse move, mouse release, these three complete things, and they are all in order.
Any other combination of these three operations is considered invalid and should not be responded to, filtered in the program's execution logic.
Calls made during program execution are as follows: the mouse is pressed to execute once, the mouse is moved to execute once, and the mouse is released to execute once.
The mouse release event does not trigger when the element that added the mouse event is moved out during the mouse press and move process and then released.
That is to say, the previous restriction failed, so you need to add an additional mouse-out event with the same logic as the mouse-release event.

Picture Moving Effect Logic

The effect of picture moving is actually very simple. When the mouse moves, it empties the last graphic and redraws the graphic in a new location. Through this continuous emptying and drawing process, the effect that the picture can be moved on canvas is formed. The principle of graphic transformation is very simple, that is, the knowledge of junior middle school mathematics.
See the following illustration for details:
The upper left is the initial position of the graphic, the lower right is the moving position of the graphic; the coordinates (x1, y1) are the initial left vertex, the coordinates (x1', y1') are the moved left vertex (we don't know its value); the coordinates (x, y) are the location of the initial mouse click, and the coordinates (now.x, now.y) are the location of the mouse click after the move.

Considering that the mouse moves from (x, y) to (now.x, now.y), its offset in the X direction is: now.x-x, and similarly its offset on the Y position is: now.y-y. Therefore, the position of the left vertex (x1', y1') after the mouse moves can be derived from this, which is:
x1' = x1 + (now.x-x)
y1' = y1 + (now.y-y)

Note: My code contains x-now.x, which is not an error, because I used now.x to record the location of the last point, x is the current point, where the logic is unchanged.

Red checkbox

Here, the implementation of the red checkbox is also simple. When the mouse is selected, a red checkbox is drawn; when the mouse releases or leaves the canvas element, the graphic is redrawn, and a white checkbox is drawn. (The background of the whole canvas is white, so it feels like the checkbox has disappeared.)

Graphic Move Boundary Limit

Because the graphics are ugly to move outside the boundary and can be a bit cumbersome to move back, there is a special restriction here that restricts vertices in the four directions from moving out of the canvas boundary. However, it was later discovered that all you need to do is to restrict the upper left vertex and the lower right vertex to a total of four points.However, I have not simplified the code, I hope you can see my thinking process. Specific logic, just refer to my code, here is just a simple statement.

Full implementation code (not optimized)

This is the original code, because after writing, I found many places are actually unnecessary, some judgment logic is redundant, but I will not delete it, the code is redundant, it is also easy for you to understand my implementation process.

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title>mouse event</title>
		<style>
			body, div {
				margin: 0;
                padding: 0;
			}
		</style>
	</head>
	<body>
		<div id="cas">
			<canvas id="cs" width="800" height="600" style="border: 2px solid green"></canvas>
		</div>
		
		<script type="text/javascript">
			let canvas = document.getElementById("cs");
			// Get width and height of canvas
			let width = canvas.width;
			let height = canvas.height;
			
			let ctx = canvas.getContext("2d");
			let now = {x: 0, y: 0};  // Mouse position
			let pos = {x1: 0, y1: 0, x2: 0, y2: 0, x3: 0, y3: 0, x4: 0, y4: 0};  // The location of the starting point of the picture, where the default (x1, y1) is (0, 0)
			// Mouse press, mouse move and mouse release are two separate actions. Now you want to take the intersection of the two actions in order.
			// That is, when the mouse is pressed, moved and released, it is considered an action.
			
			let isDown = false;  
			
			// Create img element
			let img = document.createElement("img")
			// Set its src so the picture will be loaded
			img.src = "./test.png";
			img.style.position = "absolute"
			// When the picture is loaded, draw it on canvas
			// Without this, picture drawing may not occur because the picture is empty when it is not loaded.
			img.onload = () => {
				drawCanvas(img, "#FFFFFF");
				// Starting point for updating pictures
				updatePos(pos.x1, pos.y1);
			};
			
			// Add mouse things to achieve drag-and-drop of pictures.
			// Get the current coordinates when the mouse is pressed
			canvas.onmousedown = e => {
				isDown = true;     // When the mouse is pressed, isDown is set to true, and moving the mouse is considered valid.
				console.log("mousedown")
				let x = e.pageX-canvas.offsetLeft;     // The latter one is the offset, but here it is 0
				let y = e.pageY-canvas.offsetTop;
				now.x = x;
				now.y = y;
				console.log(x + " -> " + y);
				
				if (!ctx.isPointInPath(x, y)) {
					console.log("Mouse not in path");
					return;
				}

				drawCanvas(img, "red");
			};
			
			// Continuously redraw the entire canvas as the mouse moves
			canvas.onmousemove = e => {
				if (!isDown) {  // If the mouse is not pressed, it returns directly without responding to the event.
					return;
				}

				// Getting the coordinates of a point can be encapsulated as a function
				let x = e.pageX;
				let y = e.pageY;
				
				console.log(x + " " + y);
				if (!ctx.isPointInPath(x, y)) {
					console.log("Mouse not in path");
					return;
				}
				// There's a problem with restricting the mouse from crossing the border. Pictures that move outside the interface don't work well.
				
				// Clear the current canvas graphic
				ctx.clearRect(0, 0, canvas.width, canvas.height);
				
				// Calculate the changing position of the drawing starting point of the picture
				pos.x1 = pos.x1 + (x-now.x);
				pos.y1 = pos.y1 + (y-now.y);
				// Update the location of other points
				updatePos(pos.x1, pos.y1);
				// Judges the position of four points, cannot cross the boundary
				judgePosition();
				
				// canvas after redrawing offset
				ctx.drawImage(img, pos.x1, pos.y1);
				
				ctx.beginPath();
				ctx.strokeStyle="red";
				ctx.rect(pos.x1, pos.y1, img.width, img.height);  
				ctx.stroke();
				
				now.x = x; now.y = y;
				console.log("Mouse is moving..." + x + " --> " + y);
			}

			canvas.onmouseup = e => {
				isDown = false;  // When the mouse is released, the above encapsulated action ends.
				console.log("MouseUp");
				drawCanvas(img, "#FFFFFF");
			}
			
			// If the mouse is pressed and moved away from the current element and then released, but the mouse release event cannot be triggered.
			// So when you listen for the mouse to move out of an element, you must also set isDown to false.
			canvas.onmouseout = e => {
				isDown = false;
				console.log("The mouse left the canvas element");
				drawCanvas(img, "#FFFFFF");
				// Judges the position of four points, cannot cross the boundary
				judgePosition();
				// canvas after redrawing offset
				ctx.drawImage(img, pos.x1, pos.y1);
			}
			
			function drawCanvas(img, color) {
				ctx.clearRect(0, 0, canvas.width, canvas.height);   // Clear the current canvas graphic
			    ctx.drawImage(img, pos.x1, pos.y1);   // Global variables should not be used to pass parameters here.
				ctx.beginPath();
				ctx.strokeStyle=color;
				ctx.rect(pos.x1, pos.y1, img.width, img.height);
				ctx.stroke();
			}
			
			// Because the picture is rectangular, you can determine the remaining four points by knowing only one point.
			// Here we take (x1,y1), the point on the upper left corner, which is more convenient.
			// Because x1 and y1 are changing, you need to update x1 and y1 first.
			function updatePos(x, y) {
			console.log("Incoming parameter values:", x, y);
			    pos.x1 = x;
				pos.y1 = y;
				pos.x2 = x + img.width; 
				pos.y2 = y;
				pos.x3 = x + img.width;
				pos.y3 = y + img.height;
				pos.x4 = x;
				pos.y4 = y + img.height;
			}
			
			// Determine the location and process when the point crosses the boundary
			function judgePosition() {
				// First look at the rules for points
				console.log("judgePosition:", pos);
				
				// There are three cases for one-sided and two-sided boundaries
				// As long as the upper left corner and the lower right corner are not out of bounds, all cases will not be out of bounds.
				if (pos.x1 < 0 && pos.y1 > 0) {
					updatePos(0, pos.y1);
				} else if (pos.x1 > 0 && pos.y1 < 0) {
					updatePos(pos.x1, 0);
				} else if (pos.x1 < 0 && pos.y1 < 0) {
					updatePos(0, 0)
				} else if (pos.x3 > width && pos.y3 < height) {
					updatePos(width-img.width, pos.y3-img.height);
				} else if (pos.x3 < width && pos.y3 > height) {
					updatePos(pos.x3-img.width, height-img.height);
				} else if (pos.x3 > width && pos.y3 > height) {
					updatePos(width-img.width, height-img.height);
				}
			}
		</script>
	</body>
</html>

Points of Attention

The use of the isPointInPath(x, y) function is conditionally limited. Some functions draw graphs that do not support this function, so if used incorrectly, it can be confusing and not what you expect. Especially for me, it has been a long time.

Keywords: Javascript Front-end html5

Added by dookie on Mon, 20 Sep 2021 10:09:54 +0300