H5頁(yè)面圖片裁剪

前言: 前陣子做中再保險(xiǎn)的項(xiàng)目,客戶提到一個(gè)需求需要開發(fā)一個(gè)在線頁(yè)面嵌套到別的供應(yīng)商產(chǎn)品中,其中有上傳報(bào)銷單和裁剪的功能(雖然裁剪最后pass掉了)罐呼,這里說(shuō)一說(shuō)關(guān)于圖片裁剪遇到的一些坑罩锐。
首先由于是嵌套在別的系統(tǒng)中求泰,一些原生的插件就不可能用了芝加。于是本圖片裁剪功能就基于canvas自己用畫布裁出來(lái)硅卢,并上傳。

1 要裁剪首先肯定就是讀取圖片文件藏杖。由于沒有cordova插件可用将塑,用一個(gè)input指定為file類型就可以了。

<input type="file" class="file" accept="image/*;capture=camera" name="img" @change="clipImg($event)">

// Vue里面的methods
clipImg(event){
   this.clip = new Clip('container',this);
   this.clip.init(event.target.files[0]);
   this.isClip = true;
   document.body.addEventListener('touchmove',this.noScoll,false);
},

接著蝌麸,處理所獲取的文件:
根據(jù)FileReader創(chuàng)建一個(gè)實(shí)例点寥,然后把讀得的文件用readAsDataURL轉(zhuǎn)換為base64編碼,再通過paintImg方法進(jìn)行繪制處理来吩。需要注意的是文件讀取是異步的敢辩,所以讀取后的操作需要在onload函數(shù)中進(jìn)行蔽莱。

//這些方法是寫在Clip類里面的
handleFiles(file){
    let t = this;
    let reader = new FileReader();
    reader.readAsDataURL(file);
    reader.onload = function() {
        t.imgUrl = this.result;
        t.paintImg(this.result);
    }
}

2 繪制裁剪界面

這一步的繪制的大小尺寸可根據(jù)個(gè)人需求而定,首先我們初始化一些基本參數(shù)戚长,這里我們暫且設(shè)定比例為233:175盗冷。

//初始化
init(file){
    this.sx = 0; //裁剪框的初始x
    this.sy = 0; //裁剪框的初始y
    this.sWidth = 233; //裁剪框的寬
    this.sHeight = 175; //裁剪框的高 
    this.chooseBoxScale = 233/175;//裁剪框的寬高比

    this.handleFiles(file);
}

下面這一段代碼稍微有點(diǎn)長(zhǎng),我們先確認(rèn)三個(gè)元素:

1 裁剪界面中同廉,首先有一個(gè)最外層的容器仪糖,裝著圖片,即id為container的div
2 然后是圖片容器迫肖,即id為image-box的canvas (裁剪選中的部分)
3 被裁剪掏空的部分锅劝,即id為cover-box的canvas(被裁剪舍棄的部分)

這幾個(gè)容器中,1的寬高是固定的蟆湖。而2則在保證比例不變的情況下有一邊占滿整個(gè)1故爵。所以可以看到上面那段判斷盒子與圖片比列的代碼是在實(shí)現(xiàn)這個(gè)顯示方式。
同時(shí)可以看到3的裁剪區(qū)域也是同理的隅津,在保證比例不變的情況下有一邊占滿整個(gè)2稠集。亦即裁剪區(qū)域的比例是在事先就設(shè)計(jì)好的,在這里是4:3饥瓷。
所以我在效果圖中將高填滿容器和寬填滿容器的圖片都演示了一遍剥纷。

paintImg(picUrl){
    //other code
    //.....
    img.onload = function() {

        let imgScale = img.width / img.height;
        let boxScale = t.regional.offsetWidth / t.regional.offsetHeight;

        //判斷盒子與圖片的比列
        if (imgScale < boxScale) {
            //設(shè)置圖片的像素
            t.imgWidth = t.regional.offsetHeight * imgScale;
            t.imgHeight = t.regional.offsetHeight;
        } else {
            //設(shè)置圖片的像素
            t.imgWidth = t.regional.offsetWidth;
            t.imgHeight = t.regional.offsetWidth / imgScale;
        }

        //判斷圖片與選擇框的比例大小,作出裁剪
        if (imgScale < t.chooseBoxScale) {
            //設(shè)置選擇框的像素
            t.sWidth = t.imgWidth;
            t.sHeight = t.imgWidth / t.chooseBoxScale;

            //設(shè)置初始框的位置
            t.sx = 0;
            t.sy = (t.imgHeight - t.sHeight) / 2;
        } else {
            //設(shè)置選擇框的像素
            t.sWidth = t.imgHeight * t.chooseBoxScale;
            t.sHeight = t.imgHeight;

            t.sx = (t.imgWidth - t.sWidth) / 2;
            t.sy = 0;
        }
        //(1)
    }

3 防止高分屏下出現(xiàn)圖片模糊的情況呢铆,做以下處理

第一個(gè)是高分屏下圖片模糊的問題晦鞋,在高分屏下用canvas繪制某些圖片是會(huì)出現(xiàn)模糊,估計(jì)是和canvas的繪制機(jī)制有關(guān)棺克,具體原因戳這里悠垛。解決辦法也比較簡(jiǎn)單,將canvas的css寬高固定娜谊,容器寬高擴(kuò)大兩倍确买。(我的理解,css寬高就是canvas標(biāo)簽style樣式設(shè)置的寬高纱皆,容器寬高就是里面那個(gè)畫板的寬高湾趾,不是同一個(gè)東西)經(jīng)過這樣的處理后,絕大多數(shù)圖片的模糊問題解決了派草。

第二個(gè)是圖片繪制壓縮問題搀缠,在低版本的ios機(jī)型下繪制1m多以上的圖片時(shí)會(huì)出現(xiàn)壓縮,翻轉(zhuǎn)等問題近迁,詳情及解決辦法戳這里艺普。我上面就是用了一個(gè)stackflow里面的fix方法。

 //續(xù)上面(1)
    
    //高分屏下圖片模糊,需要2倍處理
    t.getImage.height = 2 * t.imgHeight;
    t.getImage.width = 2 * t.imgWidth;
    t.getImage.style.width = t.imgWidth + 'px';
    t.getImage.style.height = t.imgHeight + 'px';
    
    let vertSquashRatio = t.detectVerticalSquash(img);
    
    cxt.drawImage(img, 0, 0,2 * t.imgWidth * vertSquashRatio, 2 * t.imgHeight * vertSquashRatio)
    
    t.cutImage();
    t.drag();

4 移動(dòng)的時(shí)候繪制

這里主要記錄裁剪的區(qū)域歧譬,首先確定起始位置岸浑,然后判斷左右或者上下移動(dòng),對(duì)應(yīng)的高度填滿容器高度和寬度瑰步,在根據(jù)pageX和pageY判斷位置即可助琐。
drag(){
let t = this;
let draging = false;

    //記錄初始點(diǎn)擊的pageX,pageY面氓。用于記錄位移
    let pageX = 0;
    let pageY = 0;

    //初始位移
    let startX = 0;
    let startY = 0;
    t.editBox.addEventListener('touchmove', function(ev) {
        let e = ev.touches[0];
        let offsetX = e.pageX - pageX;
        let offsetY = e.pageY - pageY;
        if (draging) {
            if (t.imgHeight == t.sHeight) {
                t.sx = startX + offsetX;
                if (t.sx <= 0) {
                    t.sx = 0;
                } else if (t.sx >= t.imgWidth - t.sWidth) {
                    t.sx = t.imgWidth - t.sWidth;
                }
            } else {
                t.sy = startY + offsetY;
                if (t.sy <= 0) {
                    t.sy = 0;
                } else if (t.sy >= t.imgHeight - t.sHeight) {
                    t.sy = t.imgHeight - t.sHeight;
                }
            }
            t.cutImage();
        }
    });
    t.editBox.addEventListener('touchstart', function(ev) {
        let e = ev.touches[0];
        draging = true;
        pageX = e.pageX;
        pageY = e.pageY;
        startX = t.sx;
        startY = t.sy;
    })
    t.editBox.addEventListener('touchend', function() {
        draging = false;
    })
}

5 將裁剪的區(qū)域保存起來(lái)

裁剪框那里記錄了裁剪開始及結(jié)束的坐標(biāo)兵钮,然后新建一個(gè)canvas裁出來(lái),并用toDataURL方法轉(zhuǎn)換為base64編碼舌界,就可以傳輸?shù)胶笈_(tái)了掘譬。我這里裁剪后的尺寸是固定的,這個(gè)是根據(jù)當(dāng)初的初定業(yè)務(wù)需求做的呻拌,有需要的可以根據(jù)需要修改葱轩。

save(){
       let t = this;
       let saveCanvas = document.createElement('canvas');
       let ctx = saveCanvas.getContext('2d');

       //圖片裁剪后的尺寸
       saveCanvas.width = 466;
       saveCanvas.height = 350;

       let images = new Image();
       images.src = t.imgUrl;

       images.onload = function(){

           //計(jì)算裁剪尺寸比例,用于裁剪圖片
           let cropWidthScale = images.width/t.imgWidth;
           let cropHeightScale = images.height/t.imgHeight;

           t.drawImageIOSFix(ctx, images,cropWidthScale * t.sx , cropHeightScale* t.sy,
                           t.sWidth * cropWidthScale, t.sHeight * cropHeightScale, 0, 0, 466, 350);
       //    ctx.drawImage(images,2 * t.sx, 2 * t.sy, t.sWidth * 2, t.sHeight * 2, 0, 0, 466, 350);
           t.$vm.clipUrl = saveCanvas.toDataURL();
           t.regional.removeChild(t.getImage);
           t.regional.removeChild(t.editBox);
       }
   }

最后可將保存的裁剪部分通過base64活其他格式傳輸?shù)胶笈_(tái)藐握!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末靴拱,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子猾普,更是在濱河造成了極大的恐慌袜炕,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,036評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件初家,死亡現(xiàn)場(chǎng)離奇詭異偎窘,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)溜在,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,046評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門陌知,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人掖肋,你說(shuō)我怎么就攤上這事仆葡。” “怎么了志笼?”我有些...
    開封第一講書人閱讀 164,411評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵沿盅,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我籽腕,道長(zhǎng)嗡呼,這世上最難降的妖魔是什么纸俭? 我笑而不...
    開封第一講書人閱讀 58,622評(píng)論 1 293
  • 正文 為了忘掉前任皇耗,我火速辦了婚禮,結(jié)果婚禮上揍很,老公的妹妹穿的比我還像新娘郎楼。我一直安慰自己万伤,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,661評(píng)論 6 392
  • 文/花漫 我一把揭開白布呜袁。 她就那樣靜靜地躺著敌买,像睡著了一般。 火紅的嫁衣襯著肌膚如雪阶界。 梳的紋絲不亂的頭發(fā)上虹钮,一...
    開封第一講書人閱讀 51,521評(píng)論 1 304
  • 那天,我揣著相機(jī)與錄音膘融,去河邊找鬼芙粱。 笑死,一個(gè)胖子當(dāng)著我的面吹牛氧映,可吹牛的內(nèi)容都是我干的春畔。 我是一名探鬼主播,決...
    沈念sama閱讀 40,288評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼岛都,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼律姨!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起臼疫,我...
    開封第一講書人閱讀 39,200評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤择份,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后烫堤,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體缓淹,經(jīng)...
    沈念sama閱讀 45,644評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,837評(píng)論 3 336
  • 正文 我和宋清朗相戀三年塔逃,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了讯壶。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,953評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡湾盗,死狀恐怖伏蚊,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情格粪,我是刑警寧澤躏吊,帶...
    沈念sama閱讀 35,673評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站帐萎,受9級(jí)特大地震影響比伏,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜疆导,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,281評(píng)論 3 329
  • 文/蒙蒙 一赁项、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦悠菜、人聲如沸舰攒。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,889評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)摩窃。三九已至,卻和暖如春芬骄,著一層夾襖步出監(jiān)牢的瞬間猾愿,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,011評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工账阻, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留匪蟀,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,119評(píng)論 3 370
  • 正文 我出身青樓宰僧,卻偏偏與公主長(zhǎng)得像材彪,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子琴儿,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,901評(píng)論 2 355

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

  • 前陣子七夕的時(shí)候搞了一個(gè)h5活動(dòng)頁(yè)段化,需要做一個(gè)本地圖片的裁剪上傳功能,用于生成一些特定的表白圖片造成。主要是用到了H5...
    Bless_L閱讀 6,278評(píng)論 1 14
  • canvas元素的基礎(chǔ)知識(shí) 在頁(yè)面上放置一個(gè)canvas元素显熏,就相當(dāng)于在頁(yè)面上放置了一塊畫布,可以在其中進(jìn)行圖形的...
    oWSQo閱讀 10,294評(píng)論 0 19
  • 一晒屎、需求 最近做了一個(gè)H5頁(yè)面喘蟆,大概內(nèi)容是:用戶進(jìn)入H5頁(yè)面后做一些測(cè)試題,答完測(cè)試題后生成一個(gè)結(jié)果鼓鲁,將這些結(jié)果生...
    小奵貓閱讀 40,266評(píng)論 10 28
  • 1 CALayer IOS SDK詳解之CALayer(一) http://doc.okbase.net/Hell...
    Kevin_Junbaozi閱讀 5,150評(píng)論 3 23
  • Swift1> Swift和OC的區(qū)別1.1> Swift沒有地址/指針的概念1.2> 泛型1.3> 類型嚴(yán)謹(jǐn) 對(duì)...
    cosWriter閱讀 11,101評(píng)論 1 32