您好,登錄后才能下訂單哦!
小編給大家分享一下怎么使用Node.js實現圖片的動態裁切及算法,相信大部分人都還不怎么了解,因此分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后大有收獲,下面讓我們一起去了解一下吧!
背景&概覽
目前常見的圖床服務都會有圖片動態裁切的功能,主要的應用場景用以為各種終端和業務形態輸出合適尺寸的圖片。
一張動輒以 MB 為計量單位的原始大圖,通常不會只設置一下顯示尺寸就直接輸出到終端中,因為體積太大加載體驗會很差,除了影響加載速度還會增加終端設備的內存占用。所以要想在各種終端下都能保證圖片質量的同時又確保輸出合適的尺寸,那么此時就需要根據圖片 URL 來對原始圖片進行裁切,然后動態生成并輸出一張新的圖片。
URL 的設計
圖片 URL 需要包含圖片 id、尺寸、質量等信息。有兩種類型的圖片 URL,分別是原圖 URL 和帶動態裁切信息的 URL。
// 原圖 URL http://example.com/$imgId // 帶裁切信息的圖片 URL http://example.com/$cropType/$width_$height_$quality/$imgId
來分析一下上面 URL 中的變量:
$imgId
$cropType
$width
$height
$quality
那么一張圖片 id 為 4b2d4edcc1f82452 的原圖 URL 應該是:
http://example.com/4b2d4edcc1f82452.jpg
如果想要一張該圖 800×600 的版本,裁切的 URL 大致是下面這樣的:
http://example.com/es/800_600_/4b2d4edcc1f82452.jpg
裁切算法
該來說說以上 URL 背后的算法了。在 Node.js 中可以使用著名的圖片裁切庫 GM ,該庫是基于 imagemagick 和 graphicsmagick 底層庫的封裝。
最常見的裁切算法是等比例裁切,等比裁切的算法需要至少給出裁切目標圖片的寬度和高度的其中一個,如果圖片限寬就給出寬度,限高就給出高度,如果兩個參數都有,就需要確保裁切的目標寬高相對于原始的寬高是按比例計算的,否則裁切的結果就會出現拉伸。
var gm = require('gm'); // 裁切的最小尺寸 var minSize = 48; var defaultQuality = 90; /** * 等比例縮放 equal scaling * @param { String } 原文件路徑 * @param { String } 新文件路徑 * @param { String } 縮放規則 * @return { promise } */ var es = function(src, dest, rules) { return new Promise(function(resolve, reject) { // 900_600_90 => 寬度900/高度600/品質90 rules = rules.split('_'); if (rules.length !== 3) { return reject(new Error('Resize rules invalid')); } // 解析裁切的目標寬高 let resizeWidth = parseInt(rules[0]); let resizeHeight = parseInt(rules[1]); let quality = parseInt(rules[2]) || defaultQuality; const readStream = fs.createReadStream(src); const writeStream = fs.createWriteStream(dest); gm(readStream) .size({ bufferStream: true }, function(err, size) { if (err) { return reject(err); } const origWidth = size.width; const origHeight = size.height; let resizeResult; // 縮放的寬度和高度做最大最小值限制 if (resizeWidth) { if (resizeWidth > origWidth * 1.5) { resizeWidth = Math.floor(origWidth * 1.5); } else if (resizeWidth < minSize) { resizeWidth = minSize; } } if (resizeHeight) { if (resizeHeight > origHeight * 1.5) { resizeHeight = Math.floor(origHeight * 1.5); } else if (resizeHeight < minSize) { resizeHeight = minSize; } } resizeResult = this.resize(resizeWidth, resizeHeight); resizeResult .quality(quality) .interlace('line') // 使用逐行掃描方式 .unsharp(2, 0.5, 0.5, 0) .stream() .on('end', resolve) .pipe(writeStream); }); }); };
說說幾個重要的 API:
quality 設置圖片的質量,GM 圖片質量范圍是 0-100,默認的質量是 75。
interlace 用于設置圖片在顯示器上加載時的顯示方式,當然顯示方式本身還要受圖片本身的影響。
unsharp 用來設置圖片的銳度,將一張大圖縮放成一張小圖時,會損失很多像素,需要適當的增加圖片銳度來保證圖片的質量。關于 unsharp 的使用,詳見 Using ImageMagick to make sharp web-sized photographs 。
等比例裁切嚴格來說實際上還只是對圖片進行縮放,并未動用圖片裁切的 API。
還有一種比較常見的裁切方式,會先將圖片等比例縮放后再從中心裁切,裁切出來的圖片是一個正方形,這樣能盡可能保證圖片的內容。
/* * 等比例縮放后從中心裁切 equal scaling crop center(正方形裁切) * @param { String } 原文件路徑 * @param { String } 新文件路徑 * @param { String } 縮放規則 * @return { promise } */ var escc = function(src, dest, rules) { return new Promise(function(resolve, reject) { // 600_90 => 寬度600/高度600/品質90 rules = rules.split('_'); if (rules.length !== 2) { return reject(new Error('Resize rules invalid')); } let cropSize = parseInt(rules[0]); let quality = parseInt(rules[1]) || defaultQuality; const readStream = fs.createReadStream(src); const writeStream = fs.createWriteStream(dest); if (!cropSize) { reject(new Error('Crop params invalid')); return; } gm(readStream) .size({ bufferStream: true }, function(err, size) { if (err) { reject(err); return; } const origWidth = size.width; const origHeight = size.height; let cropX = 0; let cropY = 0; let resizeWidth; let resizeHeight; let resizeResult; // 裁切的寬度和高度做最大最小值限制 if (cropSize > origWidth) { cropSize = origWidth; } else if (cropSize > origHeight) { cropSize = origHeight; } else if (cropSize < minSize) { cropSize = minSize; } // 先計算出等比縮放的尺寸,然后再根據此尺寸計算出裁切位置 if (origWidth > origHeight) { resizeWidth = cropSize / origHeight * origWidth; resizeHeight = cropSize; cropX = Math.floor((resizeWidth - cropSize) / 2); cropY = 0; } else { resizeHeight = cropSize / origWidth * origHeight; resizeWidth = cropSize; cropX = 0; cropY = Math.floor((resizeHeight - cropSize) / 2); } resizeResult = this.resize(resizeWidth, resizeHeight); resizeResult .quality(quality) .interlace('line') // 使用逐行掃描方式 .crop(cropSize, cropSize, cropX, cropY) .unsharp(2, 0.5, 0.5, 0) .stream() .on('end', resolve) .pipe(writeStream); }); }); };
上面的 crop 就是對圖片進行裁切。當然除了中心裁切,還能延伸出頂部裁切,底部裁切等,相對來說使用場景要少很多。
以上是“怎么使用Node.js實現圖片的動態裁切及算法”這篇文章的所有內容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內容對大家有所幫助,如果還想學習更多知識,歡迎關注億速云行業資訊頻道!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。