Recently in the company's Internal Technology Association forum, when wandering, inadvertently found a hand Amoy front-end big Nuetzen two years ago wrote a blog, about the canvas 2d mapping technology. It feels pretty magical after seeing it. So I realized it for a moment. But Zenghan predecessors of that blog is just about the realization of the idea, the whole logic or their own slowly groping out, the process is very sad, so here to record and share, let me like the canvas of the people have some harvest it.

Nonsense don't say, first put out the demo, at least let everyone see how we want to achieve the effect:

First Demo: image pulling Deformation demo_1

a second demo: image 3d Deformation demo_2

After watching the demo, do you think it's fun?

If you think it's fun, go ahead and I'll analyze the entire implementation logic in the next step. The main thing is the implementation of the first demo logic, because the second one is based on the first implementation, as long as the first understanding of the principle, the second becomes very simple.

The first demo, the realization of the pull of the image, involving this deformation, the first thought is transform, yes, is the canvas 2d drawing API transform. The abcdef six values passed in the transform method are the parameters of the transformation matrix. In other words, we can modify these six values to achieve the deformation of the image operation.

Transform () allows you to scale, rotate, move, and tilt the current environment. If you are not familiar with transform, you can read this blog: http://yehao.diandian.com/post/2012-12-30/40046242001 inside is still very detailed.

After understanding transform, you will find that transform can do as if only zoom, rotate, move, tilt these several functions. But the demo1 can be pulled into a variety of shapes, it does not feel like it can be achieved with these few. But in fact, it really is to use these transformations to achieve.

Demo1 map to the right there is a numeric selection, when you select 1, and select the Display box, we see a picture:

Yes, what does this mean, that this picture is actually divided into two pieces, the upper left corner of the triangle and the lower right corner of the triangle, we drag a piece, and then look at the effect:

In order to facilitate understanding, I added the auxiliary line, draw the auxiliary line, it becomes very simple, equivalent to two pieces, the above normal picture, a piece is turned from the red circle, the other is turned into a black circle up, when with the brush completion, two triangles are actually a parallelogram, And from the rectangle into parallelogram, transform can do, when it becomes the shape we need, then through the canvas clip method, only half of the triangle, the two triangles together. It has a pull effect.

And in order to make the pull effect more realistic, it is necessary to use more triangular area, when I divide the rectangle into 20*20 small rectangle, that is, when the 20*20*2 triangle, when the mouse pulled when the following effects appear:

Above, is the entire theoretical logic of demo1.

The next step is to say how the code should be implemented:

The first is the image of the deformation effect, that is, with transform, to pass in the matrix parameters, at first I was to use the vector to do, but to do after the discovery of the vector will be a lot of other problems. For example: When the picture pulls excessively, the picture flips out the question, and so on ...

So in the end, still chose to use algebraic method to achieve, that is, to solve the ternary one-time equation!!!!

Why is it that it is a three-dimensional equation? Because the rules that follow transform's matrix operations

|a, B, 0|

[x,y,1] = [X, Y, 1] * |c, D, 0|

|e, F, 1|

The solution is this: X = ax + cy +e and Y = bx + dy + F, that is, the new coordinates of the XY value is equal to the old coordinates of the XY value of some operations can be obtained.

Relative, that is, as long as we know the parallelogram three vertex transformation before and after the coordinate values, we can calculate abcdef six matrix parameters, and then we first use transform to change the drawing environment, and then the picture to the parallelogram transformation before the position, you can draw the corresponding tilt effect.

So, first of all, we need to encapsulate a method to solve the ternary-once equation and get the matrix parameters:

First solve the three-dimensional equation of the method, the specific principle I do not speak, Baidu a bit to know, or have the spirit of the mind can himself take written calculation a bit:

/** * Solve ternary once equation, need to pass in three sets of equation parameter * @param arr1 first set of parameters * @param arr2 Second set of parameters * @param ARR3 Third Group Number * @returns {{x:number, y:number, Z:number}}*/ functionequation (arr1, arr2, ARR3) {varA1 = +arr1[0]; varB1 = +arr1[1]; varC1 = +arr1[2]; varD1 = +arr1[3]; varA2 = +arr2[0]; varB2 = +arr2[1]; varC2 = +arr2[2]; varD2 = +arr2[3]; varA3 = +arr3[0]; varB3 = +arr3[1]; varC3 = +arr3[2]; varD3 = +arr3[3]; //separating compute units varM1 = C1-(B1 * C2/B2); varm2 = C2-(B2 * C3/b3); varM3 = d2-(B2 * D3/b3); varM4 = A2-(B2 * A3/b3); varM5 = d1-(B1 * D2/B2); varM6 = A1-(B1 * A2/B2); //Calculate XYZ varx = ((m1/m2) * M3-M5)/((M1/M2) * M4-M6); varz = (M3-M4 * x)/m2; vary = (D1-A1 * X-C1 * z)/B1; return{x:x, y:y, z:z}}

Then is to get the matrix, in fact, the various parameters are collated, passed into the solution equation method, Processing:

/** * Based on the point coordinates before and after the change, calculate the matrix * @param arg_1 change before coordinates 1 * @param _arg_1 Change coordinates 1 * @param arg_2 change before coordinates 2 * @param _arg_2 Change coordinates 2 * @param arg_3 change before coordinates 3 * @param _arg_3 change after coordinates 3 * @returns {a:number, B:numbe R, C:number, D:number, E:number, F:number}}*/ functionGetmatrix (arg_1, _arg_1, Arg_2, _arg_2, Arg_3, _arg_3) {//incoming x value solution the first equation is x = ax + cy + E for Ace //The four parameters passed, corresponding to the three-dimensional equation: The four parameters of Ax+by+cz=d: A, B, C, D, and the Matrix equation C is 1 varARR1 = [arg_1.x, ARG_1.Y, 1, _arg_1.x]; varARR2 = [arg_2.x, ARG_2.Y, 1, _arg_2.x]; varARR3 = [arg_3.x, ARG_3.Y, 1, _arg_3.x]; varresult =equation (arr1, arr2, ARR3); //Incoming Y value solution the second equation is y = bx + dy + F to seek the BDFARR1[3] =_arg_1.y; arr2[3] =_arg_2.y; arr3[3] =_arg_3.y; varRESULT2 =equation (arr1, arr2, ARR3); //get A, C, e varA =Result.x; varc =Result.y; varE =result.z; //get B, D, F varb =result2.x; varD =result2.y; varf =result2.z; return{a:a, b:b, C:c, D:d, E:e, f:f} ; }

After the calculation, we can get to six matrix parameters.

These two calculations seem simple, but carelessly prone to error, the landlord before doing the time has been wrong, has not known the reason in the end, and finally manually solved the ternary equation once, only to find that a certain parameter is wrong. Therefore, the landlord to encapsulate the two calculations for later reuse:

GitHub Address: Https://github.com/whxaxes/wheels/tree/master/matrix is interested in or need to be able to use

Back to the point, to the present, we can get all the matrix parameters, the next problem is how to put any four-point line formed by the quadrilateral into N-part logic (note: Is any four points, because after pulling, four points formed by the coordinates is not a rectangle). This can be done with vectors, the use of vectors, the amount of computation will be much smaller, the performance of the promotion is also very helpful.

How to achieve it, draw a picture is clear:

We just need to get to the ad vector, as well as the BC vector, put two vector n equal, and then use a loop, in each equal to get the vector ab direction, and then the n equal, then calculate, you can get all the points. Because of the vector, we don't have to think about the angle at all, regardless of the shape of the quadrilateral, as long as we have four points of coordinates, we can calculate the inside of all the point coordinates. The code is as follows:

/** * to divide the ABCD quadrilateral into n parts of N, to obtain all the point coordinates of n equal points * @param n how many divisions * @param a point coordinate * @param b b Point coordinate * @ param c Point coordinates * @param d d Point coordinates * @returns {Array}*/ functionRectsplit (n, a, B, C, d) {//ad vector Direction n equal varad_x = (d.x-a.x)/n;varAd_y = (D.Y-A.Y)/n;//BC vector Direction N equal varbc_x = (c.x-b.x)/n;varBc_y = (c.y-b.y)/n;varNdots = []; varx1, y1, x2, y2, ab_x, ab_y; //The left point increments, the right point increments, gets the new vector after each increment, and continues n equal to get all the point coordinates for(vari=0;i<=n;i++){ //get the coordinates of AD vector nX1 = a.x + ad_x *i; Y1= A.y + ad_y *i; //get the BC vector n equal coordinatesx2 = b.x + bc_x *i; Y2= B.y + bc_y *i; for(varj=0;j<=n;j++){ //The ab vectors are: [X2-x1, Y2-y1], so the increment after n is equal to nab_x = (x2-x1)/n; Ab_y = (y2-y1)/n;Ndots.push ({x:x1+ ab_x *J, Y:y1+ ab_y *J})}}returnndots; }

When the calculations are complete and the points are drawn to each coordinate, dragging four vertices will result in the following effects, regardless of how my four vertex positions are changing, ensuring that all points are positioned correctly.

When this is finished, the entire demo production is basically completed, and then is the rendering of the picture, the next logic is quite simple, first with the above Rectsplit method to divide the current quadrilateral into n parts, and get all the coordinates point, Of course, you also need to directly get the initial quadrilateral into the N parts of all the coordinate points, but this can be initialized at the beginning of the good, because this value will not change, there is no need to repeat the calculation.

The two sets of point coordinates are obtained, then the matrix is passed into the method, the clip is processed, and the picture is drawn, and the entire rendering process is completed.

/** * Canvas Rendering*/ functionrender () {Ctx.clearrect (0,0, Canvas.width,canvas.height); varNdots = Rectsplit (count, Dots[0], dots[1], dots[2], dots[3]); Ndots.foreach (function(d, i) {//get four points of a quad varDOT1 =Ndots[i]; varDot2 = ndots[i + 1]; varDOT3 = ndots[i + count + 2]; varDOT4 = ndots[i + count + 1]; //gets the four points of the initial quad varIDOT1 =Idots[i]; varIdot2 = idots[i + 1]; varIDOT3 = idots[i + count + 2]; varIDOT4 = idots[i + count + 1]; if(Dot2 && dot3 && i% (count+1) <count) { //draw the lower half of the trianglerenderimage (IDOT3, Dot3, Idot2, Dot2, IDOT4, DOT4); //draw the top half of the trianglerenderimage (idot1, Dot1, Idot2, Dot2, IDOT4, DOT4); } if(Hasdot) {ctx.save (); Ctx.fillstyle= "Red"; Ctx.fillrect (d.x-1, D.y-1, 2, 2); Ctx.save (); } }); } /** calculate matrix, render picture simultaneously * @param arg_1 * @param _arg_1 * @param arg_2 * @param _arg_2 * @param arg_3 * @param _arg_3*/ functionrenderimage (arg_1, _arg_1, Arg_2, _arg_2, Arg_3, _arg_3) {ctx.save (); //create clipping regions based on transformed coordinatesCtx.beginpath (); Ctx.moveto (_arg_1.x, _ARG_1.Y); Ctx.lineto (_arg_2.x, _ARG_2.Y); Ctx.lineto (_arg_3.x, _ARG_3.Y); Ctx.closepath (); if(hasrect) {ctx.linewidth= 2; Ctx.strokestyle= "Red"; Ctx.stroke (); } ctx.clip (); if(haspic) {//point coordinates before and after the transformation, calculating the transformation matrix varresult = Matrix.getMatrix.apply ( This, arguments); //deformationCtx.transform (RESULT.A, result.b, result.c, RESULT.D, RESULT.E, RESULT.F); //Drawing PicturesCtx.drawimage (IMG, idots[0].x, idots[0].y, Img.width, img.height); } ctx.restore (); }

At this point, the entire theory of DEMO1 and the code logic are analyzed, the following post the GitHub address of the project:

Https://github.com/whxaxes/canvas-test/tree/gh-pages/src/Funny-demo/transform

When the demo1 is done, the Demo2 is very simple, because, as long as we know the coordinates of the various points around the quadrilateral, we can make the picture become anything we want to look like.

And the above demo2 is on the basis of the demo1, adding the z-axis effect, the x, Y axis are only the plane, when the z axis is added, then the z-axis values mapped to the x, Y axis, and then the image transformation, there is a demo2 effect. Demo2 source code is also on the above GitHub address, inside the demo1.js is Demo1, Demo2.js is Demo2 logic.

So far, the whole process has been told. Thanks for a read.

Canvas 2d Mapping Technology Practice