圖片上傳自動(dòng)旋轉(zhuǎn)胧辽、壓縮

轉(zhuǎn)載自林鑫的博客

前言

最近工作中遇到一個(gè)在前端上傳圖片后需求自動(dòng)調(diào)整圖片方向的需求峻仇,查找了一下資料,結(jié)合自己的工作總結(jié)如下邑商。

背景

通過網(wǎng)頁 input 標(biāo)簽上傳圖片摄咆,有一些使用手機(jī)拍攝的圖片會(huì)出現(xiàn)旋轉(zhuǎn)了90度的問題,包括 iPhone 和個(gè)別三星手機(jī)人断。這些手機(jī)豎著拍的時(shí)候才會(huì)出現(xiàn)這種問題吭从,橫拍出來的照片就正常顯示。因此恶迈,可以通過獲取手機(jī)拍照方向來對(duì)照片進(jìn)行旋轉(zhuǎn)涩金,從而解決這個(gè)問題谱醇。

Orientation

orientation,英文翻譯為方向步做,這個(gè)參數(shù)不是所有圖片都有副渴,但手機(jī)拍攝的照片是有的,所以我們需要在處理時(shí)判斷一下全度。

旋轉(zhuǎn)角度 參數(shù)值
1
順時(shí)針90° 6
逆時(shí)針90° 8
180° 3

參數(shù)為 1 的時(shí)候顯示正常煮剧,即手機(jī)橫拍顯示正常,Orientation = 1 将鸵,而手機(jī)豎拍的參數(shù)為 6勉盅。

想要獲取 Orientation 參數(shù),可以通過 exif.js 庫來操作顶掉。exif.js 功能很多草娜,體積也很大,未壓縮之前足足有 30k痒筒,這對(duì)手機(jī)頁面加載來說是非常大影響的宰闰。而我只需要獲取 Orientation 信息而已,所以這里使用原文作者精簡后exif.js 庫凸克,將代碼縮小到6KB议蟆。

exif.js 獲取 Orientation :

EXIF.getData(file, function() {  
    var Orientation = EXIF.getTag(this, 'Orientation');
});

其中file是通過input標(biāo)簽獲取的圖片闷沥,比如var file = $('#input').props.files[0]萎战,而其中exif.js庫的原理是把file通過FileReader對(duì)象讀取出來,然后使用DataView對(duì)象轉(zhuǎn)換成二進(jìn)制數(shù)組的格式舆逃,然后不斷讀取二進(jìn)制數(shù)據(jù)的字節(jié)信息蚂维,判斷出圖片的方向,返回一個(gè)封裝好的orientation值路狮。

旋轉(zhuǎn)

獲取到方向后虫啥,我們就要把圖片旋轉(zhuǎn)了,可能你會(huì)想到直接操作CSS的transfer rotate方法奄妨,但對(duì)于加了預(yù)加載功能的圖片涂籽,這樣會(huì)在界面上看到一個(gè)旋轉(zhuǎn)的動(dòng)畫,實(shí)在不優(yōu)雅砸抛。所以我們使用canvas來操作评雌,還能加入壓縮的功能,使圖片加載更快直焙。

ctx.rotate(angle)景东,rotate 方法的參數(shù)為旋轉(zhuǎn)弧度。需要將角度轉(zhuǎn)為弧度:degrees * Math.PI / 180奔誓,旋轉(zhuǎn)的中心點(diǎn)默認(rèn)都在 canvas 的起點(diǎn)斤吐,即 ( 0, 0 )。旋轉(zhuǎn)的原理如下圖:

旋轉(zhuǎn)之后,如果從 ( 0, 0 ) 點(diǎn)進(jìn)行 drawImage()和措,那么畫出來的位置就是在左圖中的旋轉(zhuǎn) 90 度后的位置庄呈,這時(shí)視圖不在可視區(qū)域了,而旋轉(zhuǎn)之后派阱,坐標(biāo)軸也跟著旋轉(zhuǎn)了抒痒,為了顯示在可視區(qū)域,需要將圖片點(diǎn)往 y 軸的反方向移 y 個(gè)單位颁褂,此時(shí)的起始點(diǎn)則為 ( 0, -y )故响。

同理,可以獲得旋轉(zhuǎn) -90 度后的起始點(diǎn)為 ( -x, 0 )颁独,旋轉(zhuǎn) 180 度后的起始點(diǎn)為 ( -x, -y )彩届。

壓縮

手機(jī)拍出來的照片太大,而且使用 base64 編碼的照片會(huì)比原照片大誓酒,那么在進(jìn)行預(yù)覽的時(shí)候進(jìn)行壓縮是有必要的≌寥洌現(xiàn)在的手機(jī)像素這么高,拍出來的照片寬高都有幾千像素靠柑,用 canvas 來渲染這照片的速度會(huì)相對(duì)比較慢啦桌。

因此第一步需要先對(duì)上傳照片的寬高做限制,判斷寬度或高度是否超出哪個(gè)范圍搔体,則等比壓縮其寬高剧腻。

var ratio = width / height;
if(imgWidth > imgHeight && imgWidth > xx){
    imgWidth = xx;
    imgHeight = Math.ceil(xx / ratio);
}else if(imgWidth < imgHeight && imgHeight > yy){
    imgWidth = Math.ceil(yy * ratio);
    imgHeight = yy;
}

第二步就通過 canvas.toDataURL() 方法來壓縮照片質(zhì)量。

canvas.toDataURL("image/jpeg", 1);

toDataURL() 方法返回一個(gè)包含圖片展示的 data URI 隔嫡。使用兩個(gè)參數(shù)甸怕,第一個(gè)參數(shù)為圖片格式,默認(rèn)為 image/png腮恩。第二個(gè)參數(shù)為壓縮質(zhì)量梢杭,在指定圖片格式為 image/jpeg 或 image/webp的情況下,可以從 0 到 1 的區(qū)間內(nèi)選擇圖片的質(zhì)量秸滴。

核心代碼

<input type="file" id="files" >
<img src="blank.gif" id="preview">
<script src="small-exif.js"></script>
<script>
var ipt = document.getElementById('files'),
    img = document.getElementById('preview');

ipt.onchange = function (e) {
    var file = e.target.files[0];

    if(file){
        EXIF.getData(file, function() {
            // 先獲取方向
            var Orientation = EXIF.getTag(this, 'Orientation');

            // 因?yàn)槭窃诨卣{(diào)函數(shù)里設(shè)置orientation的值武契,所以要在回調(diào)成功后再讀取圖片,保持同步
            var reader = new FileReader(),
                image = new Image();

            reader.onload = function (ev) {// 讀取文件
                image.onload = function () {// 加載照片
                    var imgWidth = this.width,
                        imgHeight = this.height;
                    // 控制上傳圖片的寬高
                    if(imgWidth > imgHeight && imgWidth > 750){
                        imgWidth = 750;
                        imgHeight = Math.ceil(750 * this.height / this.width);
                    }else if(imgWidth < imgHeight && imgHeight > 1334){
                        imgWidth = Math.ceil(1334 * this.width / this.height);
                        imgHeight = 1334;
                    }

                    var canvas = document.createElement("canvas"),
                        ctx = canvas.getContext('2d');
                    canvas.width = imgWidth;
                    canvas.height = imgHeight;

                    if(Orientation && Orientation != 1){
                        switch(Orientation){
                            case 6:     // 旋轉(zhuǎn)90度
                                canvas.width = imgHeight;
                                canvas.height = imgWidth;
                                ctx.rotate(Math.PI / 2);
                                ctx.drawImage(this, 0, -imgHeight, imgWidth, imgHeight);
                                break;
                            case 3:     // 旋轉(zhuǎn)180度
                                ctx.rotate(Math.PI);
                                ctx.drawImage(this, -imgWidth, -imgHeight, imgWidth, imgHeight);
                                break;
                            case 8:     // 旋轉(zhuǎn)-90度
                                canvas.width = imgHeight;
                                canvas.height = imgWidth;
                                ctx.rotate(3 * Math.PI / 2);
                                ctx.drawImage(this, -imgWidth, 0, imgWidth, imgHeight);
                                break;
                        }
                    }else{
                        ctx.drawImage(this, 0, 0, imgWidth, imgHeight);
                    }
                    img.src = canvas.toDataURL("image/jpeg", 0.8);
                };
                image.src = ev.target.result;// 這里一定要保證代碼的順序荡含,必須先創(chuàng)建image.onload咒唆,否則可能圖片一下就加載完了
            };
            reader.readAsDataURL(file);// 這里一定要保證代碼的順序,必須先創(chuàng)建reader.onload内颗,否則可能文件一下就讀取完了
        });
    }
}
</script>
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末钧排,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子均澳,更是在濱河造成了極大的恐慌恨溜,老刑警劉巖符衔,帶你破解...
    沈念sama閱讀 207,248評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異糟袁,居然都是意外死亡判族,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,681評(píng)論 2 381
  • 文/潘曉璐 我一進(jìn)店門项戴,熙熙樓的掌柜王于貴愁眉苦臉地迎上來形帮,“玉大人,你說我怎么就攤上這事周叮”绯牛” “怎么了?”我有些...
    開封第一講書人閱讀 153,443評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵仿耽,是天一觀的道長合冀。 經(jīng)常有香客問我,道長项贺,這世上最難降的妖魔是什么君躺? 我笑而不...
    開封第一講書人閱讀 55,475評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮开缎,結(jié)果婚禮上棕叫,老公的妹妹穿的比我還像新娘。我一直安慰自己奕删,他們只是感情好俺泣,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,458評(píng)論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著急侥,像睡著了一般砌滞。 火紅的嫁衣襯著肌膚如雪侮邀。 梳的紋絲不亂的頭發(fā)上坏怪,一...
    開封第一講書人閱讀 49,185評(píng)論 1 284
  • 那天,我揣著相機(jī)與錄音绊茧,去河邊找鬼铝宵。 笑死,一個(gè)胖子當(dāng)著我的面吹牛华畏,可吹牛的內(nèi)容都是我干的鹏秋。 我是一名探鬼主播,決...
    沈念sama閱讀 38,451評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼亡笑,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼侣夷!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起仑乌,我...
    開封第一講書人閱讀 37,112評(píng)論 0 261
  • 序言:老撾萬榮一對(duì)情侶失蹤百拓,失蹤者是張志新(化名)和其女友劉穎琴锭,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體衙传,經(jīng)...
    沈念sama閱讀 43,609評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡决帖,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,083評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了蓖捶。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片地回。...
    茶點(diǎn)故事閱讀 38,163評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖俊鱼,靈堂內(nèi)的尸體忽然破棺而出刻像,到底是詐尸還是另有隱情,我是刑警寧澤并闲,帶...
    沈念sama閱讀 33,803評(píng)論 4 323
  • 正文 年R本政府宣布绎速,位于F島的核電站,受9級(jí)特大地震影響焙蚓,放射性物質(zhì)發(fā)生泄漏纹冤。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,357評(píng)論 3 307
  • 文/蒙蒙 一购公、第九天 我趴在偏房一處隱蔽的房頂上張望萌京。 院中可真熱鬧,春花似錦宏浩、人聲如沸知残。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,357評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽求妹。三九已至,卻和暖如春佳窑,著一層夾襖步出監(jiān)牢的瞬間制恍,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,590評(píng)論 1 261
  • 我被黑心中介騙來泰國打工神凑, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留净神,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,636評(píng)論 2 355
  • 正文 我出身青樓溉委,卻偏偏與公主長得像鹃唯,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子瓣喊,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,925評(píng)論 2 344

推薦閱讀更多精彩內(nèi)容