As the front end, I often deal with the mobile terminal. For the mobile terminal, there are multiple contact points operating at the same time, and the problem of hand scaling, so I thought about it and learned the knowledge of gesture development on the mobile terminal to achieve double finger scaling.

preface

The PC page we developed can only be clicked with the mouse, while the mobile page has the problem of multi-touch. We often use gestures to operate the page on the mobile page and zoom in and out. Most mobile devices (phones/ipads) support multi-touch, and developers can implement different functions for different gestures, so today’s theme is multi-touch and two-finger zooming.

Common case scenarios

  • Multi-touch on ios and Android phones

Ios and Android support multi-touch and gesture manipulations, and the browser provides an event containing an array of Touches that contains all multi-touch objects and is triggered by multi-touch objects

  • Progressive enhancement idea

Gesture operation is convenient for the user and brings great convenience to the user, but gesture can not always be used, always operating gesture is not the progressive enhancement idea we want. Some browsers do not support multi-touch, so you need to select a page that is suitable for normal click-combined touch event interaction, and gestures only enhance this interaction.

  • Touch events

Since incremental enhancement is required, it is necessary to start with basic click and touch events. Click events will not be talked about any more. As for touch events, there was a previous article about touch and click events in mobile development.

Browser compatibility:

We typically add CSS3 animation handling to touch and gesture event handlers, which requires browser-compatible handling.

var TRANSITION = 'transition';
var TRANSITION_END = 'transitionend';
var TRANSFORM = 'transform';
var TRANSFORM_PROPERTY = 'transform';
var TRANSITION_PROPERTY = 'transition';

if (typeof document.body.style.webkitTransform! == undefined) {TRANSFORM = 'webkitTransform';
    TRANSITION = 'webkitTransition';
    TRANSITION_END = 'webkitTransitionEnd';
    TRANSFORM_PROPERTY = '-webkit-transform';
    TRANSITION_PROPERTY = '-webkit-transition';
}
Copy the code

Double refers to zoom

The need for two-finger zooming is still common in mobile gesture events. This article highlights that most mobile devices now support native two-finger zooming, but this can affect the layout of the page, and in many cases you will choose to implement zooming yourself for more control.

  • Zoom center

In native zoom animation, the zoom center is usually the center of the image, so you can pick up the mobile device and try it out. In real life, we usually want to zoom around the two touchpoint centers of the gesture screen, i.e. the zoom center is the touchpoint center.

  • Transform the origin

In CSS3 animations, for example, transform animations rotate, zoom, etc., there is a transform-Origin property:

The transform-origin property lets you modify the origin for transformations of an element. The transform-Origin property allows us to change the origin of an element’s transform animation.

When we use CSS 3 transformation animation to zoom in, due to the transformation of the animation transformation origin and zoom center (contact center) are independent of each other, in order not to affect the scaling of center relative to the position of the element, element transformation scale, should also move to zoom zoom center coordinates, which in addition to transform the zoom element, the element should also be an additional offset.

  • The displacement deviation

We want the scale to be centered around the center of the touch point, and the element to scale around, assuming the transform origin is the upper left corner of the element, transform-Origin: 0, 0; In this case, the element’s transformation and scaling displacement are all positive and only scale to the lower right direction, so the scaling center (actually the element) should be offset to the opposite direction (upper left direction), and the value is the scaling coordinate value * (1-scaling ratio).

As shown in the figure above, the original element box A takes the center point L(x, y) of the contact points M and N as the zooming center point. Zooming around, its position should be box C in the figure. Since the position of point L(x, y) is uncontrollable (user behavior), we set transform-origin: 0, 0; At this time, its position after scaling is like box B, but we still want to achieve the displacement effect of box C after scaling, so we need to shift the element upward to the left. Assuming the scaling ratio is K, the calculation process of the element displacement value is as follows:

1. The calculation of the displacement of element A is actually equivalent to the displacement of the scaling point, that is, the displacement after the calculation of the coordinate system scaling; 2. The coordinate of point L(x, y) is (x, y) in the coordinate system before scaling, then after scaling k times, its coordinate in the coordinate system after scaling is (kx, ky); 3. Calculate the displacement distance of two points: horizontal displacement: X — kx; Vertical displacement: y — KY; 4. Obtain the horizontal and vertical offset distances of the elements.

Implementation of scaling

  • Zooming and scrolling

Our self-implemented zoom event requires the TouchMove event, and the TouchMove event triggers the device’s scroll event, so it either prevents scrolling or prevents the screen from having a scroll bar.

  • Stop rolling
document.addEventListener('touchmove'.function(event){
    event.preventDefault();
})
Copy the code
  • Preventing scrollbars
html, body {
    height: 100%;
}
Copy the code
  • Computing zoom center

The zoom animation is centered around the touchpoint, which is the center of the two touchpoints when we use the two-finger gesture to scale:


function getOrigin(first, second) {
    return {
        x: (first.x + second.x) / 2.y: (first.y + second.y) / 2
    };
}
getOrigin({
    x: event.touches[0].pageX, 
    y: event.touches[0].pageY
}, {
    x: event.touches[1].pageX, 
    y: event.touches[1].pageY
});
Copy the code
  • Calculate the scale

How to determine the scaling ratio? The initial touch distance divided by the distance between the two fingers when scaling, that is, the scaling ratio:

function getDistance(start, stop) {
    return Math.sqrt(Math.pow((stop.x - start.x), 2) + Math.pow((stop.y - start.y), 2));
}

function getScale(start, stop) {
    return getDistance(start[0], start[1]) / getDistance(stop[0], stop[1]);
}
Copy the code
  • Handling touch events

To realize the zoom function, in addition to calculating the relevant zoom center and zoom ratio, we also need to handle touch events:

var distance = {};
var origin;
var scale = 1;
function handleTouch(e) {
    switch(e.type) {
        case 'touchstart':
            if (e.touches.length > 1) {
                distance.start = getDistance({
                    x: e.touches[0].screenX, 
                    y: e.touches[0].screenY  
                }, {
                    x: e.touches[1].screenX, 
                    y: e.touches[1].screenY
                });
            }
            break;
        case 'touchmove':
            if (e.touches.length === 2) {
                origin = getOrigin({
                    x: event.touches[0].pageX, 
                    y: event.touches[0].pageY
                }, {
                    x: event.touches[1].pageX, 
                    y: event.touches[1].pageY
                });
                distance.stop = getDistance({
                    x: e.touches[0].screenX, 
                    y: e.touches[0].screenY  
                }, {
                    x: e.touches[1].screenX, 
                    y: e.touches[1].screenY
                });
                scale = distance.stop / distance.start;
                setScaleAnimation(scale, true);
            }
            break;
        case 'touchend':
            scale = 1;
            setScaleAnimation(scale);
            break;
        case 'touchcancel':
            scale = 1;
            setScaleAnimation(scale);
            break;
        default:; }}Copy the code
  • Using transformation

The next step is to use CSS3 animation zoom image, the code is as follows:

function setScaleAnimation(scale, animation) {
    var transition_animation = ' ';
    var x, y;
    if (animation) {
        transition_animation = 'none';
    } else {
        transition_animation = TRANSFORM_PROPERTY + '0.3 s ease - out';
    }
    element.style[TRANSITION] = transition_animation;
    // Calculate the displacement bias
    x = origin.x + (-origin.x) * scale;// Scale the center offset
    y = origin.y + (-origin.y) * scale;

    // Scale and shift
    element.style[TRANSFORM] = 'matrix(' + scale + ', 0, 0, ' + scale + ', ' + x + ', ' + y +  ') ';
}
Copy the code