利用canvas制作圖片(可縮放和平移)+相框+文字


前言:

  公司一個售前問我能不能用H5做一個手機拍照,給相片添加相框和添加文字上傳到服務器的功能,我當時一琢磨覺得可行,就利用空余時間做了一個demo,去掉了拍照和上傳,如果以后有機會,會給補上,當然對於開發過webApp的朋友來做到這個很簡單。下面來看代碼

1,思路

 首先我們需要准備素材,一個相框和一個相片,然后我們要思考,只是把他們和並且就可以了嗎?答案是否定的,我還需要對照片進行編輯,比如平移和縮放等。還要可以切換相框。

2,如何合並相框和圖片?

  

上面是我的界面,從界面可以看出,我有三張圖片和兩個相框,最右邊是相框和圖片合並之后的結果。看代碼:

html:

    <style> body,html,ul,li,h1,h5,a,p,span,div{ margin: 0px; padding: 0px;
        } ul,li{ list-style: none;
        } .wrap{ position: relative; margin:40px; width: 1000px; height: 400px; background-color: #fff; box-shadow: 0px 0px 1px 1px #eee;
        } .imgWrap, .photoWrap{ width: 100px; height: 380px; float: left; border: 1px solid #ccc; margin:10px 40px 10px 40px; text-align: center;
        } .imgWrap img{ width: 60px; height: 100px;
        } .photoWrap img{ width: 90px; height: 140px;
        } .photoWrap_canvas{ position: absolute;
            /*opacity: 0.4;*/ width: 300px; left: 365px; height: 400px;
            /*background-color: red;*/
        }
    </style>

    <div class="wrap">
        <div class="photoWrap_canvas" id="photoWrap_canvas" >
        
        </div>
        <canvas id="myCanvas" width="300" height="400" style="background-color:#ccc"></canvas>
        <div class="photoWrap" id="photoWrap">
            <ul>
                <li><img src="img/xk1.png" alt=""></li>
                <li><img src="img/xk2.png" alt=""></li>
            </ul>
        </div>
        <div class="imgWrap">
            <ul>
                <li><img src="img/mm1.jpg" alt=""></li>
                <li><img src="img/mm2.jpg" alt=""></li>
                <li><img src="img/mm3.jpg" alt=""></li>
            </ul>
        </div>
        <div>
            <input type="text" id="txtKey">
            <button id="btnAddFont">輸入</button>
            <button id="btnOk">完成</button>
            <button id="btnReset">還原</button>
        </div>
    </div>
View Code

js:

            var _ = function(selector){return document.getElementById(selector);} var util = { isNull:function(str){return str == null || str.length == 0 } } var c = _("myCanvas"); var ctx = c.getContext("2d"); var ctxW = c.width, ctxH = c.height; var img = new Image(); img.src = "img/mm2.jpg"; var imgW,imgH; img.onload=function(){ imgW = 300 || img.width, imgH = 400 || img.height; onDraw(); ctx.save(); } function onDraw(){ ctx.drawImage(img,0,0,300,400); } // 切換相冊
            $(".photoWrap").delegate("ul li img","click",function(){ var src = $(this).attr("src"); $(".photoWrap_canvas").css("background","url("+src+") no-repeat").css("background-size","100% 100%").attr("data-url",src); })

 

看到上面的代碼,很簡單,

  1,獲取一個canvas標簽,然后利用getContext("2d")獲取到一個畫布對象;

  2,創建一個image對象,給它添加src屬性

  3,圖片加載完畢使用 drawImage()把img對象畫到canvas里面,

  4,再給div.photoWrap的div添加背景圖片,(通過給canvas添加一個透明的蒙層可以做一個切換相框的效果)

3,如何平移圖片?

  

以上效果,通過移動鼠標來設置圖片的位置。看代碼:

      // 移動相冊的動作
            var hasTouch = 'ontouchstart' in window; // console.log(window);
            var STA_EN = hasTouch ? "touchstart" : "mousedown", MV_EV = hasTouch ? "touchmove":"mousemove", END_EV = hasTouch ? "touchend" : "mouseup", END_Cancel = hasTouch ? "touchcancel" : "mouseout"; _("photoWrap_canvas").addEventListener(STA_EN,start,false); _("photoWrap_canvas").addEventListener(MV_EV,move,false); _("photoWrap_canvas").addEventListener(END_EV,end,false); _("photoWrap_canvas").addEventListener(END_EV,end,false); var bStart = 0; var bginX,bginY,startX = 0,startY = 0; var offsetX_ctx = 0,offsetY_ctx = 0; function start(ev){ // console.log("32")
 ev.preventDefault(); bStart = 1; var poi = getEvtLocation(ev); // console.log(poi.x,poi.y);
                bginX = poi.x; bginY = poi.y; } function move(ev){ ev.preventDefault(); if(bStart === 0)return; var poi = getEvtLocation(ev); var offsetX = poi.x - bginX, offsetY = poi.y - bginY; ctx.translate(offsetX,offsetY); onDraw(); bginX = poi.x; bginY = poi.y; } function end (ev) { // body...
 ev.preventDefault(); bStart = 0;
 } function getEvtLocation(ev){ if(util.isNull(ev)) return; // var touch = ev.touches[0];

                return{ x : ev.offsetX, y : ev.offsetY } }

上面代碼做了如下動作:

  1,首先給div.photoWrap_canvas監聽鼠標事件,有些人可能會問了,為什么要給div注冊事件呢?我們操作的可是 canvas啊,剛剛我們說過我們為了能達到隨意的切換相框的效果就給canvas上面蒙一個層,這樣的話,我們可能就沒法直接給canvas添加事件,希望大家理解。

  2,在鼠標剛開始點擊的時候執行 start()方法,他記住鼠標在div內部起點的位置,並告訴瀏覽器我們可以滑動了(bStart = 1)。

  3,在鼠標移動的時候會先判斷 是否執行過 start(),如果有的話,計算移動位置和開始位置的偏移值,然后賦值給 translate()方法。圖片就可移動了

  4,結束移動的時候,可以設置bStart = 0,等待下次啟動

4,如何消除移動痕跡?

  以上講述的能讓我們的圖片隨之鼠標的移動而移動,不過在移動的過程中,我們會發現,移動方向的相反位置會留下,很多痕跡。

上面的圖片可以看到,很多痕跡,就像電腦卡頓住了一樣,,那如何去除呢?

我們想明白造成這樣的原因是什么?隨着我們的移動,圖片的位置也在做相應的改變,注意,我們調用drawImage()的時候,我是畫上去,就像畫畫一樣,當然有痕跡,幸好,canvas對象也給我們提供了清除的方法 clearRect()--清除矩形,沒錯使用這個方法,我們就能清除痕跡了。

讓我們重寫onDraw()方法:

            function onDraw(){ ctx.clearRect(- ctxW,-ctxH, 2 * ctxW, 2 * ctxH); ctx.drawImage(img,0,0,300,400); }

重寫之后的效果:

但是,當我們想左移動的時候會發現,還是會有痕跡,怎么回事呢?

在已上代碼是指,在圖片右下角的相反方向,清除一個是當前canvas大小2的范圍。(在這里需要大家注意一點,外面的那個框是 c 這個dome元素,而我們現在對canvas做的任何操作,都是有 c 這個元素生成的 ctx對象上,所以,圖像移動,那么圖片的右下角也移動,當然它本身還是圖片的右下角)其實不難理解,canvas初始化的時候,ctxW和ctxH這個就作為畫布的右下角,為什么要用負號呢,因為clearRect(x,y,width,height)這個方法的使用是以(x,y)為起點,向右清除一個矩形范圍的,注意是向右,那我們肯定是不能向右的(再向右的話,就什么也沒有了),當然向左也不行,那怎么辦呢?

在這里呢,我也通過查找博文找到了方案,就是我們用圖片的的中心點,當然我們canvas的原點,什么意思呢?讓我們來修改下代碼:

            img.onload=function(){ imgW = 300 || img.width, imgH = 400 || img.height; // 把canvas的原點設置為圖片的中心點,但是現實的時候,要還原,否則圖片會已左上角釘在canvas的中心點上
                ctx.translate(imgW /2,imgH/2); onDraw(); ctx.save(); } 

  以上代碼,修改了圖片剛加載完畢之后,我們做的操作,就是給canvas先平移下位置,平移的位置就是圖片大小的中心點上,這就完成了圖片中心點偏移。

 偏移之后,我們頁面剛開始的時候,不能就這么顯示對吧?不然會被客戶罵的,那我們要進行顯示還原。

            function onDraw(){ // 這是清除圖片因為平移而造成的痕跡,-ctxw是圖片平移的反方向的位置,2*ctxW,是清除的面積
                ctx.clearRect(- ctxW,-ctxH, 2 * ctxW, 2 * ctxH); // -imgW/2 是為了讓圖片顯示的回復正常,因為上面顯示的時候做了旋轉
                ctx.drawImage(img,-imgW / 2, -imgH / 2,300,400); }

代碼:drawImage()的時候,(x,y)的值,本來是(0,0)的,也就是canvas的左上角,但是,我們把原點偏移了,通過顯示的時候把這個偏移量給補回來就好了,所有就是(-imgW/2,-imgH/2);

好了,寫到這兒,我們這個功能算是完成了一半,我們實現了相框的切換和圖片的平移,下面就是圖的縮放和圖片合並了。

5,圖片縮放

            // 注冊鼠標滾輪事件,,暫時只做除去 firfox之外的瀏覽器
            window.onmousewheel = document.onmousewheel = scrollFnc; var scale = 1; function scrollFnc(ev){ var delta = ev.wheelDelta; if(delta > 0 && scale <= 5){    //滾輪向上
                    scale += 0.1; }else if(delta < 0 && scale >= 1){ scale -= 0.2; } ctx.scale(scale,scale); onDraw(); }

注意,我們這邊縮放是通過鼠標的滾輪來做的,當然了,如果使用手機來的話,那就需要手勢的操作了,通過計算兩個手指或則三個手指之間的距離來進行縮放操作,以后我會補上,現在先這么做吧。

  代碼很簡單,獲取鼠標在元素上滾輪的動作如果是負數就縮小,正數就放大,scale(w,h),放大縮小的函數。

6,圖片和相框的合並

  我們可以進行圖片的縮放和平移了,那么下步就是把圖片和相框進行合並,我剛才說了,為了能夠隨意的切換相框,那我就先用div的背景來做,並不是直接加入到canvas中,那樣會很麻煩。

 

// 完成
            var newxCtx; $("#btnOk").click(function(){ newCanvas = document.createElement('canvas'); newCanvas.width = 300; newCanvas.height = 400; newxCtx = newCanvas.getContext("2d"); var img = new Image(); img.src = $(".photoWrap_canvas").attr("data-url"); img.onload=function(){ $(".photoWrap_canvas").hide(); // var x = (-ctxW / 2) - offsetX_ctx,
                    // y = (-ctxH / 2) - offsetY_ctx;
                    // console.log(offsetX_ctx,offsetY_ctx);
                    newxCtx.drawImage(c,0,0); newxCtx.drawImage(img,0,0,300,400);  //-ctxW /2, -ctxH / 2
 $(".wrap").prepend(newCanvas); } })

其實大家看看到了,我是把相框和canvas(圖片)再重新drawImage到一個新的canvas里面去的,那為什么這么做呢?直接把相框draw到原有的canvas不久行了嗎?

我先給大家看下如果這么做的效果是什么:

可以明顯的看出,相框隨着圖片的小而小,位置也發生了變化,當然了,位置我們可以給他設置偏移,但是大小卻不一定,至少,我還沒有找到方法,那我就是想到了一個辦法,再創建一個canvas這個專門為相框而做的,然后把做好的相片在畫到相框這個canvas里面就可以了。

7,寫入文字

// 添加文字
            $("#btnAddFont").click(function(){ var val = $("#txtKey").val(); if(val == null || val.length == 0) return; if(util.isNull(newxCtx)) return; newxCtx.font="40px Arial"; newxCtx.fillText(val,50,100); })

寫入文字很簡單,對不對,得到想要的之,使用 fillText()就可以了,,,

OK,到這,都寫完了,希望對你有幫助.

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM