The WEB Front-end allows you to crop and upload images.

Source: Internet
Author: User

The WEB Front-end allows you to crop and upload images.

The final effect is as follows:

There are several functions, the first is to support drag and drop, the second is compression, the third is cropping and editing, and the fourth is the upload and upload progress display. The following describes the implementation of each function in sequence:

1. Drag and Drop to display images

The drag-and-drop reading function is mainly used to listen to html5 drag events. There is nothing to say about it. Check the api and you will know how to do it, the main reason is how to read the image dragged by the user and convert it to base64 for local display.

Var handler = {init: function ($ container) {// you need to disable the default dragover behavior. Otherwise, the page will jump $ container. on ("dragover", function (event) {event. preventDefault () ;}); $ container. on ("drop", function (event) {event. preventDefault (); // obtain the dragged image File, which is a file object var File = event. originalEvent. dataTransfer. files [0]; handler. handleDrop ($ (this), file) ;}}varhandler = {init: function ($ container) {// disable the default dragover behavior, otherwise, the page will jump $ container. on ("dragover", function (event) {event. preventDefault () ;}); $ container. on ("drop", function (event) {event. preventDefault (); // obtain the dragged image File, which is a File object varfile = event. originalEvent. dataTransfer. files [0]; handler. handleDrop ($ (this), file );});}}

Line 3 of the Code obtains the image file and passes it to line 11 for processing.

If input is used, the system listens to the change event of input:

$container.on("change", "input[type=file]", function(event){if(!this.value) return;var file = this.files[0];handler.handleDrop($(this).closest(".container"), file);this.value = "";});$container.on("change","input[type=file]",function(event){if(!this.value)return;varfile=this.files[0];handler.handleDrop($(this).closest(".container"),file);this.value="";});

Line 3 of the Code gets the File object and passes it to handleDrop for processing.

Next, read the file content in the handleDrop function and convert it to the base64 format:

handleDrop: function($container, file){var $img = $container.find("img");handler.readImgFile(file, $img, $container);},handleDrop:function($container,file){var$img= $container.find("img");handler.readImgFile(file,$img,$container);},

In my code, a readImgFile function is called. Many helper functions are called to disassemble large modules and reuse small modules.

Read the image file content in readImgFile:

Use FileReader To Read File JavaScript

ReadImgFile: function (file, $ img, $ container) {var reader = new FileReader (file); // check whether the user selects an image file if (file. type. split ("/") [0]! = "Image") {util. toast ("You shoshould choose an image file"); return;} reader. onload = function (event) {var base64 = event.tar get. result; handler. compressAndUpload ($ img, base64, file, $ container);} reader. readAsDataURL (file);} readImgFile: function (file, $ img, $ container) {varreader = newFileReader (file); // checks whether a user selects an image file if (file. type. split ("/") [0]! = "Image") {util. toast ("You shocould choose an image file"); return;} reader.onload?function(event=#varbase64#event.tar get. result; handler. compressAndUpload ($ img, base64, file, $ container);} reader. readAsDataURL (file );}

Here, we use FileReader to read the file content and call readAsDataURL. This api can convert the binary image content to the base64 format. After reading the file, the onload event is triggered, display and upload in onload:

// Obtain the base64 content of the image var base64 = event.tar get. result; // if the image is larger than 1 MB, set the body to translucent if (file. size> ONE_MB) {$ ("body" ).css ("opacity", 0.5);} // The picture is stuck during the Conference, and the entire page cannot be operated $ img. attr ("src", baseUrl); // restore if (file. size> ONE_MB) {$ ("body" ).css ("opacity", 1) ;}// then call a compression and upload function handler. compressAndUpload ($ img, file, $ container); // obtain the image base64content varbase641_event.tar get. result; // if the image is larger than 1 MB, set the body to translucent if (file. size> ONE_MB) {$ ("body" ).css ("opacity", 0.5);} // The picture is stuck during the Conference, and the entire page cannot be operated $ img. attr ("src", baseUrl); // restore if (file. size> ONE_MB) {$ ("body" ).css ("opacity", 1) ;}// then call a compression and upload function handler. compressAndUpload ($ img, file, $ container );

If the image contains several Mb of data, it will be stuck when the first line is displayed. I tried to solve the problem by using multiple threads in web worker. However, since multithreading does not have a window object, dom cannot be operated, therefore, this problem cannot be effectively solved. A compensation measure was taken: the page is being processed by changing the page to virtual, and the page cannot be operated. Wait a moment.

There is also a problem here, that is, the photos taken by the ios system. If they are not taken horizontally, the rotation angle of the displayed Photos may be incorrect. The following is a vertical picture, the read result is as follows:


That is, no matter how you take the image, the images actually saved in ios are placed horizontally, so you need to manually rotate them. The rotation angle is placed in the exif data structure. Read the Rotation Angle and read it from an EXIF database:

Read exif information

ReadImgFile: function (file, $ img, $ container) {EXIF. getData (file, function () {var orientation = this. exifdata. orientation, rotateDeg = 0; // if it is not an ios image or a horizontal image, you do not need to process it, directly read if (typeof orientation = "undefined" | orientation = 1) {// Add a rotateDeg parameter handler to the original readImgFile. doReadImgFile (file, $ img, $ container, rotateDeg);} // otherwise use a canvas to rotate else {rotateDeg = orientation = 6? 90 * Math. PI/180: orientation = 8? -90 * Math. PI/180: orientation = 3? 180 * Math. PI/180: 0; handler. doReadImgFile (file, $ img, $ container, rotateDeg) ;}} readImgFile: function (file, $ img, $ container) {EXIF. getData (file, function () {varorientation = this. exifdata. orientation, rotateDeg = 0; // if it is not an ios image or a horizontal image, you do not need to process it, directly read if (typeoforientation = "undefined" | orientation = 1) {// Add a rotateDeg parameter handler to the original readImgFile. doReadImgFile (file, $ img, $ container, rotateDeg);} // else use canvas to rotate else {RotateDeg = orientation === 6? 90 * Math. PI/180: orientation = 8? -90 * Math. PI/180: orientation = 3? 180 * Math. PI/180: 0; handler. doReadImgFile (file, $ img, $ container, rotateDeg );}});}

After knowing the angle, you can use canvas for processing. The following compressed image is described, because canvas is also used for compression.

2. compress the image

You can use canvas to compress images. The principle of canvas is to draw an image to a small canvas, and then export the content of the canvas to base64, you can get a small image:

// Set the maximum image compression width to 1500 pxvar maxWidth = 1500; var resultImg = handler. compress ($ img [0], maxWidth, file. type); // set the maximum image compression width to 1500 pxvarmaxWidth = 1500; varresultImg = handler. compress ($ img [0], maxWidth, file. type );

Compress functions are compressed. In this function, first create a canvas object and then calculate the size of the canvas:

Compress: function (img, maxWidth, mimeType) {// create a canvas object var cvs = document. createElement ('canvas '); var width = img. naturalWidth, height = img. naturalHeight, imgRatio = width/height; // if the image dimension exceeds the given maxWidth 1500, // to maintain the image width to height ratio, calculate the canvas size if (width> maxWidth) {width = maxWidth; height = width/imgRatio;} cvs. width = width; cvs. height = height;} compress: function (img, maxWidth, mimeType) {// create a canvas object varcvs = document. createElement ('canvas '); varwidth = img. naturalWidth, height = img. naturalHeight, imgRatio = width/height; // if the image dimension exceeds the given maxWidth 1500, // to maintain the image width to height ratio, calculate the canvas size if (width> maxWidth) {width = maxWidth; height = width/imgRatio;} cvs. width = width; cvs. height = height ;}

Next, draw a large image on a small canvas and export it again:

// Draw a large image to a small canvas var ctx = cvs. getContext ("2d "). drawImage (img, 0, 0, img. naturalWidth, img. naturalHeight, 0, 0, width, height); // compress the image quality by using var quality = width> = 1500? 0.5: width> 600? 0.6: 1; // export the image to base64var newImageData = cvs. toDataURL (mimeType, quality); var resultImg = new Image (); resultImg. src = newImageData; return resultImg; // draw a large image to a small canvas varctx = cvs. getContext ("2d "). drawImage (img, 0, 0, img. naturalWidth, img. naturalHeight, 1500, width, height); // the image quality is properly compressed varquality = width> =? 0.5: width> 600? 0.6: 1; // export the image as base64varnewImageData = cvs. toDataURL (mimeType, quality); varresultImg = newImage (); resultImg. src = newImageData; returnresultImg;

The last line returns a small compressed image, which can be cropped.

Before cropping, because ios photos need to be rotated at the second point, they can be processed together during compression. That is to say, if you want to rotate it, you can rotate it on the canvas:

var ctx = cvs.getContext("2d");var destX = 0,destY = 0;if(rotateDeg){ctx.translate(cvs.width / 2, cvs.height / 2);ctx.rotate(rotateDeg);destX = -width / 2,destY = -height / 2;}ctx.drawImage(img, 0, 0, img.naturalWidth, img.naturalHeight, destX, destY, width, height);varctx=cvs.getContext("2d");vardestX=0,destY=0;if(rotateDeg){ctx.translate(cvs.width/2,cvs.height/2);ctx.rotate(rotateDeg);destX=-width/2,destY=-height/2;}ctx.drawImage(img,0,0,img.naturalWidth,img.naturalHeight,destX,destY,width,height);

This solves the ios image rotation problem. After obtaining a rotated and compressed adjusted image, you can use it for cropping and editing.

3. Crop an image

Crop images and find a plug-in cropper online. This plug-in is quite powerful and supports cropping, rotating, and turning, but it does not really process images, it only records the changes made by the user, and then you can process them yourself. You can transmit the transformed data to the backend for processing. Here we are processing at the front end, because we do not need to be compatible with IE8.

As shown below, I rotated an image and flipped it over:

Its output is:

{height: 319.2000000000001,rotate: 45,scaleX: -1,scaleY: 1,width: 319.2000000000001x: 193.2462838120872y: 193.2462838120872}{height:319.2000000000001,rotate:45,scaleX:-1,scaleY:1,width:319.2000000000001x:193.2462838120872y:193.2462838120872}

Through this information, we can see that the image is flipped right and right, and the clockwise rotation is 45 degrees. We also know the position and size of the cropping box. With this complete information, you can perform one-to-one processing.

During the display, the plug-in uses the img tag and sets its css transform attribute for transformation. Actually, you still need to use canvas for processing. Here we will describe it in three steps:

1. If the user does not rotate or flip, but simply chooses to crop the lower area, it is much simpler. The simplest way is to create a canvas. The size of the canvas is the size of the Selection box, and then draw the corresponding position of the image to the canvas Based on the start point x, y, and width and height, export the image. Considering the need to flip, use the second method to create a canvas of the same size as the image, draw the image intact, and save the imageData of the selected area, reset the size of the canvas to the size of the selected box, then draw the imageData, and then export it:

Var cvs = document. createElement ('canvas '); var img = $ img [0]; var width = img. naturalWidth, height = img. naturalHeight; cvs. width = width; cvs. height = height; var ctx = cvs. getContext ("2d"); var destX = 0, destY = 0; ctx. drawImage (img, destX, destY); // Save the image content in the selected box var imageData = ctx. getImageData (cropOptions. x, cropOptions. y, cropOptions. width, cropOptions. height); cvs. width = cropOptions. width; cvs. height = cropOptions. height; // then draw ctx. putImageData (imageData, 0, 0); varcvs = document. createElement ('canvas '); varimg = $ img [0]; varwidth = img. naturalWidth, height = img. naturalHeight; cvs. width = width; cvs. height = height; varctx = cvs. getContext ("2d"); vardestX = 0, destY = 0; ctx. drawImage (img, destX, destY); // Save the image content in the selected box as varimageData = ctx. getImageData (cropOptions. x, cropOptions. y, cropOptions. width, cropOptions. height); cvs. width = cropOptions. width; cvs. height = cropOptions. height; // then draw ctx. putImageData (imageData, 0, 0 );

Line 14 of the Code: Save the image data of the selected area through the data provided by the plug-in, and draw the data in line 18.

2. If you flip the canvas, you can easily implement it by using the above structure. You only need to perform a flip change on the canvas before the drawImage of Row 3:

Canvas flip implements JavaScript

//fipif(cropOptions.scaleX === -1 || cropOptions.scaleY === -1){destX = cropOptions.scaleX === -1 ? width * -1 : 0; // Set x position to -100% if flip horizontaldestY = cropOptions.scaleY === -1 ? height * -1 : 0; // Set y position to -100% if flip verticalctx.scale(cropOptions.scaleX, cropOptions.scaleY);}

Ctx. drawImage (img, destX, destY );

//fipif(cropOptions.scaleX===-1||cropOptions.scaleY===-1){destX=cropOptions.scaleX===-1?width*-1:0; // Set x position to -100% if flip horizontaldestY=cropOptions.scaleY===-1?height*-1:0; // Set y position to -100% if flip verticalctx.scale(cropOptions.scaleX,cropOptions.scaleY);}

Ctx. drawImage (img, destX, destY );

You don't need to change anything else, so you can flip up, down, left, right, and right. The difficulty is that you need to flip and rotate

3. There is no way to overlay the two transformations directly by changing the coordinates of the canvas and drawImage at a time. There are two ways to do this. The first is to use imageData for mathematical transformation, calculate the new rgba value of each pixel from the first row to the last row, and then draw it; the second method is to create a second canvas, flip the first canvas, draw the result of the canvas to the second canvas, rotate it, and finally export it. Since the second method is relatively simple, we adopt the second method:

Same as above. After the first canvas is painted:

Rotate, flip, and JavaScript

Ctx. drawImage (img, destX, destY); // rotateif (cropOptions. rotate! = 0) {var newCanvas = document. createElement ("canvas"), deg = cropOptions. rotate/180 * Math. PI; // After rotation, the canvas becomes larger. Calculate newCanvas. width = Math. abs (width * Math. cos (deg) + Math. abs (height * Math. sin (deg); newCanvas. height = Math. abs (width * Math. sin (deg) + Math. abs (height * Math. cos (deg); var newContext = newCanvas. getContext ("2d"); newContext. save (); newContext. translate (newCanvas. width/2, newCanvas. Height/2); newContext. rotate (deg); destX =-width/2, destY =-height/2; // draw the content of the first canvas in the rotated coordinate system newContext. drawImage (cvs, destX, destY); newContext. restore (); ctx = newContext; cvs = newCanvas;} ctx. drawImage (img, destX, destY); // rotateif (cropOptions. rotate! = 0) {varnewCanvas = document. createElement ("canvas"), deg = cropOptions. rotate/180 * Math. PI; // After rotation, the canvas becomes larger. Calculate newCanvas. width = Math. abs (width * Math. cos (deg) + Math. abs (height * Math. sin (deg); newCanvas. height = Math. abs (width * Math. sin (deg) + Math. abs (height * Math. cos (deg); varnewContext = newCanvas. getContext ("2d"); newContext. save (); newContext. translate (newCanvas. width/2, newCanvas. height/2); newContext. rotate (deg); destX =-width/2, destY =-height/2; // draw the content of the first canvas in the rotated coordinate system newContext. drawImage (cvs, destX, destY); newContext. restore (); ctx = newContext; cvs = newCanvas ;}

Insert the code in step 2 into step 1, and then insert the code in step 3 into step 2, which is a complete process.

Next, we will introduce the next upload.

4. File Upload and upload progress

File Upload can only be submitted in the form of a form. The encoding method is multipart/form-data. In this article, I will discuss how to upload files without refreshing the new pages: iframe/FormData/FileReader has been discussed in detail. You can write a form label for submission, but you can also simulate the form submission format, which has been mentioned in that article.

First, create an ajax request:

JavaScript

var xhr = new XMLHttpRequest();xhr.open('POST', upload_url, true);var boundary = 'someboundary';xhr.setRequestHeader('Content-Type', 'multipart/form-data; boundary=' + boundary);varxhr=newXMLHttpRequest();xhr.open('POST',upload_url,true);varboundary='someboundary';xhr.setRequestHeader('Content-Type','multipart/form-data; boundary='+boundary);

Set the encoding method, and then upload the data in the Form format:

Upload JavaScript using ajax

var data = img.src;data = data.replace('data:' + file.type + ';base64,', '');xhr.sendAsBinary([//name=data'--' + boundary,'Content-Disposition: form-data; name="data"; filename="' + file.name + '"','Content-Type: ' + file.type, '',atob(data), '--' + boundary,//name=docName'--' + boundary,'Content-Disposition: form-data; name="docName"', '',file.name,'--' + boundary + '--'].join('\r\n'));vardata=img.src;data=data.replace('data:'+file.type+';base64,','');xhr.sendAsBinary([//name=data'--'+boundary,'Content-Disposition: form-data; name="data"; filename="'+file.name+'"','Content-Type: '+file.type,'',atob(data),'--'+boundary,//name=docName'--'+boundary,'Content-Disposition: form-data; name="docName"','',file.name,'--'+boundary+'--'].join('\r\n'));

Different Fields in form data are separated by a boundary random string. Use sendAsBinary to send events, including

1) Upload progress:

Upload progress JavaScript

xhr.upload.onprogress = function(event){if(event.lengthComputable) {duringCallback((event.loaded / event.total) * 100);}};xhr.upload.onprogress=function(event){if(event.lengthComputable){duringCallback((event.loaded/event.total)*100);}};

Here, the duringCallback callback function is used to pass the current progress parameter to the callback function. You can use this parameter to set the progress bar. You can implement the progress bar on your own, or find one on the Internet.

2) Success and Failure:

Success and Failure callback JavaScript

xhr.onreadystatechange = function() {if (this.readyState == 4){if (this.status == 200) {successCallback(this.responseText);}else if (this.status >= 400) {if (errorCallback && errorCallback instanceof Function) {errorCallback(this.responseText);} } }};xhr.onreadystatechange=function(){if(this.readyState==4){if(this.status==200){successCallback(this.responseText);}elseif(this.status>=400){if(errorCallback&& errorCallback instanceofFunction){errorCallback(this.responseText);} } }};

For this upload function, refer to a JIC plug-in.

So far, the entire function is completely disassembled. The above code can be compatible with IE10, and FileReader's api is only compatible with IE10. The problem should not be big, because Microsoft has abandoned browsers earlier than IE11, why should we be compatible.

This reduces the pressure on the backend, and does not need to interact with the backend. It is good for users, except that the above mentioned area will be stuck. The core code has been described above, and the complete code and demo will not be released.

The above section describes how to crop and upload images on the WEB front end. I hope it will be helpful to you. If you have any questions, please leave a message and I will reply to you in a timely manner. Thank you very much for your support for the help House website!

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.