最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

前端JS壓縮圖片的原理詳解(附源碼)

 更新時(shí)間:2024年06月12日 10:25:45   作者:JacksonChen_  
上傳圖片/視頻/文件是我們經(jīng)常會(huì)遇到的問(wèn)題,但是一旦圖片過(guò)大就會(huì)導(dǎo)致不好的操作體驗(yàn),下面這篇文章主要給大家介紹了關(guān)于前端JS壓縮圖片原理及實(shí)現(xiàn)的相關(guān)資料,需要的朋友可以參考下

前言

相信大家都做過(guò)圖片上傳相關(guān)的功能,在圖片上傳的過(guò)程中,不知道大家有沒(méi)有考慮過(guò)文件體積的問(wèn)題,如果我們直接將原圖片上傳,可以圖片體積比較大,一是上傳速度較慢,二是前端進(jìn)行渲染時(shí)速度也比較慢,比較影響客戶的體驗(yàn)感。所以在不影響清晰度的情況下,前端可以在上傳前對(duì)圖片的大小體積進(jìn)行壓縮,壓縮到一個(gè)比較合適的大小進(jìn)行上傳,本文就帶大家一起來(lái)看看前端 JS 如何實(shí)現(xiàn)圖片壓縮,有需要的小伙伴抓緊收藏一下吧!

原理(必看)

省流:主要使用 canvas的 drawImage 方法先繪制為 canvas 圖像,再結(jié)合 toDataURL 轉(zhuǎn)化為DataURl 進(jìn)行存儲(chǔ)圖片鏈接。

drawImage簡(jiǎn)單介紹

Canvas 2D API 中的 CanvasRenderingContext2D.drawImage() 方法提供了多種在畫布Canvas)上繪制圖像的方式。

用法如下:

CanvasRenderingContext2D.drawImage() - Web API 接口參考 | MDN (mozilla.org)

語(yǔ)法如下:

drawImage(image, dx, dy);
drawImage(image, dx, dy, dWidth, dHeight);
drawImage(image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight);

 我們使用第二種進(jìn)行繪制,參數(shù)含義如下:

  • image:繪制到上下文的元素。
  • dximage 的左上角在目標(biāo)畫布上 X 軸坐標(biāo)。
  • dy:image 的左上角在目標(biāo)畫布上 Y 軸坐標(biāo)。
  • dWidth:image 在目標(biāo)畫布上繪制的寬度。允許對(duì)繪制的 image 進(jìn)行縮放。如果不說(shuō)明,在繪制時(shí) image 寬度不會(huì)縮放。
  • dHeight:image 在目標(biāo)畫布上繪制的高度。允許對(duì)繪制的 image 進(jìn)行縮放。如果不說(shuō)明,在繪制時(shí) image 高度不會(huì)縮放。

簡(jiǎn)單示例

注意:如果隨意的修改圖像的尺寸,會(huì)導(dǎo)致圖像失真,我們可以先獲取到圖像資源的原始尺寸,然后進(jìn)行等比縮放,意思就是當(dāng)我們確定設(shè)置寬度之后,高度要進(jìn)行等比調(diào)整。公式就是交叉相乘積相等。

        // 如果寬度設(shè)置為 500, 那么高度也應(yīng)該進(jìn)行等比縮放
        // naturalWidth         =>  500
        // naturalHeight        =>  X
        // naturalWidth * X     =   naturalHeight * 500

       //  計(jì)算得出高度
        X =   naturalHeight * 500 / naturalWidth

  var can = document.querySelector('canvas')
  var context = can.getContext('2d')   
  var imgDom = new Image();
    imgDom.src = './img.jpg';
    imgDom.onload = function () {
        // 注意:圖像繪制時(shí),必須保證資源已經(jīng)加載完成
        console.log('圖片的原始寬度', imgDom.naturalWidth);
        console.log('圖片的原始高度', imgDom.naturalHeight);

        context.drawImage(
            imgDom,
            0, 0,
            500, imgDom.naturalHeight * 500 / imgDom.naturalWidth
        );
    }

toDataURL簡(jiǎn)單介紹

我們將圖片繪制到 canvas 之后,還需要將 canvas 轉(zhuǎn)化為 Data URl,轉(zhuǎn)化為 DataURl 之后可以顯示到我們的屏幕上面,也可以存放到后端服務(wù)器,使用 canvas 所提供的 toDataURL 實(shí)例方法即可。

? 官方解釋:HTMLCanvasElement.toDataURL() 方法返回一個(gè)包含圖片展示的 data URI ?

HTMLCanvasElement.toDataURL() - Web API 接口參考 | MDN (mozilla.org)

語(yǔ)法:canvas.toDataURL(type, encoderOptions);

type(可選):圖片格式,默認(rèn)為 image/png

encoderOptions(可選):在指定圖片格式為 image/jpeg 或 image/webp 的情況下,可以從 0 到 1 的區(qū)間內(nèi)選擇圖片的質(zhì)量。如果超出取值范圍,將會(huì)使用默認(rèn)值 0.92。其他參數(shù)會(huì)被忽略。

簡(jiǎn)單示例 

       // 獲取壓縮后的圖片數(shù)據(jù)
      can.width = imgDom.naturalWidth
      can.height = imgDom.naturalHeight

      const compressedData = can.toDataURL('image/jpeg', 0.6) // 可調(diào)整質(zhì)量參數(shù)
      console.log('compressedData: ', compressedData)

轉(zhuǎn)化后 DataURL 結(jié)果如下 

實(shí)現(xiàn)

先奉上全部代碼,方便大家看,下面進(jìn)行解釋!

<!DOCTYPE html>
<html>
<head>
    <title>圖片壓縮上傳</title>
    <meta charset="UTF-8">
</head>
<body>
    <input type="file" id="fileInput" accept="image/*">
    <button onclick="compressAndUpload()">壓縮并上傳圖片</button>
    <canvas id="canvas" style="display: none;"></canvas>
    <script>
        function compressAndUpload() {
            const fileInput = document.getElementById('fileInput');
            const file = fileInput.files[0];
            if (!file) {
                alert('請(qǐng)先選擇要上傳的圖片');
                return;
            }
            const reader = new FileReader();
            reader.onload = function () {
                const img = new Image();
                img.src = reader.result;
                img.onload = function () {
                    const canvas = document.getElementById('canvas');
                    const ctx = canvas.getContext('2d');
                    const maxWidth = 800; // 設(shè)置最大寬度為800像素
                    let width = img.width;
                    let height = img.height;

                    // 判斷是否需要縮放
                    if (width > maxWidth) {
                        height *= maxWidth / width;
                        width = maxWidth;
                    }
                    // 設(shè)置 canvas 的寬高
                    canvas.width = width;
                    canvas.height = height;

                    // 將圖片繪制到 canvas 上
                    ctx.drawImage(img, 0, 0, width, height);
                    // 獲取壓縮后的圖片數(shù)據(jù)
                    const compressedData = canvas.toDataURL('image/jpeg', 0.7); // 可調(diào)整質(zhì)量參數(shù)

                    // 創(chuàng)建一個(gè)新的壓縮后的 File 對(duì)象
                    const compressedFile = dataURItoBlob(compressedData, file.type);
                    compressedFile.lastModifiedDate = file.lastModifiedDate;
                    compressedFile.name = file.name;

                    // 上傳壓縮后的圖片文件
                    uploadImage(compressedFile);
                };
            };
            reader.readAsDataURL(file);
        }

        function dataURItoBlob(dataURI, mimeType) {
            const binary = atob(dataURI.split(',')[1]);
            const array = [];
            for (let i = 0; i < binary.length; i++) {
                array.push(binary.charCodeAt(i));
            }
            return new Blob([new Uint8Array(array)], { type: mimeType });
        }

        function uploadImage(compressedFile) {
            const formData = new FormData();
            formData.append('image', compressedFile);

            fetch('/upload', {
                method: 'POST',
                body: formData
            })
            .then(response => {
                if (response.ok) {
                    console.log('圖片上傳成功');
                } else {
                    console.error('圖片上傳失敗');
                }
            })
            .catch(error => {
                console.error('發(fā)生錯(cuò)誤:', error);
            });
        }
    </script>
</body>
</html>

我們看一下壓縮前后體積對(duì)比,壓縮前550290,壓縮后31523,縮小了十幾倍,這個(gè)壓縮還是很明顯的。

 首先我們看這三行代碼

我們先初始化一個(gè) reader 是一個(gè) FileReader 對(duì)象的實(shí)例 

reader.readAsDataURL(file),這行代碼的作用是將選擇的文件讀取為 Data URI 格式的字符串。

當(dāng)執(zhí)行 reader.readAsDataURL(file) 時(shí),會(huì)發(fā)生以下幾件事情:

FileReader 對(duì)象開(kāi)始異步讀取 file 中的數(shù)據(jù)。

一旦讀取完成,FileReader 的 onload 事件將被觸發(fā)。

讀取的結(jié)果將存儲(chǔ)在 FileReader 對(duì)象的 result 屬性中,格式為 Data URI 字符串。

看一下 FileReader 對(duì)象的 result  的打印結(jié)果,為 Data URL格式

最終我們將讀取出來(lái)的 Data URI 字符串賦值給 Image 的 src,也就是下面這行代碼,然后等待 img 加載完畢開(kāi)始對(duì) img 進(jìn)行壓縮,具體怎么壓縮上面已經(jīng)簡(jiǎn)單演示過(guò)。

          const img = new Image()
          img.src = reader.result
          img.onload = function () {}

接下來(lái)我們?cè)O(shè)置了一個(gè)最大寬度為800,然后判斷當(dāng)前圖片寬度是否大于該值,如果大于進(jìn)行縮放計(jì)算,小于就不進(jìn)行等比縮放計(jì)算。最后將計(jì)算出的值使用 drawImage 繪制到 canvas 上面。

                    const maxWidth = 800; // 設(shè)置最大寬度為800像素
                    let width = img.width;
                    let height = img.height;

                    // 判斷是否需要縮放
                    if (width > maxWidth) {
                        height *= maxWidth / width;
                        width = maxWidth;
                    }

                    // 設(shè)置 canvas 的寬高
                    canvas.width = width;
                    canvas.height = height;

                    // 將圖片繪制到 canvas 上
                    ctx.drawImage(img, 0, 0, width, height);

現(xiàn)在我們將 canvas 轉(zhuǎn)化成Data URI 字符串,canvas.toDataURL('image/jpeg', 0.7),這行代碼的作用是將 canvas 上繪制的圖像數(shù)據(jù)導(dǎo)出為 JPEG 格式的 Data URI 字符串,并設(shè)置圖像質(zhì)量為 0.7。

// 獲取壓縮后的圖片數(shù)據(jù)
const compressedData = canvas.toDataURL('image/jpeg', 0.7); // 可調(diào)整

我們看一下 compressedData,是一個(gè)Data URI 字符串,其實(shí)到這里就可以了,我們可以將 Data URI 傳到后端進(jìn)行存儲(chǔ)起來(lái),也可以轉(zhuǎn)化為文件格式進(jìn)行存儲(chǔ),我這里選擇使用文件格式進(jìn)行存儲(chǔ),不需要的可以不使用下面的方式。

接下來(lái)就是創(chuàng)建一個(gè)新的壓縮后的 File 對(duì)象

      const compressedFile = dataURItoBlob(compressedData, file.type)
      compressedFile.lastModifiedDate = file.lastModifiedDate
      compressedFile.name = file.name

      function dataURItoBlob(dataURI, mimeType) {
        const binary = atob(dataURI.split(',')[1])
        const array = []
        for (let i = 0; i < binary.length; i++) {
          array.push(binary.charCodeAt(i))
        }
        return new Blob([new Uint8Array(array)], { type: mimeType })
      }

新的文件對(duì)象

最后直接使用 FormData 進(jìn)行上傳即可,這一塊就不說(shuō)了。

總結(jié)

前端實(shí)現(xiàn)圖片壓縮主要是利用的 canvas 來(lái)實(shí)現(xiàn),實(shí)現(xiàn)思路為使用 canvas 的 drawImage 方法先繪制為 canvas 圖像,再結(jié)合 toDataURL 轉(zhuǎn)化為 DataURl 進(jìn)行存儲(chǔ)圖片鏈接以及壓縮圖像質(zhì)量。在toDataURL 中可以調(diào)整圖像質(zhì)量,需要注意的是我們?cè)趬嚎s圖像時(shí)要注意等寬高縮放,否則會(huì)導(dǎo)致圖像出現(xiàn)失真的情況。

到此這篇關(guān)于前端JS壓縮圖片的文章就介紹到這了,更多相關(guān)前端JS壓縮圖片內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • js序列化和反序列化的使用講解

    js序列化和反序列化的使用講解

    今天小編就為大家分享一篇關(guān)于js序列化和反序列化的使用講解,小編覺(jué)得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來(lái)看看吧
    2019-01-01
  • 利用JS實(shí)現(xiàn)點(diǎn)擊按鈕后圖片自動(dòng)切換的簡(jiǎn)單方法

    利用JS實(shí)現(xiàn)點(diǎn)擊按鈕后圖片自動(dòng)切換的簡(jiǎn)單方法

    下面小編就為大家?guī)?lái)一篇利用JS實(shí)現(xiàn)點(diǎn)擊按鈕后圖片自動(dòng)切換的簡(jiǎn)單方法。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2016-10-10
  • cocos2dx骨骼動(dòng)畫Armature源碼剖析(二)

    cocos2dx骨骼動(dòng)畫Armature源碼剖析(二)

    本篇主要給大家介紹cocos2dx骨骼動(dòng)畫Armature源碼剖析之flash中數(shù)據(jù)與xml中數(shù)據(jù)關(guān)系,需要的朋友一起來(lái)學(xué)習(xí)吧
    2015-09-09
  • JavaScript?日期和時(shí)間的格式化方法

    JavaScript?日期和時(shí)間的格式化方法

    這篇文章主要介紹了JavaScript?日期和時(shí)間的格式化,JavaScript?中的?Date?對(duì)象提供了許多方法和屬性,可以用于處理日期和時(shí)間,根據(jù)具體情況選擇適合的方法和技巧,需要的朋友可以參考下
    2023-02-02
  • TypeScript 映射類型詳情

    TypeScript 映射類型詳情

    這篇文章主要介紹了TypeScript 映射類型詳情,一個(gè)類型需要基于另外一個(gè)類型, 又不想拷貝一份,這個(gè)時(shí)候可以考慮使用映射類型,映射類型建立在索引簽名的語(yǔ)法上,下面文章我們就從回顧下索引簽名展開(kāi)TypeScript 映射類型的相關(guān)資料,需要的朋友可以參考一下
    2021-12-12
  • JavaScript 浮點(diǎn)數(shù)運(yùn)算 精度問(wèn)題

    JavaScript 浮點(diǎn)數(shù)運(yùn)算 精度問(wèn)題

    JavaScript小數(shù)在做四則運(yùn)算時(shí),精度會(huì)丟失,這會(huì)在項(xiàng)目中引起諸多不便,先請(qǐng)看下面腳本。
    2009-10-10
  • javascript中[]和{}對(duì)象使用介紹

    javascript中[]和{}對(duì)象使用介紹

    []不僅僅可以表示數(shù)組,可以直接通過(guò)對(duì)象的屬性設(shè)置值和訪問(wèn)值,接下來(lái)為大家介紹下[]和{}對(duì)象的使用,感興趣的你可以參考下哈
    2013-03-03
  • 微信小程序數(shù)據(jù)分析之自定義分析的實(shí)現(xiàn)

    微信小程序數(shù)據(jù)分析之自定義分析的實(shí)現(xiàn)

    這篇文章主要介紹了微信小程序數(shù)據(jù)分析之自定義分析的實(shí)現(xiàn),小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2018-08-08
  • 返回頁(yè)面頂部top按鈕通過(guò)錨點(diǎn)實(shí)現(xiàn)(自寫)

    返回頁(yè)面頂部top按鈕通過(guò)錨點(diǎn)實(shí)現(xiàn)(自寫)

    用戶在使用系統(tǒng)時(shí),會(huì)有很多表單的操作然而很多表單就會(huì)很長(zhǎng),所以就需要一個(gè)返回頁(yè)面頂部的top按鈕啦,于是自己寫了一個(gè),喜歡的朋友可以參考下
    2013-08-08
  • 使用bootstrap莫名其妙出現(xiàn)橫向滾動(dòng)條的問(wèn)題及解決

    使用bootstrap莫名其妙出現(xiàn)橫向滾動(dòng)條的問(wèn)題及解決

    這篇文章主要介紹了使用bootstrap莫名其妙出現(xiàn)橫向滾動(dòng)條的問(wèn)題及解決,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-11-11

最新評(píng)論

中卫市| 阿克陶县| 洛南县| 宜兴市| 舒城县| 娄底市| 黑龙江省| 新蔡县| 安丘市| 周宁县| 昌邑市| 青岛市| 和龙市| 姜堰市| 武强县| 桑植县| 辽阳县| 白银市| 新源县| 曲靖市| 奉新县| 旌德县| 斗六市| 佛学| 资中县| 大连市| 丹寨县| 嘉祥县| 三明市| 文昌市| 黄石市| 连南| 祥云县| 水城县| 兴和县| 安仁县| 曲松县| 湖口县| 盐亭县| 广昌县| 葵青区|