Original address: github.com/whinc/blog/…
Recently I received a request to post comments: users enter comments and can take photos or select pictures from the album to upload, which means text and picture comments are supported. It needs to be implemented on both the H5 and mini-programs at the same time. This requirement requires many places to process images. This article makes a summary of the image processing practice on the H5 end. The project code is based on the Vue framework. In order to avoid being affected by the framework, I changed all the code to the implementation of the native API for explanation. At the same time, there are many other additional details and functions (preview, cropping, upload progress, etc.) in the project code here. Omit it and only introduce the key ideas and codes related to image processing. The implementation of the mini program is similar to H5 and will not be repeated. The implementation code of the mini program is attached at the end of the article.
PhotographUse the <input> tag, set type to file to select the file, set accept to image/* to select the file type and camera, and set multiple to support multiple selections. Listen to the change event to get the selected file list, each file is a Blob type.
<input type=file accept=image/* multiple /> <img class=preivew /> <script type=text/javascript> function onFileChange (event) { const files = Array.prototype.slice.call(event.target.files ) files.forEach(file => console.log('file name:', file.name)) } document.querySelector('input').addEventListener('change', onFileChange) </script>Picture preview
The URL.createObjectURL method can create a local URL path pointing to the local resource object. This interface is used below to create the address of the selected image and display it.
function onFileChange (event) { const files = Array.prototype.slice.call(event.target.files) const file = files[0] document.querySelector('img').src = window.URL.createObjectURL(file) }Picture rotation
Pictures taken with a camera may be rotated due to the direction in which the camera is held when shooting, and need to be corrected. Correcting the rotation requires knowing the rotation information of the image. Here we use a library called exif-js, which can read the EXIF metadata of the image, including the direction of the camera when shooting. Based on this direction, the rotation information of the image can be calculated.
The following is the EXIF rotation flag bit. There are 8 types in total, but when shooting through the camera, only 1, 3, 6, and 8 can be generated, which respectively correspond to the camera normal, 180° clockwise rotation, 90° counterclockwise rotation, and clockwise rotation. Photo taken at 90°.
Therefore, to correct the image rotation angle, you only need to read the EXIF rotation flag of the image, determine the rotation angle, rotate the image on the canvas, and re-export a new image. Regarding the rotation operation of the canvas, you can refer to the article "Canvas Image Rotation and Flip Posture Unlocking". The following function implements the rotation angle correction of the image file, receives an image file, and returns the corrected new image file.
/** * Fix image rotation angle problem * @param {file} original image * @return {Promise} resolved promise Return the corrected new image */function fixImageOrientation (file) { return new Promise((resolve, reject) => { // Get the image const img = new Image(); img.src = window.URL.createObjectURL(file); img.onerror = () => resolve(file); img.onload = () => { // Get the image metadata (EXIF variables are global variables exposed by the introduced exif-js library) EXIF.getData(img, function() { // Get the image rotation flag var orientation = EXIF.getTag(this, Orientation); // Rotate the image on the canvas according to the rotation angle if (orientation === 3 || orientation === 6 || orientation === 8) { const canvas = document.createElement(canvas); const ctx = canvas.getContext(2d); switch (orientation) { case 3: // Rotate 180° canvas.width = img.width; canvas.height = img.height; ctx.rotate( (180 * Math.PI) / 180); ctx.drawImage(img, -img.width, -img.height, img.width, img.height); break; case 6: // Rotate 90° canvas.width = img.height; canvas.height = img.width; ctx.rotate((90 * Math.PI) / 180); ctx.drawImage(img, 0, -img.height, img.width, img.height); break; case 8: // Rotate -90° canvas.width = img.height; canvas.height = img.width; ctx.rotate((-90 * Math.PI) / 180); ctx.drawImage(img, -img.width, 0, img .width, img.height); break; } // Return the new image canvas.toBlob(file => resolve(file), 'image/jpeg', 0.92) } else { return resolve(file); } }); }; });}Image compression
Nowadays, the camera effect of mobile phones is getting better and better, and with it the size of pictures has increased, which can easily reach a few MB or even more than ten MB. Directly uploading the original picture is slow and easy to upload failure, and the background also has restrictions on the size of the request body. , subsequent loading of image display will also be slower. These problems can be solved if the front-end compresses the images and then uploads them.
The following function implements image compression. The principle is to draw the scaled image on the canvas, and finally export the compressed image from the canvas. There are two ways to control image compression: one is to control the zoom ratio of the image; the other is to control the quality of the exported image.
/** * Compressed image * @param {file} Input image * @returns {Promise} resolved promise Returns the new compressed image */function compressImage(file) { return new Promise((resolve, reject) => { // Get the image (loading the image is to get the width and height of the image) const img = new Image(); img.src = window.URL.createObjectURL(file); img.onerror = error => reject(error); img.onload = () => { // Canvas width and height const canvasWidth = document.documentElement.clientWidth * window.devicePixelRatio; const canvasHeight = document.documentElement.clientHeight * window.devicePixelRatio; // Calculate scaling factor // Here I take the larger horizontal and vertical scaling factors as the scaling factor, so as to ensure that all the image content is visible const scaleX = canvasWidth / img.width; const scaleY = canvasHeight / img.height; const scale = Math.min(scaleX, scaleY); // Scale the original image according to the scaling factor and draw it to the canvas const canvas = document.createElement(' canvas'); const ctx = canvas.getContext(2d); canvas.width = canvasWidth; canvas.height = canvasHeight; const imageWidth = img.width * scale; const imageHeight = img.height * scale; const dx = (canvasWidth - imageWidth) / 2; const dy = (canvasHeight - imageHeight) / 2; ctx.drawImage(img, dx, dy, imageWidth, imageHeight ); // Export new image // Specify the image MIME type as 'image/jpeg', pass quality Control the quality of the exported images and implement image compression const quality = 0.92 canvas.toBlob(file => resolve(tempFile), image/jpeg, quality); }; });},Image upload
Create form data through FormData and initiate an ajax POST request. The following function implements uploading files.
Note: When sending FormData data, the browser will automatically set the Content-Type to an appropriate value. There is no need to set the Content-Type, otherwise an error will be reported because the HTTP request body delimiter boundary is generated by the browser and cannot be set manually.
/** * Upload file* @param {File} file File to be uploaded* @returns {Promise} Returns resolved promise if the upload is successful, otherwise returns rejected promise */function uploadFile (file) { return new Promise((resolve, reject) = > { // Prepare form data const formData = new FormData() formData.append('file', file) // Submit request const xhr = new XMLHttpRequest() xhr.open('POST', uploadUrl) xhr.onreadystatechange = function () { if (this.readyState === XMLHttpRequest.DONE && this.status === 200) { resolve(JSON.parse(this.responseText)) } else { reject(this.responseText) } } xhr.send(formData) })}summary
With the above auxiliary functions, the processing is much simpler. The final calling code is as follows:
function onFileChange (event) { const files = Array.prototype.slice.call(event.target.files) const file = files[0] // Fix image rotation fixImageOrientation(file).then(file2 => { // Create preview Image document.querySelector('img').src = window.URL.createObjectURL(file2) // Compression return compressImage(file2) }).then(file3 => { // Update preview image document.querySelector('img').src = window.URL.createObjectURL(file3) // Upload return uploadFile(file3) }).then(data => { console.log('Upload successful') }).catch(error => { console.error('Upload failed') })}
H5 provides an interface for processing files. With the help of canvas, complex image processing can be implemented in the browser. This article summarizes some image processing practices in the scenario of uploading images in H5 on the mobile terminal. It can be used as a partial reference when encountering similar needs in the future.
Attached is a small program implementation reference
// Take photos wx.chooseImage({ sourceType: [camera], success: ({ tempFiles }) => { const file = tempFiles[0] // Process images }});/** * Compress images* @param {Object } params * filePath: String The input image path * success: Function Called back when the compression is successful and returns the new compressed image path * fail: Function Callback when compression fails */compressImage({ filePath, success, fail }) { // Get the image width and height wx.getImageInfo({ src: filePath, success: ({ width, height }) => { const systemInfo = wx.getSystemInfoSync (); const canvasWidth = systemInfo.screenWidth; const canvasHeight = systemInfo.screenHeight; // Update the canvas size this.setData({ canvasWidth, canvasHeight }) // Calculate the scaling ratio const scaleX = canvasWidth / width; const scaleY = canvasHeight / height; const scale = Math.min(scaleX, scaleY); const imageWidth = width * scale ; const imageHeight = height * scale; // Draw the scaled image to the canvas const ctx = wx.createCanvasContext(hidden-canvas); let dx = (canvasWidth - imageWidth) / 2; let dy = (canvasHeight - imageHeight) / 2; ctx.drawImage(filePath, dx, dy, imageWidth, imageHeight); ctx.draw(false, () => { // Export Compressed images to temporary files wx.canvasToTempFilePath({ canvasId: hidden-canvas, width: canvasWidth, height: canvasHeight, destWidth: canvasWidth, destHeight: canvasHeight, fileType: jpg, quality: 0.92, success: ({ tempFilePath }) => { // Hide the canvas this.setData({ canvasWidth: 0, canvasHeight: 0 }) // Compression completed success({ tempFilePath }); }, fail: error => { // Hide canvas this.setData({ canvasWidth: 0, canvasHeight: 0 }) fail(error); } }); }); }, fail: error => { fail(error); } });}/** * Upload file*/uploadFile({ uploadUrl, filePath, onData, onError }) { wx.uploadFile({ url: uploadUrl filePath: filePath, name: file, header: { Cookie: cookie }, success: res => { if (res.statusCode === 200) { onData(res.data) } else { onError(res); } }, fail: error => { onError(error); } });}Summarize
The above is the HTML5 and small program introduced by the editor to realize the functions of rotating, compressing and uploading pictures. I hope it will be helpful to you. If you have any questions, please leave me a message and the editor will reply to you in time. I would also like to thank everyone for your support of the VeVb martial arts website!