Introduction to the

In front-end development, sometimes there will be the need to scale the picture, Canvas and DOM elements by rolling the wheel according to the current position of the mouse. Some students may find it a little difficult, but in fact, with the help of matrix operations in linear algebra, it can be very easy to achieve this function. More importantly, as a discipline, mathematics has universality, independent of specific programming language and environment, and universality can be achieved by mastering the principle well.

Nature of scaling

The essence of scaling is matrix transformation.

When we want to scale a Div element, we can generally think of it as scaling a rectangle. To make this easier to understand, let’s use the simplest rectangle scaling as an example. In the following figure, we assume that there is a rectangle with both sides of length 4. Taking its center as the origin, we establish a two-dimensional XY coordinate axis, which can be obtained as follows:

When we enlarge the rectangle by 2 times, we will get a rectangle with both sides of length 8. Continue to take the center as the origin to establish a two-dimensional XY coordinate axis, and the following figure can be obtained:

If we mathematically abstract the graphic coordinate points of these two graphs, we can obtain the following two matrices:

A matrix is A:


[ 2 2 2 2 2 2 2 2 ] \left[ \begin{matrix} -2 & 2 \\ 2 & 2 \\ 2 & -2 \\ -2 & -2 \\ \end{matrix} \right]

Matrix B:


[ 4 4 4 4 4 4 4 4 ] \left[ \begin{matrix} -4 & 4 \\ 4 & 4 \\ 4 & -4 \\ -4 & -4 \\ \end{matrix} \right]

That is to say, the enlargement of the rectangle by 2 times is actually just the transformation of matrix A into matrix B, so we can skillfully transform the problem of rectangular scaling into the transformation of matrices. We can use the matrix mathematical formula for abstract calculation. Next, we will understand the basis of matrix transformation: matrix multiplication.

Matrix multiplication

setAforThe matrix,BforOf the matrix, then calledThe matrix ofCFor matrixAwithBThe product of theta, let’s call it theta, where the first in matrix CLine firstColumn elements can be expressed as:

As follows:

Remember that A and B can be multiplied only if the number of columns in MATRIX A equals the number of rows in matrix B. Because later on because of this principle and convenience of calculation, we’re going to convert the 4×2 matrix to the 4×4 matrix.

For easy understanding, here is an excerpt from the introduction of 3×3 matrix multiplication in the book 3D Mathematics Fundamentals: Graphics and Game Development to help you understand and recall the specific details of matrix multiplication.

Matrix transformation

When we talk about transformations, we usually use functions (also called maps) in mathematics, which take inputs and produce outputs. We can call the F function/mapping from a to B F(a)=b. The easiest way to use mathematical tools to solve transformations between matrices (scaling is one kind of transformation, but there are also translation, rotation, shear, etc.) is to find the mappings expressed by matrices and the rules for how they operate.

In primary school, we all learned the four operations of mathematics. For example, there is a number A now. If we want to make a twice as large as before, we would use:


a = a 2 a’ = a * 2

If we want to scale a matrix, we need to find a similar rule for multiplying a matrix by what matrix gives it a multiple. Remember when we started learning math in kindergarten? Except for the special number 0, we know that the world of this number starts from 1, and other numbers can be obtained by adding and subtracting 1. For example, the 2 we need above can be obtained by 1+11 +11 +1. Then what is the 1 in the matrix becomes an important thing.

The one in the matrix1— Identity matrix

In matrix multiplication, one kind of matrix plays a special role, like the one in number multiplication, and this kind of matrix is called the identity matrix. It is a square matrix, and the diagonal from the top left to the bottom right (called the main diagonal) has elements of 1. Everything else is 0.

[1001] \left[\begin{matrix} 1&0 \\ 0&1 \end{matrix} right][1001], 3×3 unit matrix [100010001] \left[\begin{matrix} 1&0&0 \\ 0&1&0 \\ 0&0&1 \end{matrix} \right]⎣⎢, “100010001” ⎥⎤, Unit 4 x4 matrix [1000010000100001] \ left [\ begin {matrix} 1 & & & 0 0 0 \ \ & 1 & & 0 0 0 \ \ 0 & 0 and 1 & 0 \ \ & & 0 0 0 1 \ \ & {matrix} \ \ end right] ⎣ ⎢ ⎢ ⎢ ⎡ 1000010000100001 ⎦ ⎥ ⎥ ⎥ ⎤

By the identity matrix, any matrix multiplied by the identity matrix is equal to itself.

Now that we know what a “1” is, what is a “2”? For example, the “2” of a 2×2 matrix is [2002] \left[\begin{matrix} 2&0 \\ 0&2 \end{matrix} right]. A=[1001] A= \left[\begin{matrix} 1&0 \\ 0&1 \end{matrix} right]A=[1001] If B=A∗[2002]B =A * \left[\begin{matrix} 2&0 \\ 0&2 \end{matrix} \right]B=A∗[2002], according to the calculation rules of matrix multiplication mentioned above, We can get B=[2002]B = \left[\begin{matrix} 2&0\0&2 \end{matrix} right]B=[2002], then we can think of BBB matrix as double of AAA matrix.

Scaling along the axis

When I say I’m going to scale the matrix by 2, it’s just to make it easier to understand, but it’s actually more accurate to say I’m going to scale along the axis, because in addition to scaling along the axis, I can scale in any direction, like 45 degrees in the first quadrant of the axis. Because this article mouse wheel scaling does not involve scaling along any direction for the time being, so this later free to write an article to explain.

2D scaling matrix along the axis

If there is a matrix M=[p00q] M= \left[\begin{matrix} p&0 \\ 0&q \end{matrix}\right]M=[p00q], Let’s think of it as the basis vectors p parallel to the X axis and Q parallel to the Y axis in 2D. Assuming two scaling factors: kxk_{x}kx and kyk_{y}ky, then:


p = k x p = k x [ 1 0 ] = [ k x 0 ] p^{‘}=k_{x}p=k_{x}\left[\begin{matrix} 1 & 0 \end{matrix}\right]=\left[\begin{matrix} k_{x} & 0 \end{matrix}\right]

q = k y p = k y [ 0 1 ] = [ k y 0 ] q^{‘}=k_{y}p=k_{y}\left[\begin{matrix} 0 & 1 \end{matrix}\right]=\left[\begin{matrix} k_{y} & 0 \end{matrix}\right]

Using the basis vector to construct the matrix, the 2D scaling matrix along the coordinate axis is as follows:


S ( k x . k y ) = [ p q ] = [ k x 0 0 k y ] S(k_{x},k_{y})=\left[ \begin{matrix} p^{‘} \\ q^{‘} \\ \end{matrix} \right]=\left[ \begin{matrix} k_{x} & 0 \\ 0 & k_{y} \end{matrix} \right]

M ‘M^{‘}M’ : M ‘M^{‘}M’ : M ‘M^{‘}M’


M = M [ 2 0 0 1 3 ] M^{‘}=M*\left[ \begin{matrix} 2 & 0 \\ 0 & \frac{1}{3} \end{matrix} \right]

3D scaling matrix along the axes

For 3D, add a third scaling factor kzk_{z}kz, and the 3D scaling matrix along the coordinate axis is as follows:


S ( k x . k y . k z ) = [ k x 0 0 0 k y 0 0 0 k z ] S(k_{x},k_{y},k_{z})=\left[ \begin{matrix} k_{x} & 0 & 0 \\ 0 & k_{y} & 0 \\ 0 & 0 & k_{z} \end{matrix} \right]

4D scaling matrix along the axes

For 4D, add the fourth scaling factor kWk_{W}kW, and the 4D scaling matrix along the coordinate axis is as follows:


S ( k x . k y . k z . k w ) = [ k x 0 0 0 0 k y 0 0 0 0 k z 0 0 0 0 k w ] S(k_{x},k_{y},k_{z},k_{w})=\left[ \begin{matrix} k_{x} & 0 & 0 & 0 \\ 0 & k_{y} & 0 & 0 \\ 0 & 0 & k_{z} & 0 \\ 0 & 0 & 0 & k_{w} \end{matrix} \right]

How to represent a 2D matrix with a 3D matrix?

Compared with 2D matrix, 3D matrix has more expressions about ZZZ axis. Since 2D plane can be regarded as “flattened object” in 3D coordinate system, we need to give it a value of ZZZ axis, but it cannot be 0. At this time, the value of ZZZ axis is 1.

For example, 2D matrix A mentioned above: [- 22222-2-2-2] \ left [\ begin {matrix} – 2 & 2 2 & 2 \ \ \ \ \ \ & – 2-2 & 2-2 {matrix} \ \ \ \ end right] ⎣ ⎢ ⎢ ⎢ ⎡ – 222-222-2-2 ⎦ ⎥ ⎥ ⎥ ⎤, When transformed into 3D matrix, it is: [21] – 2212212-21-2 – \ left [\ begin {matrix} – 2 & 2 & 1 \ \ 2 & 2 \ \ & 1 & 2-2 \ \ & 1-2 & – 2 & 1 \ \ \ end {matrix} \ right] ⎣ ⎢ ⎢ ⎢ ⎡ – 222-222-2-21111 ⎦ ⎥ ⎥ ⎥ ⎤

How to use 4D matrix to represent 2D matrix?

Compared with 2D matrix, 4D matrix has more expressions about ZZZ axis and WWW axis.

For example, 2D matrix A mentioned above: [- 22222-2-2-2] \ left [\ begin {matrix} – 2 & 2 2 & 2 \ \ \ \ \ \ & – 2-2 & 2-2 {matrix} \ \ \ \ end right] ⎣ ⎢ ⎢ ⎢ ⎡ – 222-222-2-2 ⎦ ⎥ ⎥ ⎥ ⎤, The 4D matrix is: [- 221122112-211-2-211] \ left [\ begin {matrix} – 2 & 2 & 1 & 1 \ \ 2 & 2 & 1 &1 \ \ & 2-2 & 1 &1 \ \ – 2 & – 1 \ \ & 1 & 2 {matrix} \ \ end right] ⎣ ⎢ ⎢ ⎢ ⎡ – 222-222-2-211111111 ⎦ ⎥ ⎥ ⎥ ⎤

Matrix computing library GL-matrix

Gl-matrix is an open source matrix computing library written in JavaScript. We can take advantage of the matrix-to-matrix computing capabilities provided by this library to simplify and speed up our development. In order to avoid reducing complexity, the following article adopts the native ES6 syntax, uses the

Scale the element from the current mouse position

We have already reduced the scaling of an element to the scaling of a rectangle. Now we continue to abstract the scaling of a rectangle to the scaling of a coordinate point on the coordinate axis, so as to see the surface of a point.

Suppose there are two coordinate points (−3,0)\left(-3,0 \right)(−3,0) and (3,0)\left(3,0 \right)(3,0) in the XY coordinate axis, the distance between them is 6, as shown below:

The two coordinate points (−3,0)\left(-3,0 \right)(−3,0) and (3,0) are centered on the origin, \left(3,0 \right) and (3,0) are centered on the origin, and the two coordinate points are enlarged by 2 times along the X-axis and X-axis. The new coordinate points (−6,0)\left(-6,0 \right)(−6,0) and (6,0)\left(6,0 \right)(6,0) can be obtained, and the distance between them is 12, as shown below:

If we want to keep the distance between the two coordinate points 12 units after magnification, and the position of the coordinate point in the positive direction of X-axis X-axis unchanged, then we need to move the two coordinate points 3 units to the left along X-axis X-axis X-axis after magnification, namely -3, as shown below:

Observation can be obtained:


3 = 3 3 2 = 3 ( 1 2 ) That is: after scaling in X / Y Offset on axis = X / Y Coordinate values ( 1 Zoom multiples ) – 3 = 3-3 * 2 = 3 * (1-2) \ \ : the resized offset on the X/Y = X/Y coordinate values * (1 – zoom multiples)

In fact, the above process is the current mouse point as the origin of the zoom graph process abstraction, that is, first zoom graph, and then the original zoom point translation back to the previous position.

4×4 translation matrix

Since the 3×3 transformation matrix represents linear transformation and does not include translation, translation can still be expressed by matrix multiplication of 4×4 matrix in 4D:


[ x y z 1 ] [ 1 0 0 0 0 1 0 0 0 0 1 0 Δ x Δ y Δ z 1 ] = [ x + Δ x y + Δ y z + Δ z 1 ] \left[\begin{matrix}x &y &z &1 \end{matrix}\right]\left[\begin{matrix}1 &0 &0 &0\\ 0&1&0&0\\0&0&1&0\\\Delta x &\Delta y &\Delta z&1 \end{matrix}\right]=\left[\begin{matrix}x+\Delta x &y+\Delta y &z+\Delta z &1 \end{matrix}\right]

Matrix calculations express scaling and then translation

Suppose we have a matrix VVV that scales and then shifts, The scaling matrix is R=[KX0000KY0000Kz0000KW]R=\left[\begin{matrix} k_{x} &0 & 0 & 0 \\ 0 & K_ {y} &0 & 0 \\ 0 & 0 & k_{z} &0 \\ 0 & 0 & 0 & k_ {w} {matrix} \ \ end right] R = ⎣ ⎢ ⎢ ⎢ ⎡ kx0000ky0000kz0000kw ⎦ ⎥ ⎥ ⎥ ⎤, The translation matrix is T=[100001000010 δ x δ Y δ z1]T=\left[\begin{matrix} 1&0&0&0\0&1&0\ 0&0&1&0\\ Delta x &\Delta y &\Delta z&1 T = {matrix} \ \ end right] ⎣ ⎢ ⎢ ⎢ ⎡ 100 Δ x010 Δ y001 Δ z0001 ⎦ ⎥ ⎥ ⎥ ⎤, then:


v = v R T v^{‘}=v*R*T

The matrix allows the Div element to be scaled with the mouse as the origin

Suppose the page now has a div element with ID app in the middle of the page, as follows:

<! DOCTYPEhtml>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="Width = device - width, initial - scale = 1.0">
    <title>Matrix scaled Div</title>
    <style>
        *,
        *::before,
        *::after {
            box-sizing: border-box;
        }

        body {
            position: relative;
            background-color: #eee;
            min-height: 1000px;
            margin: 0;
            padding: 0;
        }

        #app {
            position: absolute;
            left: 50%;
            top: 50%;
            transform: translate(-50%, -50%);
            width: 200px;
            height: 200px;
            border: 1px dashed black;
        }
    </style>
</head>

<body>
    <div id="app"></div>
    <script src="./gl-matrix-min.js"></script>
    <script src="./index.js"></script>
</body>

</html>
Copy the code

The layout effect is as follows:

First of all, we need to get information about Div Element position and wide high information, use them to form matrix, this Element can use #. GetBoundingClientRect () this API.

Then listen to the mouse scroll event of DIV# app. When scrolling, judge whether to zoom in or out according to the value of deltaY of the event object. Here, in order to keep consistent with the original zoom direction of Windows system, select zoom out when scroll down and zoom out when scroll up, that is, zoom out when the value of deltaY is less than 0 and zoom out when it is less than 0.

Matrix transformation multiplication, here we use 4×4 matrix, so we can use the API glmatrix.mat4.multiply, so the code is as follows:

document.addEventListener("DOMContentLoaded".() = > {
    const $app = document.querySelector(`#app`);

    $app.addEventListener("wheel".(e) = > {
        const {clientX, clientY, deltaY } = e;
        let scale = 1 + (deltaY < 0 ? 0.1 : -0.1);
        scale = Math.max(scale > 0 ? scale : 1.0.1);
        const {top, right, bottom, left}   = $app.getBoundingClientRect();
        const o = new Float32Array([
            left, top, 1.1,
            right, top, 1.1,
            right, bottom, 1.1,
            left, bottom, 1.1
        ]);
        const x = clientX * (1 - scale);
        const y = clientY * (1 - scale);
        const t = new Float32Array([
            scale, 0.0.0.0, scale, 0.0.0.0.1.0.0.0.0.1
        ]);
        const m = new Float32Array([
            1.0.0.0.0.1.0.0.0.0.1.0,
            x, y, 0.1
        ]);
        // Scale on the XY axis
        let res1 = glMatrix.mat4.multiply(new Float32Array([
            0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0
        ]), t, o);
        // Shift on the XY axis
        const res2 = glMatrix.mat4.multiply(new Float32Array([
            0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0
        ]), m, res1);
        $app.setAttribute("style".`left: ${res2[0]}px; top: ${res2[1]}px; width:${res2[4] - res2[0]}px; height:${res2[9] - res2[1]}px; transform: none; `);
    });
});
Copy the code

The effect is shown below:

The matrix implements Div element drag and drop

The matrix implementation of Div element drag and drop is similar to our usual implementation of drag and drop code, but the absolute positioning information data into a translation matrix, the specific code is as follows:

document.addEventListener("DOMContentLoaded".() = > {
    const $app = document.querySelector(`#app`);
    const width = $app.offsetWidth;
    const height = $app.offsetHeight;
    let isDrag = false;
    let x; // The abscissa value of the mouse when the mouse is dragged
    let y; // The vertical value of the mouse when the mouse is dragged
    let left; // The element's abscissa offset from the top left corner of the page
    let top; // The vertical offset of the element from the top left corner of the page
    
    $app.addEventListener("mousedown".(e) = > {
        const bcr = $app.getBoundingClientRect();
        isDrag = true;
        x = e.clientX;
        y = e.clientY;
        left = bcr.left + window.scrollX;
        top = bcr.top + window.scrollY;
    });
    document.addEventListener("mousemove".(e) = > {
        if(! isDrag) {return;
        }
        const {clientX, clientY} = e;
        const movementX = clientX - (x - left); // Calculate the offset of the X axis
        const movementY = clientY - (y - top); // Calculate the offset of the Y axis
        // Shift the matrix
        const t = new Float32Array([
            movementX, movementY
        ]);
        // Calculate the absolute positioning matrix relative to the top left corner of the page
        const res = glMatrix.mat2.add(new Float32Array([0.0]),  t, new Float32Array([0.0]));
        $app.setAttribute("style".`left: ${res[0]}px; top:${res[1]}px; width:${width}px; height:${height}px; transform: none; `);
    })
    document.addEventListener("mouseup".() = > {
        isDrag = false;
    });
});

Copy the code

The matrix allows for both dragging and scaling of Div elements

Since matrix multiplication is associative, given the existing matrix VVV, which scales and then shifts, The scaling matrix is R=[KX0000KY0000Kz0000KW]R=\left[\begin{matrix} k_{x} &0 & 0 & 0 \\ 0 & K_ {y} &0 & 0 \\ 0 & 0 & k_{z} &0 \\ 0 & 0 & 0 & k_ {w} {matrix} \ \ end right] R = ⎣ ⎢ ⎢ ⎢ ⎡ kx0000ky0000kz0000kw ⎦ ⎥ ⎥ ⎥ ⎤, The translation matrix is T=[100001000010 δ x δ Y δ z1]T=\left[\begin{matrix} 1&0&0&0\0&1&0\ 0&0&1&0\\ Delta x &\Delta y &\Delta z&1 T = {matrix} \ \ end right] ⎣ ⎢ ⎢ ⎢ ⎡ 100 Δ x010 Δ y001 Δ z0001 ⎦ ⎥ ⎥ ⎥ ⎤, so are:

V ‘∗ ∗ ∗ T R = v = v ([kx0000ky0000kz0000kw] [100001000010 Δ Δ Δ z1] y x) = v ∗ [kx0000ky0000kz0 Δ Δ x y Δ ZKW] v ^ {‘} * * R * T = v = v (\ left [\ begin {matrix} k_{x} & 0 & 0 & 0 \\ 0 & k_{y} & 0 & 0 \\ 0 & 0 & k_{z} & 0 \\ 0 & 0 & 0 & k_{w} \end{matrix} \right]\left[\begin{matrix}1 &0 &0 &0\\ 0&1&0&0\\0&0&1&0\\\Delta x &\Delta y &\Delta z&1 \end{matrix}\right])=v*\left[ \begin{matrix} k_{x} & 0 & 0 & 0 \\ 0 & k_{y} & 0 & 0 \\ 0 & 0 & k_{z} & 0 \\ \Delta x &\Delta y &\Delta z & k_{w} \end{matrix} ‘= v ∗ \ right] v R ∗ ∗ T = v (⎣ ⎢ ⎢ ⎢ ⎡ kx0000ky0000kz0000kw ⎦ ⎥ ⎥ ⎥ ⎤ ⎣ ⎢ ⎢ ⎢ ⎡ 100 Δ x010 Δ y001 Δ z0001 ⎦ ⎥ ⎥ ⎥ ⎤) = v ∗ ⎣ ⎢ ⎢ ⎢ ⎡ kx00 Δ x0ky0 Δ y00kz Δ z000kw ⎦ ⎥ ⎥ ⎥ ⎤ Here’s the code for dragging and scaling Div elements at the same time:

document.addEventListener("DOMContentLoaded".() = > {
    const $app = document.querySelector(`#app`);
    let isDrag = false;
    let x; // The abscissa value of the mouse when the mouse is dragged
    let y; // The vertical value of the mouse when the mouse is dragged
    let left; // The element's abscissa offset from the top left corner of the page
    let top; // The vertical offset of the element from the top left corner of the page


    function reDraw(el, t, move=false) {
        const bcr = el.getBoundingClientRect();
        const {width, height} = bcr;
        const o = new Float32Array([
            bcr.left, bcr.top, 1.1,
            bcr.right, bcr.top, 1.1,
            bcr.right, bcr.bottom, 1.1,
            bcr.left, bcr.bottom, 1.1,]);const out = new Float32Array([
            0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0,]);const res = glMatrix.mat4.multiply(out,  t, o);
        const left = parseInt(res[0]);
        const top = parseInt(res[1]);
        // There is no need to adjust the width and height if it is moving
        const w = move ?  width : res[4] - left;
        const h = move ? height : res[9] - top;
        el.setAttribute("style".`left: ${left}px; top:${top}px; width:${w}px; height:${h}px; transform: none; `);
    }

    $app.addEventListener("mousedown".(e) = > {
        const bcr = $app.getBoundingClientRect();
        isDrag = true;
        x = e.clientX;
        y = e.clientY;
        left = bcr.left + window.scrollX;
        top = bcr.top + window.scrollY;
    });
    document.addEventListener("mousemove".(e) = > {
        if(! isDrag) {return;
        }
        const {clientX, clientY} = e;
        const movementX = clientX - (x - left); // Calculate the offset of the X axis
        const movementY = clientY - (y - top); // Calculate the offset of the Y axis
        // 4x4 translation matrix
        const t = new Float32Array([
            0.0.0.0.0.0.0.0.0.0.0.0,
            movementX, movementY, 0.1
        ]);
        reDraw($app, t, true);
    })
    document.addEventListener("mouseup".() = > {
        isDrag = false;
    });
    $app.addEventListener("wheel".(e) = > {
        const {clientX, clientY, deltaY } = e;
        const currSacle = 1 + (deltaY < 0 ? 0.1 : -0.1);
        const zoom = Math.max(currSacle > 0 ? currSacle : 1.0.1);
        const x = (clientX + window.scrollX) * (1 - zoom);
        const y = (clientY + window.scrollY) * (1 - zoom);
        const t = new Float32Array([
            zoom, 0.0.0.0, zoom, 0.0.0.0.1.0,
            x, y, 0.1,]); reDraw($app, t); }); });Copy the code

The matrix implements both drag and zoom on the Canvas image

The drag and zoom logic of Canvas picture is basically the same as that of ordinary Div. The difference lies in that what we need to modify is the current transformation matrix of Canvas rendering, which is the identity matrix at the beginning. We only need to carry out corresponding matrix transformation, set up a new transformation matrix, and hand it to Canvas base rendering. The specific code is as follows:

<! DOCTYPEhtml>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="Width = device - width, initial - scale = 1.0">
    <title>Canvas zooming and dragging</title>
    <style>
        body {
            position: relative;
            background-color: black;
            min-height: 1000px;
            margin: 0;
            padding: 0;
        }

        #app {
            border:1px solid white;
        }
    </style>
</head>
<body>
    <canvas id="app" width="640" height="340"></canvas>
    <script src="./gl-matrix-min.js"></script>
    <script src="./index.js"></script>
</body>
</html>
Copy the code
// index.js
document.addEventListener("DOMContentLoaded".() = > {
    const $app = document.querySelector(`#app`);
    const {width, height} = $app.getBoundingClientRect();
    const ctx = $app.getContext("2d");
    const $img = document.createElement("img");
    $img.onload = () = > {
        ctx.drawImage($img, 0.0);
    };
    $img.src = "./01.png";
    let isDrag = false;
    let ov = new Float32Array([
            1.0.0.0.0.1.0.0.0.0.1.0.0.0.0.1,]);function reDraw(ctx, o, t) {
        const out = new Float32Array([
            0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0,]);const nv = glMatrix.mat4.multiply(out,  t, o);
        ctx.save();
        ctx.clearRect(0.0, width, height);
        ctx.transform(nv[0], nv[4], nv[1], nv[5], nv[12], nv[13]);
        ctx.drawImage($img, 0.0);
        ctx.restore();
        return nv;
    }

    $app.addEventListener("mousedown".(e) = > {
        isDrag = true;
    });

    document.addEventListener("mousemove".(e) = > {
        if(! isDrag) {return;
        }
        const {movementX, movementY} = e;
        const t = new Float32Array([
            1.0.0.0.0.1.0.0.0.0.1.0,
            movementX, movementY, 0.1,]); ov = reDraw(ctx, ov, t); });document.addEventListener("mouseup".(e) = > {
        isDrag = false;
    });

    $app.addEventListener("wheel".(e) = > {
        const {clientX, clientY, deltaY } = e;
        const currSacle = 1 + (deltaY < 0 ? 0.1 : -0.1);
        const zoom = Math.max(currSacle > 0 ? currSacle : 1.0.1);
        const x = clientX * (1 - zoom);
        const y = clientY * (1 - zoom);
        const t = new Float32Array([
            zoom, 0.0.0.0, zoom, 0.0.0.0.1.0,
            x, y, 0.1,]); ov = reDraw(ctx, ov, t); }); });Copy the code

conclusion

This is a series of articles on the use of linear algebra in the front end, and more practical articles on linear algebra will be shared next.

Because my math level is general, writing in the hard to avoid has the wrong place, the significance of writing this article is more of a knowledge, convenient to review in the future, if you can cause your interest in mathematics applied in the front, that is even more wonderful, especially for the background management system form the front-end engineers like me, in the form to find other fun.