Html crop image. Crop images without changing aspect ratio using CSS. New image sizes

The practice of implementing the image adaptation process is still under development. Exists a large number of ideas and suggestions on how to process the pictures.

In this tutorial, we'll look at a neat little library that not only allows you to automatically resize images when you change viewport settings, but also crops images based on a given focal point. In addition, all actions are carried out in pure CSS.

Meet Focal Point

Focal Point - GitHub project and CSS library? created by Adam Bradley. The concept of responsiveness requires images to change size and position to achieve optimal proportions for the current viewport. Focal Point takes this idea a step further and not only resizes images, but also crops them.

The problem with this idea is that random cropping can remove important parts of the image! For example, in the example above, the important object is on the right side of the image. How to prevent it from being cut off?

Focal Point allows you to set the target area of ​​the image that should remain intact (focal point). This way, when the library performs cropping, the images themselves will still look great.

How it works?

The implementation of Focal Point is quite a unique, but at the same time simple process. Let's first discuss how to select the focal point, and then dive into the implementation code.

When you insert an image into a web page with using Focal Point, it is automatically divided into an invisible grid with 12x12 cells. It doesn't matter what size the picture is, the grid adapts to it.

Now we have a system for dividing the image and we can determine a point on the grid that will serve as the focal point. When cropping a picture, the specified point will be used as the central part around which the cropping occurs. That is, if we select the man's face, then this important aspect of the image will remain after making changes.

You need to find the important area, then count the number of grid units from the center of the image to it. In our example, the face is located three units to the right and three units up from the center of the grid (English names are left in the figure, since they coincide with the working parameters of the library).

Code

Now we can specify the focal point and it's time to understand the implementation code. To get started, download the project from GitHub and link the CSS file to your HTML document.

After the preliminary operations, you need to add two div elements and an img tag. Yes, the markup turns out to be redundant for one image, and this fact is a disadvantage of the library. Here's a typical markup:

As you can see, the outer div element has the class “ focal point”, and the inner one contains our image without any classes.

Converting grid units to classes

Now you need to point the Focal Point to the place where the focus of the image is. Our focal point is shifted three units to the right and three units up. Therefore, we specify the classes “ right-3" And " up-3″.

Implementing our code will result in: big screens The image will be displayed in full size, and when the viewing window is reduced, it will be reduced in size with cropping around the focal point.

Note that the image on the right is not only smaller, but also cropped around an important part. Everything is done using CSS!

Page structure

In order to better imagine how to use the library in real applications, let's build a simple page.

Let's define a div element with the class “ column”:

Let's add a title and paragraph to simulate the content of the page:

Focal Point

Lorem ipsum....

Next, we'll insert an image as shown in the previous example (with two divs and classes for the focal point):

Focal Point

Lorem ipsum....

To complete the example, let's copy the code using a different image and a different focus point.

Focal Point

Lorem ipsum...

Focal Point

Lorem ipsum...

For the second image, the focal point is in a different location:

CSS

Let's define styles for our page. Adapting images and working with a focal point are outside our area of ​​expertise. All you need to do is determine appearance elements. For example, the page is divided into two columns and styles are set for the text.

* ( margin: 0; padding: 0; -webkit-box-sizing: border-box; -moz-box-sizing: border-box; -ms-box-sizing: border-box; box-sizing: border-box ; ) .column ( float: left; overflow: auto; padding: 20px; width: 50%; ) h1 ( text-transform: uppercase; font: bold 45px/1.5 Helvetica, Verdana, sans-serif; ) p ( margin- bottom: 20px; color: #888; font: 14px/1.5 Helvetica, Verdana, sans-serif; ) @media all and (max-width: 767px) ( p ( font-size: 12px; ) h1 ( font-size: 35px; ) ) @media all and (max-width: 550px) ( h1 ( font-size: 23px; ) )

Let's see it in action

Now you can see how the demo works. If pages are displayed in a large viewing window (for example, on a monitor screen) desktop computer), then the images will be displayed in full:

Now, if you reduce the size of the viewport or view the demo page from a mobile device, you can see how the images adapt. As the window shrinks, the images are not only smaller, but also cropped.

From a design point of view, how effective is this technique!? The smaller the image becomes, the more the focal point stands out. With this library you can be sure that even on small screens users will have important information.

Browser compatibility

The library works in all new browsers. In IE8, images are resized but not cropped. But at the moment, 99.99% of sites do not have such an image adaptation mechanism with automatic cropping, so the response of IE8 is not critical.

What's inside?

Now let's look at how the library works.

The first part of the code is a basic CSS solution for responsive images.

Focal-point ( width: 100%; height: auto; overflow: hidden; ) .focal-point img ( width: 100%; max-width: 100%; height: auto; -ms-interpolation-mode: bicubic; ) .focal-point div ( position: relative; max-width: none; height: auto; )

The following code is a little more complicated. First, the media query is implemented at 767px. Then, negative margin values ​​for each of the available classes are used to crop the image. In the text of the lesson we present only the classes “ up-3" And " right-3″ that were used in the demonstration.

@media all and (max-width: 767px) ( /* 4x3 Landscape Shape (Default) */ .focal-point div ( margin: -3em -4em; ) /* Landscape up (Total 6em) */ .up-3 div ( margin-top: -1.5em; margin-bottom: -4.5em; ) .right-3 div ( margin-left: -6em; margin-right: -2em; ) )

There's not a lot of code here, but it's quite elegant. Negative margins are used with em units to crop images around a given point.

Cutting an image into fragments and then combining them into one whole picture is a long-standing technique that has become part of the web page layout arsenal. The pre-prepared drawing is cut into pieces in graphic editor, save the parts as separate graphics and then connect them together using a table.

Let's first consider why image slicing is used, and what advantages it ultimately gives, and then how to use the table in practice.

Pros of Image Slicing

Link creation

If necessary, individual pictures can be turned into links, and you can assign their own description ( title attribute ) and alternative text ( alt attribute ), which is visible when you disable the display of pictures in the browser or when you hover the mouse over the image.

Rolling effect

A set of individual fragments allows you to create a rolling effect - dynamically changing one picture to another when you hover the mouse cursor over it, and back to the previous one when the cursor moves away.

Reducing file size

It is more convenient to manipulate individual parts of the image by selecting a graphic format and its parameters for them so that the file size is minimal while maintaining acceptable image quality. As a result, a set of graphic files will take up less space and load faster than one file containing an entire drawing.

Animated GIF

Usage animated GIF for images big size is fraught with a significant increase in file size. But you can use a trick and replace only part of the image with animation, and leave the remaining fragments static. In this case, the total volume of several files will be much less than the animation of one image.

Layout features

Images on a web page are rectangular in nature, but by cutting one image into its component elements, you get a constructor from which you can build another shape. This is reminiscent of children's blocks, on one side of which a picture is pasted. You need to add such shapes on a web page for various reasons, for example, you need to add text instead of a fragment of an image. In addition, some pictures can be replaced with their background counterpart, and then the final image, while maintaining its integrity, will occupy the entire available area of ​​the document.

Psychological aspect

When one picture consists of many fragments, the browser downloads them in several threads and shows those that downloaded first. Therefore, the image appears as mosaic elements. And this immediately attracts attention and seems to load faster. So from the technical side, one picture loads faster, but from the perspective of human perception, it seems that a set of small pictures appears faster.

Preparing the image

As an example of an image where cutting is required, take Fig. 2.12. Each of the three icons is a link to the corresponding section, in addition, a link to the main page is a picture with the name of the site.

Rice. 2.12. Original image

Theoretically, in this case you can do without cutting if you use an image map (tags And ). However, this option is unacceptable due to the following considerations. When you open any section, the icon corresponding to it is transformed, which generally changes the entire image (Fig. 2.13). If you use an image map, you will have to prepare four different images (one for the main page and three more for each section), and this will ultimately affect the amount of data sent, the speed of site display and the quality of the pictures.

Rice. 2.13. Image view when opening a section

Now you need to decide how to cut the image. There may be several options here - ultimately it depends on the will of the author, the purpose of the drawing and its content.

Image cutting

It is better to entrust cutting and “assembling” a drawing to a specialized program, in particular Adobe Photoshop, so all references to tools and menus refer specifically to this program.

To make it easier to cut the image, you should first add guide lines, along which the division into fragments will then occur (Fig. 2.14). We will add the line below using styles later, so it does not participate in the image.

Rice. 2.14. Adding guides to the image

Now we use the Slice tool (activated with the K key) and trace the required rectangular area along the guides. The designated area is marked with a blue frame with the fragment number in the upper left corner. The size of the areas can be changed using the special Slice Select tool. We click the mouse with this tool on the desired fragment - the color of the frame around the area becomes yellow, and the tonality of the picture also changes. After that, you can use the mouse cursor to move the boundaries of the fragment using special markers on the sides and corners of the area (Fig. 2.15).

Rice. 2.15. Changing the fragment area

To quickly switch between the Slice and Slice Select tools, press and hold the Ctrl key.

When resizing fragments, make sure that the areas do not intersect with each other and that there are no gaps between them. Although Photoshop itself notes such shortcomings and takes measures to eliminate them, it is better to keep everything under your control.

After preliminary analysis and application of the Slice tool, we get 13 fragments (Fig. 2.16). Blue color shows fragments created by the Slice tool, gray color shows automatically created fragments.

Rice. 2.16. Image cut into fragments

There is no need to be afraid that there will be a lot of drawings due to the fact that most of the fragments contain an empty image. Thus, the number of pictures is reduced, since some of them can be thrown out by setting the size of the picture for the table cell.

Using a table to glue fragments together

After the fragments are designated, you need to save all the images to disk. To do this, select the menu item File > Save for Web & Devices...(File > Save for Web, Alt +Shift +Ctrl +S) to open the graphics optimization panel (Fig. 2.17).

Rice. 2.17. Image optimization panel

Using the Slice Select tool, you can select the desired fragment and set personal parameters for it, such as the number of colors, the value of quality loss, transparency, etc. You can select several fragments at once by holding down the Shift key, which allows you to set the same parameters for them.

When you finish working with fragments, click the “Save” button, indicate the location on the disk where the HTML document will be saved, its name, type and settings (Fig. 2.18).

Rice. 2.18. Settings when saving a file

The pictures are saved automatically in the images folder, and their name is formed from the name of the HTML file with the addition of the fragment number. For example, the saved name would be splash.html, then the first fragment would be called splash_01.png and the last would be splash_13.png. In addition, a spacer.gif file is created, which is a transparent image 1x1 pixel in size. It is used to correctly form images in the table.

The settings by which the HTML code is built and image names are generated can be changed if, when saving a file, in the Settings section, select Other... In the settings window, you can select the folder where to save the pictures, the method of forming file names, as well as the HTML code ( Fig. 2.19).

Rice. 2.19. Panel for selecting output settings

The HTML code obtained as a result of saving the files, after minor editing, is presented in example 2.12.

Example 2.12. Table for merging images

Cutting images copy

This code still needs some work because it is necessary for the horizontal gray and white stripe to occupy the entire available width of the web page. In addition, some fragments are repeated and can be eliminated.

To get the required result, let's introduce a layer with a background image and overlay our table on top of it. This image is shown in Fig. 2.20.

Rice. 2.20. Background image, frame around is for clarity

Now we create the desired layer, call it toplayer , and specify its parameters in the styles (example 2.13).

Example 2.13. Layer for forming stripes

XHTML 1.0 CSS 2.1 IE Cr Op Sa Fx

Northland tales

...

In this example, the layer background parameters are set through the universal background property, which determines the path to the graphic file, fill color, and pattern repeatability. Although the background color in such cases can be omitted since there is a background image, it is better to do so in case the user has disabled loading images. The layer height is also not a required parameter due to the fact that the table inside the layer has a given height.

All that remains is to remove pictures with insignificant fragments, maintaining their sizes at the table cells (example 2.14).

Example 2.14. Final code

XHTML 1.0 CSS 2.1 IE Cr Op Sa Fx

Northland tales

In this example, the doctype has been changed to strict, which results in a small indentation at the bottom of the images. To remove it, display : block is added to the IMG selector in the style.

In addition to the listed advantages, cutting images also has disadvantages, which are evident in the example given. The code turns out to be quite complex, and since the cells are interconnected, changing the size of one picture will entail modification of the entire table. In addition, it is inconvenient to edit the position of individual drawings. Again, to move one drawing a couple of pixels to the right, you will have to make edits in several cells at once. Therefore, cutting is not always worth using, especially if there is an alternative in the form of using layers. Let's look at how to make a similar layout using them.

To control the position of pictures, we will set relative positioning to the parent menu class, and absolute positioning to pictures. Then applying the left and top properties will set the coordinates of the image relative to its parent, i.e. layer menu . The layer itself can be easily moved and this will not affect its child elements in any way (example 2.15).

Example 2.15. Layout using layers

XHTML 1.0 CSS 2.1 IE Cr Op Sa Fx

Northland tales

Here the toplayer layer uses a background image to create a stripe of a given height across the entire width of the window. The menu layer defines a parent element that is centered on top of the background bar. The position of images within the menu layer is controlled by the top and left properties. Due to the active use of styles, the HTML code is greatly reduced, the pictures are independent of each other, they can be easily moved, changed to others, and new ones added. This is exactly the case when a table doesn’t stand a chance during layout.

Hello everyone, my name is Anna Blok and today we will talk about how to crop images without using graphics programs.

Where can this be useful?

First of all, on sites where content with images most likely will not be cropped for any specific block.

A striking example: blog on WordPress.

Let's say you want the cover of your article to have a 1:1 (square) aspect ratio. Your actions:

  1. Download a suitable image from the Internet;
  2. Crop it in Photoshop to the desired proportions;
  3. Publish an article.

When you visit the site, you will see the result you expected.

But, suppose you forgot to crop the image in Photoshop and downloaded a random image as a cover from the Internet, what will happen then?! That's right, the layout will break. And if you haven’t used CSS at all, then an HD image can completely block the entire view of the text. Therefore, it is important to know how to crop images using CSS styles.

Let's look at different situations of how this can be implemented not only using CSS, but also SVG.

Example 1

Let's try to crop the image that is placed using background-image. Let's create a little HTML markup

Let's move on to CSS styling. Using background-image we add an image, specify the frames for our image, center the image using background-position and set the background-size:

jpg); background-position:center center; background-size:cover; width:300px; height:300px; )

This was the first and simplest method for cropping an image. Now let's look at the second example.

Example 2

Suppose we still have the same box container inside which there is an img tag with an image that we will now style.

We will also center our image relative to the object we will create. And we use a property that is quite rarely used: object-fit .

Box ( position: relative; overflow:hidden; width:300px; height:300px; ) .box img ( position: absolute; top:50%; left:50%; transform:translate(-50%,-50%); width:300px; height:300px; object-fit:cover)

In my opinion this is the best method. It is ideal for blogs if you use images of completely different proportions for posts.

You can learn more about HTML and CSS here:

Example 3

We can also create cropping for images at the moment if we insert them into SVG elements. Let's take a circle as an example. We can create SVG using tags. Create a svg border tag that will contain a circle tag and a pattern tag inside. In the pattern tag we write the image tag. In it we specify the xlink:href attribute and add an image. We will also add width and height attributes. But that's not all. We will need to add the fill value. For our work to be considered complete, we will add the preserveAspectRatio auxiliary attribute to the image tag, which will allow us to fill our image “from start to finish” around the entire circle.

I cannot call this method universal. But it can be used in exceptional cases. For example, if we touched on the topic of a blog, then this method could ideally fit into the avatar of the author who writes the article.

You can learn more about HTML and CSS here:

Results:
We looked at 3 methods of cropping images on websites: using background-image, using the img tag and associated with the svg pattern with embedding bitmap images using the image tag. If you know any other methods for cropping an image using SVG, then share them in the comments. It will be useful not only for me, but also for others to know about them.

Don’t forget to ask your questions about layout or front-end development from professionals at FrontendHelp online.

Learn how to resize and crop images with using JavaScript and an HTML5 Canvas element, using controls you might see in photo editing apps:

In this article, I'll show you how to resize and crop images using the HTML5 element , and since we're already doing that, let's also create those cool image resizing controls you've often seen in photo editing apps.

In a live environment, a website or app might use this technique to resize and create a profile photo before loading.

We could do this on the server as well, however this would require potentially file transfers large sizes, which will take a lot of time. Instead, we can resize the image on the client side before it's even loaded. Which will allow you to do everything much faster.

To do this we will create an HTML5 element and output the image to the canvas at a specific size, and then retrieve the new image data from the canvas as URI data.

Most browsers already support these methods, so you'll likely be able to implement this technique right away. However, you need to be aware of some limitations that are not supported by browsers, such as image quality and performance.

Resizing very large images can cause the browser to slow down, or in some cases even crash the application. Therefore, it makes sense to set reasonable limits on the file size that can be uploaded. If quality is important to you, you may find this technique impractical because the browser will reduce the quality of the image during processing.

Exists a number of techniques that can be used to increase the quality of images scaled from canvas, however, we will not consider them in this article.

You can see the final result in this demo, or you can download the ZIP archive.

Well, now let's get started!

Marking

In our demo, we'll start with an existing image:

That's all! This is all the HTML we will need for this demo.

CSS

The CSS is also very minimal. First, let's define styles for the resizing container and the image:

Resize-container ( position: relative; display: inline-block; cursor: move; margin: 0 auto; ) .resize-container img ( display: block ) .resize-container:hover img, .resize-container:active img ( outline: 2px dashed rgba(222,60,80,.9 )

Resize-handle-ne, .resize-handle-ne, .resize-handle-se, .resize-handle-nw, .resize-handle-sw ( position: absolute; display: block; width: 10px; height: 10px; background: rgba(222,60,80,.9); z-index: 999; .resize-handle-nw ( top: -5px; left: -5px; cursor: nw-resize; ) .resize-handle- sw ( bottom: -5px; left: -5px; cursor: sw-resize; ) .resize-handle-ne ( top: -5px; right: -5px; cursor: ne-resize; ) .resize-handle-se ( bottom: -5px; right: -5px; cursor: se-resize)

JavaScript

JavaScript we start by defining some variables and initializing the Canvas and the target image:

var resizeableImage = function(image_target) ( var $container, orig_src = new Image(), image_target = $(image_target).get(0), event_state = (), constrain = false, min_width = 60, min_height = 60, max_width = 800, max_height = 900, resize_canvas = document.createElement("canvas" )); resizeableImage($(".resize-image"));

Next, we create an init function that will be called immediately upon boot. This function wraps the image in a container, creates resizing handles, and creates a copy of the original image that is used for resizing.

We also assign the JQuery object for the container element to a variable so we can access it later and add a mousedown event listener that detects when someone starts dragging one of the handles:

var resizeableImage = function(image_target) ( // ... init = function())( // Create a new image with a copy of the original src // When we resize the image, we always use this copy as a basis orig_src.src=image_target.src ; // Add resizing markers $(image_target).wrap("

").before(" ").before(" ").after(" ").after(" "); // Get variables for the container $container = $(image_target).parent(".resize-container"); // Add events $container.on("mousedown", ".resize-handle", startResize); ); //... init();

The startResize and endResize functions only tell the browser to start tracking where the mouse moves and to stop tracking:

startResize = function(e)( e.preventDefault(); e.stopPropagation(); saveEventState(e); $(document).on("mousemove", resizing); $(document).on("mouseup", endResize ); endResize = function(e)( e.preventDefault(); $(document).off("mouseup touchend", endResize); $(document).off("mousemove touchmove", resizing); );

Before we start tracking the mouse position, we need to take a snapshot of the dimensions and other key data of the container.

We store them in a variable called event_state and use it later as a starting point when changing the height and width:

saveEventState = function(e)( // Save the initial parameters of the event and the state of the container event_state.container_width = $container.width(); event_state.container_height = $container.height(); event_state.container_left = $container.offset().left ; event_state.container_top = $container.offset().top; event_state.mouse_x = (e.clientX || e.pageX || e.originalEvent.touches.clientX) + $(window).scrollLeft(); = (e.clientY || e.pageY || e.originalEvent.touches.clientY) + $(window).scrollTop(); // This is a patch for mobile safari // For some reason, it cannot directly copy touch properties if (typeof e.originalEvent.touches !== "undefined")( event_state.touches = ; $.each(e.originalEvent.touches, function(i, ob)( event_state.touches[i] = (); event_state.touches [i].clientX = 0+ob.clientX; event_state.touches[i].clientY = 0+ob.clientY ));

The resizing function is where most of the action happens. This function is constantly called when the user drags one of the sizing handles. Each time this function is called, we create a new width and height by calculating the relationship between the current mouse position and starting position the angle the user pulled:

resizing = function(e)( var mouse=(),width,height,left,top,offset=$container.offset(); mouse.x = (e.clientX || e.pageX || e.originalEvent.touches .clientX) + $(window).scrollLeft(); mouse.y = (e.clientY || e.pageY || e.originalEvent.touches.clientY) + $(window).scrollTop(); width = mouse. x - event_state.container_left; height = mouse.y - event_state.container_left; top = event_state.container_top; if(constrain || e.shiftKey)( height = width / orig_src.width * orig_src.height; ) if(width > min_width && height > min_height && width< max_width && height < max_height){ resizeImage(width, height); // Без этого Firefox не будем пересчитывать размеры изображения, пока перетаскивание не завершилось $container.offset({"left": left, "top": top}); } }

We then add an option to limit the size of the image when switching using the Shift key or a variable.

Finally, we resize the image, but only if the new width and height are within the minimum and maximum values ​​of the variables we set at the beginning.

Note: Since we're actually resizing the image, not just the height and width attributes, you might want to consider limiting how often resizeImage can be called for performance reasons. This is called debouncing or throttling.

Actual image resizing

You can simply draw an image using drawImage. We first set the height and width of the canvas and always use the original copy of the full-size image. We then use toDataURL on the canvas to get a Base64 encoded version of the recently modified image and place it on the page.

The section on clipping provides a complete explanation for all the options that can be used with the drawImage method:

resizeImage = function(width, height)( resize_canvas.width = width; resize_canvas.height = height; resize_canvas.getContext("2d").drawImage(orig_src, 0, 0, width, height); $(image_target).attr( "src", resize_canvas.toDataURL("image/png") );

Too easy? There is one small caveat: the image must be hosted on the same domain as the page, or on a server with ability to exchange with different source sources (CORS). Otherwise you may have problems with errors " damaged canvas«.

Resizing from different angles

You should now have a working demo version. But that is not all. On this moment, it doesn't yet work so that resizing happens as if we're dragging the bottom right corner, no matter what corner we're resizing the image from.

We need to be able to resize the image from any angle. To do this we need to understand the behavior of our demo model.

When resizing, the corner we're dragging on, as well as its adjacent sides, should move, while the opposite corner and its adjacent sides should remain in place:

When we change the width and height of an image, the right and bottom edges will move, while the top and left edges will remain in place. This means that by default, images are resized from the bottom right corner.

We can't change this default behavior, but when resizing from any corner other than the bottom right we can change general position image so that it appears as if the opposite corner and adjacent edges remain in place. Let's update our resizing function:

resizing = function(e)( var mouse=(),width,height,left,top,offset=$container.offset(); mouse.x = (e.clientX || e.pageX || e.originalEvent.touches .clientX) + $(window).scrollLeft(); mouse.y = (e.clientY || e.pageY || e.originalEvent.touches.clientY) + $(window).scrollTop(); // Image position depends differently on the angle we drag if($(event_state.evnt.target).hasClass("resize-handle-se"))( width = mouse.x - event_state.container_left; height = mouse.y - event_state .container_top; left = event_state.container_left; top = event_state.container_top; ) else if($(event_state.evnt.target).hasClass("resize-handle-sw"))( width = event_state.container_width - (mouse.x - event_state.container_left); height = mouse.y - event_state.container_top; left = mouse.x; top = event_state.container_top; else if($(event_state.evnt.target).hasClass("resize-handle-nw" ))( width = event_state.container_width - (mouse.x - event_state.container_left); height = event_state.container_height - (mouse.y - event_state.container_top); left = mouse.x; top = mouse.y; if(constrain || e.shiftKey)( top = mouse.y - ((width / orig_src.width * orig_src.height) - height); ) ) else if($(event_state.evnt.target).hasClass("resize -handle-ne"))( width = mouse.x - event_state.container_left; height = event_state.container_height - (mouse.y - event_state.container_top); left = event_state.container_left; top = mouse.y; if(constrain | | e.shiftKey)( top = mouse.y - ((width / orig_src.width * orig_src.height) - height); ) ) // Optionally support aspect ratio if(constrain || e.shiftKey)( height = width / orig_src.width * orig_src.height ) if(width > min_width && height > min_height && width< max_width && height < max_height){ // Для увеличения производительности вы можете ограничить количество вызовов resizeImage() resizeImage(width, height); // Без этого Firefox не будет пересчитывать размеры изображения, пока перетаскивание не завершилось $container.offset({"left": left, "top": top}); } }

Our code now checks which resize-handle is being dragged and moves our image so that the corresponding corner appears to remain stationary.

Moving an Image

Now that we can resize an image by dragging any of its corners, you may have noticed that we can inadvertently change the position of the image on the page.

Now we need to ensure that the image returns back to the center of the frame. In the init function, let's add another event tracker, similar to the one we created earlier:

init = function())( //... $container.on("mousedown", "img", startMoving); )

Now we add the startMoving and endMoving functions, similar to how we added startResize and endResize:

startMoving = function(e)( e.preventDefault(); e.stopPropagation(); saveEventState(e); $(document).on("mousemove", moving); $(document).on("mouseup", endMoving ); endMoving = function(e)( e.preventDefault(); $(document).off("mouseup", endMoving); $(document).off("mousemove", moving); );

In the moving function you need to calculate new position for the top left edge of the container. It will be equal to the current mouse position, offset by the distance from the upper left corner to the mouse when we started dragging the image:

moving = function(e)( var mouse=(); e.preventDefault(); e.stopPropagation(); mouse.x = (e.clientX || e.pageX) + $(window).scrollLeft(); mouse .y = (e.clientY || e.pageY) + $(window).scrollTop(); $container.offset(( "left": mouse.x - (event_state.mouse_x - event_state.container_left), "top" : mouse.y - (event_state.mouse_y - event_state.container_top) ));

Cropping an image

Now that we can resize the image, you might want to crop it. Instead of giving users the option to crop the image to any size and shape, let's create a frame that has the exact dimensions we need and invite users to place the image inside that frame.

They will be able to determine the scale and position of the cropped frame in the original image, and we, in turn, will be sure that the final image will always have the same shape and size.

To do this we need to add the following HTML code:

The styles for the overlay frame are very important, especially the position, width and height, as they are used to determine how much of the image will be cropped.

It is also important to remember that the frame must always be visible against a background of any color. That's why in my example I used a semi-transparent white outline around the main window:

Overlay ( position: absolute; left: 50%; top: 50%; margin-left: -100px; margin-top: -100px; z-index: 999; width: 200px; height: 200px; border: solid 2px rgba( 222,60,80,.9); box-sizing: content-box; pointer-events: none; .overlay:after, .overlay:before (content: ""; position: absolute; display: block; width: 204px; height: 40px; border-left: dashed 2px rgba(222,60,80,.9); border-right: dashed 2px rgba(222,60,80,.9); 0; margin-left: -2px; margin-top: -40px ) .overlay:after ( bottom: 0; margin-left: -2px; margin-bottom: -40px; ) .overlay-inner:after, .overlay -inner:before ( content: ""; position: absolute; display: block; width: 40px; height: 204px; border-top: dashed 2px rgba(222,60,80,.9); border-bottom: dashed 2px rgba(222,60,80,.9); .overlay-inner:before ( left: 0; margin-left: -40px; margin-top: -2px; ) .overlay-inner:after ( right: 0; margin-right: -40px; margin-top: -2px) .btn-crop ( position: absolute; vertical-align: bottom; right: 5px; bottom: 5px; padding: 6px 10px; z-index: 999; background-color: rgb(222,60,80); border: none; border-radius: 5px; color: #FFF; )

Add the following function and event tracker to your JavaScript:

init = function())( //... $(".js-crop").on("click", crop); ); crop = function())( var crop_canvas, left = $(".overlay").offset().left - $container.offset().left, top = $(".overlay").offset().top - $container.offset().top, width = $(".overlay").width(), height = $(".overlay").height(); crop_canvas = document.createElement("canvas"); width = width; crop_canvas.height = height; crop_canvas.getContext("2d").drawImage(image_target, left, top, width, height, 0, 0, width, height); window.open(crop_canvas.toDataURL("image /png")); )

The crop function is similar to the resizeImage function, but instead of passing it a height and width value, we get the height and width from the overlay element.

The canvas's drawImage method requires nine parameters for cropping. The first parameter is the image source. The next four parameters indicate how much of the original image is used (the clipping box). The last four parameters indicate where on the canvas to display the image and in what size.

Adding Touch Events

For mousedown and mouseup there are equivalent touch events - touchstart and touchend, for mousemov there is an equivalent touchmove event. Whoever named these events clearly lacked a sense of humor, otherwise he might well have called them “touchdown” and “touchup”.

Let's add touchstart and touchend wherever we have mousedown and mouseup , and replace mousemove with touchmove :

// In init()... $container.on("mousedown touchstart", ".resize-handle", startResize); $container.on("mousedown touchstart", "img", startMoving); //In startResize() ... $(document).on("mousemove touchmove", moving); $(document).on("mouseup touchend", endMoving); //In endResize()... $(document).off("mouseup touchend", endMoving); $(document).off("mousemove touchmove", moving); //In startMoving()... $(document).on("mousemove touchmove", moving); $(document).on("mouseup touchend", endMoving); //In endMoving()... $(document).off("mouseup touchend", endMoving); $(document).off("mousemove touchmove", moving);

Since we're resizing the image, it's fair to expect that some users will want to use common actions such as stretching the image. There is a Hammer library that contains many convenient tools for working with touch events.

But since we only need stretching, we can do without it. Now I'll show you how easy it is to create a stretch without using a third-party library.

You may have noticed that the saveEventState function already stores the original touch data; we will need this now.

First, we check if the event contains two "touches" and measure the distance between them. We mark this as the initial distance, and then measure how much this distance changes as we move. Let's update the moving function:

moving = function(e)( var mouse=(), touches; e.preventDefault(); e.stopPropagation(); touches = e.originalEvent.touches; mouse.x = (e.clientX || e.pageX || touches.clientX) + $(window).scrollLeft(); mouse.y = (e.clientY || e.pageY || touches.clientY) + $(window).scrollTop(); $container.offset(( " left": mouse.x - (event_state.mouse_x - event_state.container_left), "top": mouse.y - (event_state.mouse_y - event_state.container_top) )); // Track stretching while moving if(event_state.touches && event_state.touches.length > 1 && touches.length > 1)( var width = event_state.container_width, height = event_state.container_height; var a = event_state.touches.clientX - event_state.touches.clientX; a = a * a; var b = event_state.touches.clientY - event_state.touches.clientY; var dist1 = Math.sqrt(a + b); a = e.originalEvent.touches.clientX - touches.clientX; a; b = e.originalEvent.touches.clientY - touches.clientY; b = b * b; var dist2 = Math.sqrt(a + b); var ratio = dist2 /dist1; width = width * ratio; height = height * ratio; // To improve performance, you can limit the number of calls to resizeImage() resizeImage(width, height); ) );

We divide the current distance by the initial distance to determine the ratio and apply it accordingly to scale the image. We calculate the new width and height and then resize the image:

That's all. Watch the demo version again or download the ZIP archive.

In testing, I saw that Chrome blocks the default browser from responding to stretching, but Firefox works fine.

I hope you found this article useful. I encourage you to read other articles on draggable elements and file upload techniques, and see how other people combine these techniques to create beautiful user interfaces.

This publication is a translation of the article “ RESIZING AND CROPPING IMAGES WITH CANVAS", prepared by the friendly project team

In this article, we'll learn how to resize and crop images using an element in HTML5, and while we're at it, let's give the controls a stylish design just like in photo editors.

Nowadays, many websites and web applications are equipped with image processing technology. This can be done on the server side, which will incur time costs for transporting a potentially large image. To avoid this, you can process images on the client machine to speed up the process.

We'll do this through the canvas, drawing the images at the desired size, then fetching new images. Many browsers support this method, so we can start right away with only minor performance limitations.

Formatting large images can slow down the browser or cause it to stop altogether. This makes us think about setting limits on uploaded images. If the quality of the resulting images is important, then we might be surprised by what the browser turns them into. You can find several technologies on the Internet to improve the quality of image processing, but we will not consider them here.

To this end, we are starting work!

Marking

In our demo we will work with one given image:

All! We don't need any other HTML.

CSS

The CSS code won't be very big either. Let's define styles for resize-container and the image itself.

Resize-container ( position: relative; display: inline-block; cursor: move; margin: 0 auto; ) .resize-container img ( display: block ) .resize-container:hover img, .resize-container:active img ( outline: 2px dashed rgba(222,60,80,.9 )

Now let’s set the positions and styles for each ‘resize handles’. These are small squares located in the corners of the images that we drag to resize the image.

Resize-handle-ne, .resize-handle-ne, .resize-handle-se, .resize-handle-nw, .resize-handle-sw ( position: absolute; display: block; width: 10px; height: 10px; background: rgba(222,60,80,.9); z-index: 999; .resize-handle-nw ( top: -5px; left: -5px; cursor: nw-resize; ) .resize-handle- sw ( bottom: -5px; left: -5px; cursor: sw-resize; ) .resize-handle-ne ( top: -5px; right: -5px; cursor: ne-resize; ) .resize-handle-se ( bottom: -5px; right: -5px; cursor: se-resize)

JavaScript

Let's start by creating a variable and a canvas in Canvas.

Var resizeableImage = function(image_target) ( var $container, orig_src = new Image(), image_target = $(image_target).get(0), event_state = (), constrain = false, min_width = 60, min_height = 60, max_width = 800, max_height = 900, resize_canvas = document.createElement("canvas" )); resizeableImage($(".resize-image"));

Now let's create a trigger function that will run immediately. This function works with the container that the image is inside, sets the size and copies the original image for cropping. We also assign a jQuery object so that we can reference it later and use mouse move operators to respond to mouse drags.

Var resizeableImage = function(image_target) ( // ... init = function())( // Create a new image with a copy of the original src // When resizing, we will always use this original copy as the base orig_src.src =image_target.src; // Add resize handles $(image_target).wrap("

").before(" ").before(" ").after(" ").after(" "); // Get a variable for the container $container = $(image_target).parent(".resize-container"); // Add events $container.on("mousedown", ".resize-handle", startResize ); //... init();

The startResize and endResize functions tell the browser when to start paying attention to mouse movement and when to stop.

StartResize = function(e)( e.preventDefault(); e.stopPropagation(); saveEventState(e); $(document).on("mousemove", resizing); $(document).on("mouseup", endResize ); endResize = function(e)( e.preventDefault(); $(document).off("mouseup touchend", endResize); $(document).off("mousemove touchmove", resizing); );

Before you begin mouse tracking, you must scan the current client settings when requesting a page. we store them in the event_state variable and use them later in our work.

SaveEventState = function(e)( // Save the initial event details and container state event_state.container_width = $container.width(); event_state.container_height = $container.height(); event_state.container_left = $container.offset(). left; event_state.container_top = $container.offset().top; event_state.mouse_x = (e.clientX || e.pageX || e.originalEvent.touches.clientX) + $(window).scrollLeft(); mouse_y = (e.clientY || e.pageY || e.originalEvent.touches.clientY) + $(window).scrollTop(); // This is a fix for mobile safari // For some reason it does not allow a direct copy of the touches property if(typeof e.originalEvent.touches !== "undefined")( event_state.touches = ; $.each(e.originalEvent.touches, function(i, ob)( event_state.touches[i] = (); event_state.touches[i].clientX = 0+ob.clientX; event_state.touches[i].clientY = 0+ob.clientY ));

The resizing function is the most important one. It is activated when the image is stretched. Each time we calculate new image sizes depending on the new position of the squares.

Resizing = function(e)( var mouse=(),width,height,left,top,offset=$container.offset(); mouse.x = (e.clientX || e.pageX || e.originalEvent.touches .clientX) + $(window).scrollLeft(); mouse.y = (e.clientY || e.pageY || e.originalEvent.touches.clientY) + $(window).scrollTop(); width = mouse. x - event_state.container_left; height = mouse.y - event_state.container_left; top = event_state.container_top; if(constrain || e.shiftKey)( height = width / orig_src.width * orig_src.height; ) if(width > min_width && height > min_height && width< max_width && height < max_height){ resizeImage(width, height); // Without this Firefox will not re-calculate the the image dimensions until drag end $container.offset({"left": left, "top": top}); } }

Next, we'll add an option to limit the size of the image using the Shift key or a variable.

note: Since we're actually resizing the image rather than just giving it a new length and height, it's worth thinking about how much we can use resizeImage to control server performance.

New image sizes

Drawing images in Canvas is as easy as drawImage . We set the height and length of the image, and then provide the original. We also use toDataURL to get a Base64-encoded version of the operation result.

Full explanations of the options available for this operation are provided.

ResizeImage = function(width, height)( resize_canvas.width = width; resize_canvas.height = height; resize_canvas.getContext("2d").drawImage(orig_src, 0, 0, width, height); $(image_target).attr( "src", resize_canvas.toDataURL("image/png") );

Too easy? There is one caveat: the image must be hosted on the same domain as our page, or the . If this is not the case, then you will have problems with 'tainted canvas'.

Increasing through other vertices

You should now have a working demo. But it's not finished yet. At the moment we can only stretch the image through one corner, but we want to use all four. To do this, you need to understand how it works.

When we stretch the image, the corners adjacent to the held corner must also move, but the opposite end of the image must be fixed.

We can change the code so that when the image is stretched at any angle, it changes. Let's update the resizing function:

Resizing = function(e)( var mouse=(),width,height,left,top,offset=$container.offset(); mouse.x = (e.clientX || e.pageX || e.originalEvent.touches .clientX) + $(window).scrollLeft(); mouse.y = (e.clientY || e.pageY || e.originalEvent.touches.clientY) + $(window).scrollTop(); // Position image differently depending on the corner dragged and constraints if($(event_state.evnt.target).hasClass("resize-handle-se"))( width = mouse.x - event_state.container_left; height = mouse.y - event_state.container_top ; left = event_state.container_left; top = event_state.container_top; ) else if($(event_state.evnt.target).hasClass("resize-handle-sw"))( width = event_state.container_width - (mouse.x - event_state .container_left); height = mouse.y - event_state.container_top; left = mouse.x; top = event_state.container_top; else if($(event_state.evnt.target).hasClass("resize-handle-nw")) ( width = event_state.container_width - (mouse.x - event_state.container_left); height = event_state.container_height - (mouse.y - event_state.container_top); left = mouse.x; top = mouse.y; if(constrain || e.shiftKey)( top = mouse.y - ((width / orig_src.width * orig_src.height) - height); ) ) else if($(event_state.evnt.target).hasClass("resize -handle-ne"))( width = mouse.x - event_state.container_left; height = event_state.container_height - (mouse.y - event_state.container_top); left = event_state.container_left; top = mouse.y; if(constrain | | e.shiftKey)( top = mouse.y - ((width / orig_src.width * orig_src.height) - height); ) ) // Optionally maintain aspect ratio if(constrain || e.shiftKey)( height = width / orig_src.width * orig_src.height ) if(width > min_width && height > min_height && width< max_width && height < max_height){ // To improve performance you might limit how often resizeImage() is called resizeImage(width, height); // Without this Firefox will not re-calculate the the image dimensions until drag end $container.offset({"left": left, "top": top}); } }

Now we check which resize-handle was used and apply the necessary changes.

Moving an Image

Now that we can change the size of the image, you may have noticed that it sometimes “moves out”. It is necessary to add the ability to move an object to the center of the frame. Let's expand our initialization function a little.

Init = function())( //... $container.on("mousedown", "img", startMoving); )

Now we add functions startMoving and endMoving, similar to startResize and endResize.

StartMoving = function(e)( e.preventDefault(); e.stopPropagation(); saveEventState(e); $(document).on("mousemove", moving); $(document).on("mouseup", endMoving ); endMoving = function(e)( e.preventDefault(); $(document).off("mouseup", endMoving); $(document).off("mousemove", moving); );

In the moving function we must work out the position of the upper left square. It should be equal to the initial one with a small offset calculated from the movement of other squares.

Moving = function(e)( var mouse=(); e.preventDefault(); e.stopPropagation(); mouse.x = (e.clientX || e.pageX) + $(window).scrollLeft(); mouse .y = (e.clientY || e.pageY) + $(window).scrollTop(); $container.offset(( "left": mouse.x - (event_state.mouse_x - event_state.container_left), "top" : mouse.y - (event_state.mouse_y - event_state.container_top) ));

Cropping an image

Now that we have learned how to resize, we need to add image cropping. Instead of letting users struggle with crop sizes, let's just create a frame that needs to be placed in the right place and everything will be cropped around it. This will give them the opportunity to adjust the image however they want, while at the same time making the resulting images uniform in size.

To do this you need to add the following HTML code:

It is important to remember that the frame must always be a different color from the background of the page, otherwise problems may arise.

Overlay ( position: absolute; left: 50%; top: 50%; margin-left: -100px; margin-top: -100px; z-index: 999; width: 200px; height: 200px; border: solid 2px rgba( 222,60,80,.9); box-sizing: content-box; pointer-events: none; .overlay:after, .overlay:before (content: ""; position: absolute; display: block; width: 204px; height: 40px; border-left: dashed 2px rgba(222,60,80,.9); border-right: dashed 2px rgba(222,60,80,.9); 0; margin-left: -2px; margin-top: -40px ) .overlay:after ( bottom: 0; margin-left: -2px; margin-bottom: -40px; ) .overlay-inner:after, .overlay -inner:before ( content: ""; position: absolute; display: block; width: 40px; height: 204px; border-top: dashed 2px rgba(222,60,80,.9); border-bottom: dashed 2px rgba(222,60,80,.9); .overlay-inner:before ( left: 0; margin-left: -40px; margin-top: -2px; ) .overlay-inner:after ( right: 0; margin-right: -40px; margin-top: -2px) .btn-crop ( position: absolute; vertical-align: bottom; right: 5px; bottom: 5px; padding: 6px 10px; z-index: 999; background-color: rgb(222,60,80); border: none; border-radius: 5px; color: #FFF; )

Let's also update the JavaScript code:

Init = function())( //... $(".js-crop").on("click", crop); ); crop = function())( var crop_canvas, left = $(".overlay").offset().left - $container.offset().left, top = $(".overlay").offset().top - $container.offset().top, width = $(".overlay").width(), height = $(".overlay").height(); crop_canvas = document.createElement("canvas"); width = width; crop_canvas.height = height; crop_canvas.getContext("2d").drawImage(image_target, left, top, width, height, 0, 0, width, height); window.open(crop_canvas.toDataURL("image /png")); )

The crop function is similar to resizeImage. The only difference is that we get the dimensions and crop position from the position of the frame.

To crop, you need to set nine parameters to the drawImage operator in the canvas. The first one is the original image. The next four are the places used for the operation. Another four are the coordinates of the place where you should start drawing in the canvas, and what size the image will be.

Adding touch and gesture recognition

We have created mouse support. Let's not deprive ourselves of attention and mobile devices.

For mousedown and mouseup there are equivalent operators - touchstart and touchend , and for mousemove there is touchmove . You need to be careful not to confuse them with touchup and touchdown (otherwise it will be funny).

Let's add touchstart and touchend wherever we have mousedown , and mouseup along with touchmove wherever we have mousemove .

// In init()... $container.on("mousedown touchstart", ".resize-handle", startResize); $container.on("mousedown touchstart", "img", startMoving); //In startResize() ... $(document).on("mousemove touchmove", moving); $(document).on("mouseup touchend", endMoving); //In endResize()... $(document).off("mouseup touchend", endMoving); $(document).off("mousemove touchmove", moving); //In startMoving()... $(document).on("mousemove touchmove", moving); $(document).on("mouseup touchend", endMoving); //In endMoving()... $(document).off("mouseup touchend", endMoving); $(document).off("mousemove touchmove", moving);

Since we have connected mobile devices, there is a possibility that the user will use the gesture of “squeezing” the image with his fingers to make it smaller. There is one very convenient library called Hammer, which allows you to recognize many gestures. But, since we only need one, we’ll write it briefly without any additional scripts.

You may have noticed that the saveEventState function is already stored in the touch information. Now we will need it.

First we check for the presence of "two touches" and the distance between them. We also look at whether the distance between them decreases as we move. Now let's update moving:

Moving = function(e)( var mouse=(), touches; e.preventDefault(); e.stopPropagation(); touches = e.originalEvent.touches; mouse.x = (e.clientX || e.pageX || touches.clientX) + $(window).scrollLeft(); mouse.y = (e.clientY || e.pageY || touches.clientY) + $(window).scrollTop(); $container.offset(( " left": mouse.x - (event_state.mouse_x - event_state.container_left), "top": mouse.y - (event_state.mouse_y - event_state.container_top) )); // Watch for pinch zoom gesture while moving if(event_state. touches && event_state.touches.length > 1 && touches.length > 1)( var width = event_state.container_width, height = event_state.container_height; var a = event_state.touches.clientX - event_state.touches.clientX; a = a * a ; var b = event_state.touches.clientY - event_state.touches.clientY; var dist1 = Math.sqrt(a + b); a * a; b = e.originalEvent.touches.clientY - touches.clientY; b = b * b; var dist2 = Math.sqrt(a + b); var ratio = dist2 /dist1; width = width * ratio; height = height * ratio; // To improve performance you might limit how often resizeImage() is called resizeImage(width, height); ) );

Based on this data, we reduce or enlarge our image and adjust its height and length.

That's all. You can open