📜 ⬆️ ⬇️

Evening class: resizing elements to pure JS

Another free evening was issued, today we will do a full-fledged resizable in any direction for any elements (div, images, whatever) in pure JavaScript.

Demo page: Resizable.js

Approach # 0

Make eight hanging divisors-resizers: four of them are strips along the sides of the element, four more - in the corners. When resizing, resize the element, as well as drag diva-resizers.
')
The cons are obvious:


Approach # 1

With a mousemove element, check if the cursor is at the edge of the element. If yes, assign the appropriate cursor style (for example, when hovering on the upper right edge will be ne-resize), if not, assign the default cursor.

When mousedown element do the same check. If yes - we start resizing in the right direction, if not - we do nothing.

We write the auxiliary function direction () , which will determine on which edge of the element the cursor is located. It will return either a number from 0 to 7 (0 - resize up, 1 - right, etc .; 4 - resize up and right, 5 - right and down, etc. clockwise). If 8 is the cursor not at the edge of the element, do not start resizing.

function direction( elem, event, pad ) { var res = 8; var pad = pad || 4; var pos = elem.getBoundingClientRect(); var top = pos.top; var left = pos.left; var width = elem.clientWidth; var height = elem.clientHeight; var eTop = event.clientY; var eLeft = event.clientX; // [...] 

The names of the arguments speak for themselves: elem is the element to be resized, event is the event object of the mousemove or mousedown element, and pad is the maximum indent from the borders in pixels (4px by default) at which you can start the resize. Those. when you hover the mouse closer than 4 pixels to the edge, the cursor will change and when you click the mouse, the resize will begin.

Starting from the dimensions and coordinates of the element, as well as from the coordinates of the event, we find out which edge the cursor is:

 var isTop = eTop - top < pad; var isRight = left + width - eLeft < pad; var isBottom = top + height - eTop < pad; var isLeft = eLeft - left < pad; if ( isTop ) res = 0; if ( isRight ) res = 1; if ( isBottom ) res = 2; if ( isLeft ) res = 3; 

That's not all - you also need to take into account the resize diagonally:

 if ( isTop && isRight ) res = 4; if ( isRight && isBottom ) res = 5; if ( isBottom && isLeft ) res = 6; if ( isLeft && isTop ) res = 7; return res; //       , //  res    8 

We introduce an auxiliary variable to assign a cursor style. We will need it in the future.

 var cursors = "nwse ne se sw nw".split(" "); 

Now let's start writing Resizable () code directly.

 function Resizable( elem, options ) { options = options || {}; options.max = options.max || [1E17, 1E17]; options.min = options.min || [10, 10]; options.allow = (options.allow || "11111111").split(""); // [...] 

Here, max and min are the maximum and minimum dimensions of the element, and allow is the allowed directions (for example, the value “11110000” will prohibit resizing diagonally).

Change the cursor when hovering the mouse close to the edge:

 elem.addEventListener( "mousemove", function ( e ) { var dir = direction( this, e ); if ( options.allow[dir] == "0" ) return; this.style.cursor = dir == 8 ? "default" : cursors[ dir ] + "-resize"; //    cursors } ); 

When mousedown element call the function resizeStart, the code of which will be next. Also prohibits text selection during resizing.

 elem.addEventListener( "mousedown", resizeStart ); document.body.onselectstart = function (e) { return false }; 

The values ​​of options will no longer be available in resizeStart, so we will cache them to use in the future.

 elem.min = options.min; elem.max = options.max; elem.allow = options.allow; elem.pos = elem.getBoundingClientRect(); 

Half the work has already been done - it remains only to resize the element, based on the direction (). With the resize to the right and down, everything is simple - you just need to change the height and width, but when you resize up and to the left, you also have to change the coordinates of the element. But first things first.

 function resizeStart( ev ) { var dir = direction( this, ev ); //   if ( this.allow[dir] == "0" ) return; //    ,   document.documentElement.style.cursor = this.style.cursor = cursors[ dir ] + "-resize"; var pos = this.getBoundingClientRect(); var elem = this; //    mousemove  var height = this.clientHeight; var width = this.clientWidth; //        //  resize().    . document.addEventListener( "mousemove", resize ); //       //     document.addEventListener( "mouseup", function () { document.removeEventListener( "mousemove", resize ); document.documentElement.style.cursor = elem.style.cursor = "default"; document.body.onselectstart = null; }); }; 

The final frontier is the internal function resize () , which will do all the work.

 function resize ( e ) { //            //      //   if-    if ( dir == 0 || dir == 4 || dir == 7 ) { elem.style.top = e.clientY - ev.clientY + pos.top; elem.style.height = height + ev.clientY - e.clientY; } if ( dir == 1 || dir == 4 || dir == 5 ) { elem.style.width = e.clientX - pos.left; } if ( dir == 2 || dir == 5 || dir == 6 ) { elem.style.height = e.clientY - pos.top; } if ( dir == 3 || dir == 6 || dir == 7 ) { elem.style.left = e.clientX - ev.clientX + pos.left; elem.style.width = width + ev.clientX - e.clientX; } 

Additional restrictions: when resizing from top to bottom or from right to left when the minimum height or width is reached, respectively, the element starts to move down or to the right without resizing. Therefore, we do this check:

 if ( e.clientY + elem.min[1] > ev.clientY + height ) return; if ( e.clientX + elem.min[0] > ev.clientX + width ) return; 

Also check if the minimum or maximum size is reached:

 if ( elem.clientHeight < elem.min[1] ) elem.style.height = elem.min[1]; if ( elem.clientWidth < elem.min[0] ) elem.style.width = elem.min[0]; if ( elem.clientHeight > elem.max[1] ) elem.style.height = elem.max[1]; if ( elem.clientWidth > elem.max[0] ) elem.style.width = elem.max[0]; if ( e.clientY < pos.bottom - elem.max[1] ) elem.style.top = pos.bottom - elem.max[1]; if ( e.clientX < pos.right - elem.max[0] ) elem.style.left = pos.right - elem.max[0]; 

I don’t know if it could have been made more elegant ... In any case, it’s necessary to work with the element size, with its coordinates, and with the coordinates of the event, and all this based on the result direction (), and taking into account the min./max. . sizes.

In general, the plugin is ready. The source for js is here , and an example of work, as always at the very beginning.

Bonus: animated resizable


Demo page: Resizable 2.0

The principle of operation is similar, only the element itself is not resized, but the created div:

 var helper = document.createElement( "DIV" ); document.body.appendChild( helper ); helper.style.cssText = "position: fixed; border: 1px dashed black"; helper.style.width = width; helper.style.height = height; helper.style.top = pos.top; helper.style.left = pos.left; 

In if-ah written above , instead of elem there will be a helper .

When the mouseup of the document is animated, the element will be the size of the helper, and the helper will be deleted:

 document.addEventListener( "mouseup", function () { document.removeEventListener( "mousemove", resize ); document.documentElement.style.cursor = elem.style.cursor = "default"; document.body.onselectstart = null; var newpos = helper.getBoundingClientRect(); var start = new Date().getTime(); setTimeout( animate, 10 ); function animate() { var m = (new Date().getTime() - start) / 300; if (m > 1) m = 1; elem.style.top = pos.top + (newpos.top - pos.top) * m; elem.style.left = pos.left + (newpos.left - pos.left) * m; elem.style.height = height + (helper.clientHeight - height) * m; elem.style.width = width + (helper.clientWidth - width) * m; if (m < 1) setTimeout( animate, 10 ); } setTimeout( function () {document.body.removeChild( helper );}, 310 ); }); 

That's all that I wanted to say.

Source: https://habr.com/ru/post/321612/


All Articles