Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

How to compress a picture with JS

2025-01-28 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >

Share

Shulou(Shulou.com)06/02 Report--

This article mainly explains "how to use JS to compress pictures". Interested friends may wish to have a look. The method introduced in this paper is simple, fast and practical. Let's let the editor take you to learn how to compress pictures with JS.

Transformation relationship

In practical applications, it is possible to use scenarios: most of the time, we directly read the File object uploaded by the user, read and write it to the canvas (canvas), compress it using the API of Canvas, and then convert it to File (Blob) object and upload it to the remote image server; sometimes we also need to compress a base64 string and then transfer it to the remote database or File (Blob) object. In general, they have the following transformation relationship:

Concrete realization

The following will be implemented one by one according to the transformation method in the transformation diagram.

File2DataUrl (file, callback)

The local images uploaded by users through the page tags are directly converted into data URL strings. You can use the FileReader file to read the constructor. The FileReader object allows Web applications to asynchronously read the contents of files (or raw data buffers) stored on the computer, using File or Blob objects to specify the files or data to read. The instance method readAsDataURL reads the contents of the file and converts it into a base64 string. After reading, the contents of the file can be obtained on the instance property result.

Function file2DataUrl (file, callback) {var reader = new FileReader (); reader.onload = function () {callback (reader.result);}; reader.readAsDataURL (file);}

Data URL consists of four parts: the prefix (data:), the MIME type that indicates the data type, the optional base64 tag if it is not text, and the data itself:

Data:

For example, a picture in png format is converted into base64 string form: data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAQAAAAEACAYAAABccqhmAAAgAElEQVR4XuxdB5g.

File2Image (file, callback)

If you want to cache the pictures uploaded locally and display them with the img tag, you can not only use the base64 string converted from the above method as the image src, but also directly use the URL object to reference the URL that stores the data in File and Blob. The advantage of using the object URL is that you can use the file contents directly without having to read the file contents into JavaScript. To do this, simply provide the object URL where the contents of the file are needed.

Function file2Image (file, callback) {var image = new Image (); var URL = window.webkitURL | | window.URL; if (URL) {var url = URL.createObjectURL (file); image.onload = function () {callback (image); URL.revokeObjectURL (url);}; image.src = url;} else {inputFile2DataUrl (file, function (dataUrl) {image.onload = function () {callback (image)) } image.src = dataUrl;});}}

Note: to create an object URL, you can use the window.URL.createObjectURL () method and pass in a File or Blob object. If the data is no longer needed, it is best to release the content it occupies. But as long as there is code referencing the object URL, memory will not be freed. To free memory manually, you can pass the object URL to URL.revokeObjectURL ().

Url2Image (url, callback)

The image Image object is obtained through the picture link (url). Since the image is loaded asynchronously, it is put into the Image object obtained by the callback function callback.

Function url2Image (url, callback) {var image = new Image (); image.src = url; image.onload = function () {callback (image);}} image2Canvas (image)

The Image object is drawn on the Canvas object using the drawImage () method.

DrawImage has three grammatical forms:

Void ctx.drawImage (image, dx, dy)

Void ctx.drawImage (image, dx, dy, dWidth, dHeight)

Void ctx.drawImage (image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight)

Parameters:

Elements drawn by image to context

The upper left corner of the sx drawing selection box takes Image as the reference X axis coordinate

The upper left corner of the sy drawing selection box takes Image as the reference Y axis coordinate

SWidth draw selection box width

SHeight draw selection box width

The upper-left corner of dx Image is in X-axis coordinates on the target canvas.

The upper left corner of dy Image is in Y-axis coordinates on the target canvas.

The width drawn by dWidth Image on the target canvas

The height dHeight Image draws on the target canvas

Function image2Canvas (image) {var canvas = document.createElement ('canvas'); var ctx = canvas.getContext (' 2d'); canvas.width = image.naturalWidth; canvas.height = image.naturalHeight; ctx.drawImage (image, 0,0, canvas.width, canvas.height); return canvas;} canvas2DataUrl (canvas, quality, type)

The HTMLCanvasElement object has a toDataURL (type, encoderOptions) method that returns a data URL containing the image display. You can also specify the output format and quality.

The parameters are:

Type image format, default is image/png.

EncoderOptions can choose the quality of the picture in the range from 0 to 1 when the picture format is image/jpeg or image/webp. If the value range is out of range, the default value of 0.92 will be used and other parameters will be ignored.

Function canvas2DataUrl (canvas, quality, type) {return canvas.toDataURL (type | | 'image/jpeg', quality | | 0.8);} dataUrl2Image (dataUrl, callback)

The picture link can also be a base64 string, which can be assigned directly to the Image object src.

Function dataUrl2Image (dataUrl, callback) {var image = new Image (); image.onload = function () {callback (image);}; image.src = dataUrl;} dataUrl2Blob (dataUrl, type)

Converts a data URL string to a Blob object. The main idea is: first extract the data URL data (data), decode the string encoded by base64 with atob, then convert it into Unicode encoding, store it in Uint8Array (8-bit unsigned integer array, each element is a byte) type array, and finally convert it into Blob object.

Function dataUrl2Blob (dataUrl, type) {var data = dataUrl.split (',') [1]; var mimePattern = / ^ data: (. *?) (; base64)?, /; var mime = dataUrl.match (mimePattern) [1]; var binStr = atob (data); var arr = new Uint8Array (len); for (var I = 0; I

< len; i++) { arr[i] = binStr.charCodeAt(i); } return new Blob([arr], {type: type || mime}); }canvas2Blob(canvas, callback, quality, type) HTMLCanvasElement 有 toBlob(callback, [type], [encoderOptions]) 方法创造 Blob 对象,用以展示 canvas 上的图片;这个图片文件可以被缓存或保存到本地,由用户代理端自行决定。第二个参数指定图片格式,如不特别指明,图片的类型默认为 image/png,分辨率为 96dpi。第三个参数用于针对image/jpeg 格式的图片进行输出图片的质量设置。 function canvas2Blob(canvas, callback, quality, type){ canvas.toBlob(function(blob) { callback(blob); }, type || 'image/jpeg', quality || 0.8); } 为兼容低版本浏览器,作为 toBlob 的 polyfill 方案,可以用上面 data URL 生成 Blob 方法 dataUrl2Blob 作为HTMLCanvasElement 原型方法。 if (!HTMLCanvasElement.prototype.toBlob) { Object.defineProperty(HTMLCanvasElement.prototype, 'toBlob', { value: function (callback, type, quality) { let dataUrl = this.toDataURL(type, quality); callback(dataUrl2Blob(dataUrl)); } }); }blob2DataUrl(blob, callback) 将 Blob 对象转化成 data URL 数据,由于 FileReader 的实例 readAsDataURL 方法不仅支持读取文件,还支持读取 Blob 对象数据,这里复用上面 file2DataUrl 方法即可: function blob2DataUrl(blob, callback) { file2DataUrl(blob, callback); }blob2Image(blob, callback) 将 Blob 对象转化成 Image 对象,可通过 URL 对象引用文件,也支持引用 Blob 这样的类文件对象,同样,这里复用上面 file2Image 方法即可: function blob2Image(blob, callback) { file2Image(blob, callback); }upload(url, file, callback) 上传图片(已压缩),可以使用 FormData 传入文件对象,通过 XHR 直接把文件上传到服务器。 function upload(url, file, callback) { var xhr = new XMLHttpRequest(); var fd = new FormData(); fd.append('file', file); xhr.onreadystatechange = function () { if (xhr.readyState === 4 && xhr.status === 200) { // 上传成功 callback && callback(xhr.responseText); } else { throw new Error(xhr); } } xhr.open('POST', url, true); xhr.send(fd); } 也可以使用 FileReader 读取文件内容,转化成二进制上传 function upload(url, file) { var reader = new FileReader(); var xhr = new XMLHttpRequest(); xhr.open('POST', url, true); xhr.overrideMimeType('text/plain; charset=x-user-defined-binary'); reader.onload = function() { xhr.send(reader.result); }; reader.readAsBinaryString(file); }实现简易图片压缩 在熟悉以上各种图片转化方法的具体实现,将它们封装在一个公用对象 util 里,再结合压缩转化流程图,这里我们可以简单实现图片压缩了: 首先将上传图片转化成 Image 对象,再将写入到 Canvas 画布,最后由 Canvas 对象 API 对图片的大小和尺寸输出调整,实现压缩目的。 /** * 简易图片压缩方法 * @param {Object} options 相关参数 */ (function (win) { var REGEXP_IMAGE_TYPE = /^image\//; var util = {}; var defaultOptions = { file: null, quality: 0.8 }; var isFunc = function (fn) { return typeof fn === 'function'; }; var isImageType = function (value) { return REGEXP_IMAGE_TYPE.test(value); }; /** * 简易图片压缩构造函数 * @param {Object} options 相关参数 */ function SimpleImageCompressor(options) { options = Object.assign({}, defaultOptions, options); this.options = options; this.file = options.file; this.init(); } var _proto = SimpleImageCompressor.prototype; win.SimpleImageCompressor = SimpleImageCompressor; /** * 初始化 */ _proto.init = function init() { var _this = this; var file = this.file; var options = this.options; if (!file || !isImageType(file.type)) { console.error('请上传图片文件!'); return; } if (!isImageType(options.mimeType)) { options.mimeType = file.type; } util.file2Image(file, function (img) { var canvas = util.image2Canvas(img); file.width = img.naturalWidth; file.height = img.naturalHeight; _this.beforeCompress(file, canvas); util.canvas2Blob(canvas, function (blob) { blob.width = canvas.width; blob.height = canvas.height; options.success && options.success(blob); }, options.quality, options.mimeType) }) } /** * 压缩之前,读取图片之后钩子函数 */ _proto.beforeCompress = function beforeCompress() { if (isFunc(this.options.beforeCompress)) { this.options.beforeCompress(this.file); } } // 省略 `util` 公用方法定义 // ... // 将 `util` 公用方法添加到实例的静态属性上 for (key in util) { if (util.hasOwnProperty(key)) { SimpleImageCompressor[key] = util[key]; } } })(window) 这个简易图片压缩方法调用和入参: var fileEle = document.getElementById('file'); fileEle.addEventListener('change', function () { file = this.files[0]; var options = { file: file, quality: 0.6, mimeType: 'image/jpeg', // 压缩前回调 beforeCompress: function (result) { console.log('压缩之前图片尺寸大小: ', result.size); console.log('mime 类型: ', result.type); // 将上传图片在页面预览 // SimpleImageCompressor.file2DataUrl(result, function (url) { // document.getElementById('origin').src = url; // }) }, // 压缩成功回调 success: function (result) { console.log('压缩之后图片尺寸大小: ', result.size); console.log('mime 类型: ', result.type); console.log('压缩率: ', (result.size / file.size * 100).toFixed(2) + '%'); // 生成压缩后图片在页面展示 // SimpleImageCompressor.file2DataUrl(result, function (url) { // document.getElementById('output').src = url; // }) // 上传到远程服务器 // SimpleImageCompressor.upload('/upload.png', result); } }; new SimpleImageCompressor(options); }, false); 如果看到这里的客官不嫌弃这个 demo 太简单可以戳这里试试水。如果你有足够的耐心多传几种类型图片就会发现还存在如下问题: 压缩输出图片寸尺固定为原始图片尺寸大小,而实际可能需要控制输出图片尺寸,同时达到尺寸也被压缩目的; png 格式图片同格式压缩,压缩率不高,还有可能出现"不减反增"现象; 有些情况,其他格式转化成 png 格式也会出现"不减反增"现象; 大尺寸 png 格式图片在一些手机上,压缩后出现"黑屏"现象; 改进版图片压缩 俗话说"罗马不是一天建成的",通过上述实验,我们发现了很多不足,下面将逐条问题分析,寻求解决方案。 压缩输出图片寸尺固定为原始图片尺寸大小,而实际可能需要控制输出图片尺寸,同时达到尺寸也被压缩目的; 为了避免压缩图片变形,一般采用等比缩放,首先要计算出原始图片宽高比 aspectRatio,用户设置的高乘以 aspectRatio,得出等比缩放后的宽,若比用户设置宽的小,则用户设置的高为为基准缩放,否则以宽为基准缩放。 var aspectRatio = naturalWidth / naturalHeight; var width = Math.max(options.width, 0) || naturalWidth; var height = Math.max(options.height, 0) || naturalHeight; if (height * aspectRatio >

Width) {height = width / aspectRatio;} else {width = height * aspectRatio;}

The size of the output image is determined, and the next step is to create a Canvas canvas according to this size and draw the picture on it. Here you can modify the image2Canvas method mentioned above a little bit:

Function image2Canvas (image, destWidth, destHeight) {var canvas = document.createElement ('canvas'); var ctx = canvas.getContext (' 2d'); canvas.width = destWidth | | image.naturalWidth; canvas.height = destHeight | | image.naturalHeight; ctx.drawImage (image, 0, 0, canvas.width, canvas.height); return canvas;} png format images are compressed in the same format with low compression rate, and the phenomenon of "not decreasing but increasing" may occur.

In general, it is not recommended to compress png images into their own format, so the compression ratio is not ideal, sometimes it will cause its own quality to become larger.

Because we have two key API about compression in "concrete implementation":

The toBlob (callback, [type], [encoderOptions]) parameter encoderOptions is used to set the quality of the output image for pictures in image/jpeg format.

ToDataURL (type, encoderOptions parameter encoderOptions) if the image format is image/jpeg or image/webp, you can select the image quality in the range from 0 to 1.

All of them did not compress the pictures in png format.

There is a compromise solution, we can set a threshold, if the quality of the png image is less than this value, it is still compressed output png format, so that the worst output results will not be too large, on this basis, if the compressed picture size "does not decrease but increase", we will handle the output of the source picture to the user. When the image quality is greater than a certain value, we compress it into jpeg format.

/ / the image size of `png` format exceeds `convertSize` and is converted to `jpeg` format if (file.size > options.convertSize & & options.mimeType = 'image/png') {options.mimeType =' image/jpeg' } / / omit some code / /... / / if the output width and height expected by the user is not greater than the width and height of the source image, the output file size is larger than the source file, return the source file if (result.size > file.size & &! (options.width > naturalWidth | | options.height > naturalHeight)) {result = file;} large png format image on some mobile phones, the phenomenon of "black screen" appears after compression.

Because different browsers support the maximum size of Canvas

Maximum width, height, maximum area of browser Chrome32767 pixels268435456 pixels (e.g.16384 x 16384) Firefox32767 pixels472907776 pixels (e.g.22528 x 20992) IE8192 pixelsN/AIE Mobile4096 pixelsN/A

If the size of the picture is too large, when you create a canvas of the same size and then draw a picture, there will be an exception, that is, the generated canvas has no picture pixels, and the background color given by the canvas itself is black by default, which leads to the picture "black screen".

Here, you can prevent the generated canvas from crossing the boundary by controlling the maximum width and height of the output image, and use transparent colors to cover the default black background to solve the "black screen" problem:

/ / minimum and maximum width and height var maxWidth = Math.max (options.maxWidth, 0) | | Infinity; var maxHeight = Math.max (options.maxHeight, 0) | | Infinity; var minWidth = Math.max (options.minWidth, 0) | | 0; var minHeight = Math.max (options.minHeight, 0) | | 0; if (maxWidth) |

< Infinity && maxHeight < Infinity) { if (maxHeight * aspectRatio >

MaxWidth) {maxHeight = maxWidth / aspectRatio;} else {maxWidth = maxHeight * aspectRatio;}} else if (maxWidth

< Infinity) { maxHeight = maxWidth / aspectRatio; } else if (maxHeight < Infinity) { maxWidth = maxHeight * aspectRatio; } if (minWidth >

0 & & minHeight > 0) {if (minHeight * aspectRatio > minWidth) {minHeight = minWidth / aspectRatio;} else {minWidth = minHeight * aspectRatio;}} else if (minWidth > 0) {minHeight = minWidth / aspectRatio;} else if (minHeight > 0) {minWidth = minHeight * aspectRatio;} width = Math.floor (Math.min (width, minWidth), maxWidth); height = Math.floor (Math.min (Math.max (height, minHeight), maxHeight)) / /... / override the default fill color (# 000) var fillStyle = 'transparent'; context.fillStyle = fillStyle; so far, I believe you have a deeper understanding of "how to compress a picture with JS", you might as well do it in practice! Here is the website, more related content can enter the relevant channels to inquire, follow us, continue to learn!

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Development

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report