The SVG editor I developed was originally designed for PC only. On the PC browser, vector graphics can be drawn normally, but on the mobile terminal, clicking on the canvas has no effect. But later, I hope that it can be easily used on some mobile devices with large screens, so I made some compatible solutions, so I summarized and wrote this article to share the adaptation processing solutions of click events on PC and mobile terminals.
Basic architecture
Like Adobe Illustrator, this SVG editor comes with a number of tools. Click on the tool icon to switch to the corresponding mode.
The SVG editor currently supports the following tools:
- Selection tool
- Path editing tool
- The pencil tool
- Pen (path) tool
- Add anchor point tool
- Delete anchor point tool
- Anchor point tool
Each tool corresponds to an Action object. This action object has the following properties:
interface EventHandler { (e: object): void; } interface Action { name: string; // The name of the action, such as select, path, etcbindEvents: string[]; // An array of strings that record the names of the events to bind. Such as mousedown unbind:function// Unbind the event hook mousedown? : EventHandler // Event name: event response function. . }Copy the code
Here’s an example:
let aciton: Action = {
name: 'select'.bindEvents: ['mousedown'.'mousemove'.'mouseup'].unbindMousedown (e) {mousemove(e) {}, mouseup(e) {}, mouseup(e) {}Copy the code
We use a file called actionsManager.js to manage these actions and switch tools. Without going into the code, here’s what it does:
First, we import these actions into the module and then save them to an Actions object. A function named bindEvents is responsible for switching actions. To switch, we also need a curAction variable that points to the action currently in use.
This way, when we click switch, we can unbind one by one with the removeEventListener method by iterating through all the event response functions corresponding to the current action’s bindEvents array. We then iterate over the bindEvents data for the new action, using addEventListener to bind the event response function.
From here, the implementation of the editor tool switch is roughly finished. Let’s start by explaining how to adapt mobile click events
Mobile click event adaptation
Before we change the code, let’s take a look at the similarities and differences between mobile and PC click behavior. Mobile terminal has unique touch events, although mobile terminal can trigger mouse events, but a little different from PC.
Click behavior on PC
On the PC, the mouseDown event is triggered when the mouse is pressed down. The mouseover event is triggered when the mouse is moved, but it cannot be triggered when the cursor moves away from the Event. SrcElement. The mouseup event is triggered when the mouse is released.
Click behavior on mobile
When the finger is pressed, the TouchStart event is triggered. If the finger is not lifted to move, the TouchMove event will be triggered. Even if the finger is moved outside the range of event. SrcElement, the TouchMove event will still be triggered, and the srcElemnt will still be the original node, not the node corresponding to the current finger position. Like the TouchMove event, srcElement does not point to the node where the finger is when the finger is raised, regardless of where the finger is at the moment.
Triggering sequence of events on the mobile end
(1) Press and lift the finger of the mobile end (without large displacement), triggering the following events successively:
- touchstart
- touchend
- mousemove
- mousedown
- mouseup
- click
(2) The sequence of events triggered by pressing the finger of the mobile end and moving it substantially, then lifting it:
- touchstart
- touchmove
- touchend
Here we see that the triggering of the TouchMove event causes the Mouse event to fail to fire.
In fact, simply add an event.preventDefault() line to the TouchStart or Touchend event and the Mouse event won’t fire when touch occurs. Touchmove prevents mouse events from firing by default, but it doesn’t necessarily fire in touch behavior (large offset required).
touchEvent
The touchEvent object is the event object generated when the touch event is triggered. It has some differences from the Mouse event object. First, the current position of the finger is stored in an array called Touches. This array holds the Touch object. Touch objects have coordinate related properties like clentX, clientY, etc., but not offsetX/offsetY.
Code transformation
Now let’s transform the original code.
OffsetX /offsetY calculation on mobile terminal
The Touch event does not provide offsetX and offsetY properties. However, these two attributes are essential for an SVG. We need these two properties to position the cursor on the canvas, to draw patterns such as paths.
To do this we need to write a method that converts pageX/pageY (the position of the cursor from the top left corner of the page) to offsetX/offsetY as a complementary property of e. Their relationship is:
offsetX + left = pageX;
offsetY + top = pageY;
Copy the code
Left (top) represents the distance from the top left corner of the page to the element to which the Touch event is bound.
const handleEventObj = function(e) { const LEFT = 256; // Need to calculate according to the actual situation. const TOP = 60; const touch = e.changedTouches[0]; const offsetX = touch.pageX - LEFT + workarea.scrollLeft; const offsetY = touch.pageY - TOP + workarea.scrollTop; Object.assign(e, {offsetX, offsetY});return e;
}
Copy the code
Modify tool switching function
Scheme 1: Determine the PC terminal or mobile terminal and decide whether to bind touch events or mouse events.
I started with this solution, but there was a problem with it. That’s fine for someone using a regular computer, but it can be a problem with a touch-capable laptop like the Surface. This is because the touch event is triggered when you click on the touch screen, but the Mouse event is not necessarily triggered. Even when mouse events are triggered, they are triggered by raising the finger, in a mouseover -> mouseDown -> mouseup order, and the mouseover cannot be triggered while the finger is pressed and moved.
The problem with this is that it’s not up to the task of a touchscreen laptop.
Scheme 2: Bind both touch and Mouse events
The bindEvents for all actions are automatically iterated when actionsManager.js introduces them. If the mousedown/mouseover/mouseup event response function, just add the corresponding touchstart/touchmove/touchend events response function.
The specific code is as follows:
const map = {
mousedown: 'touchstart',
mousemove: 'touchmove',
mouseup: 'touchend'};if (['mousedown'.'mousemove'.'mouseup'].includes(eventName)) {
action.bindEvents.push(map[eventName])
action[map[eventName]] = function(e) { e.preventDefault(); // This is important because it prevents subsequent mouse events. handleEventObj(e); // Add the offsetX ofsetY attribute to the event object. action[eventName](e); }}Copy the code
Here eventName is the eventName.
For example, if we have a selectAction object that has a mouseDown event, we add a TouchStart method to the selectAction. The TouchStart method blocks the default event, thus preventing the Mouse event from firing, and then adds offsetX/offsetY to the event object, passing the event to the Action. mouseDown method.
This makes simple SVG editing possible on mobile devices such as the iPad with a stylus.
reference
Nuggets — touchstart and Click have to tell the story