zoukankan      html  css  js  c++  java
  • 利用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,到这,都写完了,希望对你有帮助.

     

  • 相关阅读:
    298. Binary Tree Longest Consecutive Sequence
    117. Populating Next Right Pointers in Each Node II
    116. Populating Next Right Pointers in Each Node
    163. Missing Ranges
    336. Palindrome Pairs
    727. Minimum Window Subsequence
    211. Add and Search Word
    年底购物狂欢,移动支付安全不容忽视
    成为程序员前需要做的10件事
    全球首推iOS应用防破解技术!
  • 原文地址:https://www.cnblogs.com/zhiyuan-2011/p/4236371.html
Copyright © 2011-2022 走看看