Canvas实现 图片裁剪、移动、放大缩小以及图片的合成

Canvas实现 图片裁剪、移动、放大缩小以及图片的合成

前段时间项目上需要做一个合成图片的小活动,参考了很多案例,发现还是用canvas最为简便。

目录

  • Canvas实现 图片裁剪移动放大缩小以及图片的合成
    • 目录
    • 图片绘制ctxdrawImageimagesxsyswshdxdydwdh
    • 图片合成canvastoDataURLtype encoderOptions
      • 什么是 被污染的tainted canvas
      • 代码块
      • 代码解析
    • 后记

这个活动主要是做一个合影功能,和以前咱们玩的大头贴有点像,哈哈哈。

代码主要是这几部分:
- 确定容器宽高度,给canvas赋值
- 把背景图拽入画布
- 上传图片或拍照,并拽入画布
- 移动图片、或缩小
- 合成图片


图片绘制:ctx.drawImage(image,sx,sy,sw,sh,dx,dy,dw,dh)

接收9个参数,接受值 都是像素,也可缩写成 drawImage(image,dx,dy) 或 drawImage(image, dx,dy,dw,dh)

  • image image object
  • sx 原图x坐标(裁剪)
  • sy原图y坐标(裁剪)
  • sw原图宽度(裁剪)
  • sh 原图高度(裁剪)
  • dx 绘制图片在画布上的x坐标
  • dy 绘制图片在画布上的y坐标
  • dw 绘制图片的宽度
  • dh 绘制图片的高度
    Canvas实现图片裁剪、移动、放大缩小以及图片的合成_第1张图片
    每个参数的样例,图示会比较清晰

其实根据这个图片示例,很容易可以看出,
- sx sy sw sh这几个参数决定了图片的裁剪
- 改变 dx dy 可以移动图片
- 改变 dw dh 可以放大或缩小图片(dWidth > sWidth && dHeight > sHeight 绘制的是放大的图片,dWidth < sWidth && dHeight < sHeight 绘制的是缩小的图片)

原理就是这么简单。

值得注意的是,当绘制多张图片时,先绘制的图片在下层,后绘制的图片在上层。

图片合成:canvas.toDataURL(type, encoderOptions)


  • type (可选)图片格式,默认为 image/png
  • encoderOptions (可选)图片质量。

在指定图片格式为 image/jpeg 或 image/webp的情况下,可以从 0 到 1 的区间内选择图片的质量。
如果超出取值范围,将会使用默认值 0.92。其他参数会被忽略。

什么是 “被污染的(tainted)” canvas?

尽管没有CORS授权也可以在 canvas 中使用图像, 但这样做就会污染(taints)画布。
只要 canvas 被污染, 就不能再从画布中提取数据, 也就是说不能再调用 toBlob(), toDataURL() 和 getImageData() 等方法, 否则会抛出安全错误(security error).
这实际上是为了保护用户的个人信息,避免未经许可就从远程web站点加载用户的图像信息,造成隐私泄漏。

在图片合成的过程中,我们经常会遇到这样的情况。

如果需要使用跨域图片:
1)图片服务器必须设置相应的 Access-Control-Allow-Origin 响应头。
2)添加 img 元素的 crossOrigin 属性来请求头。img.crossOrigin = “Anonymous”;

代码块

代码块语法遵循标准markdown代码,例如:

var iUrl = location.origin+"/";
var w = $(".inbox").width(),h = $(".inbox").height();//画布外容器的宽高
var canvas =  document.getElementById('canvas'),
    cxt = canvas.getContext('2d');
    canvas.width = w,
    canvas.height = h,
    canvas.style.width = w,
    canvas.style.height = h ;               //给画布设置宽高
    canvas.style.background = '#fff';       //设置画布背景色
var enlargePointOrigin = {x:0,y:0};         //图片移动、缩放前的初始参数--(右上角坐标)
var enlargePoint = {x:0,y:0};               //图片移动、缩放的实时参数--(右上角坐标)
var imgPositionOrigin = {x:0, y:0, w:0, h:0};//图片移动、缩放前的初始参数--(左上角坐标+宽高)
var imgPosition = {x:0, y:0, w:0, h:0};       //图片移动、缩放的实时参数-(左上角坐标+宽高)
var img ;      //选择图片对象
var img2 ;     //遮盖/背景图片对象
var img2Src = iUrl + "activepage/customerDay/images/photobg1.png";//遮盖、背景图片地址
var uploadImg ;//合成图片地址
var isLoad = 0;//选择的图片是否加载完毕
//载入背景图片
loadImg(img2Src, function(localImg) {
    img2 = localImg
    cxt.drawImage(img2,0,0,img2.width,img2.height,0,0,canvas.width,canvas.height);  
})
$(function(){
    $('#myfile').on('change', function(event) {
        event.preventDefault();
        // var f = $(this)[0].files[0];
        var src = window.navigator.userAgent.indexOf("Chrome") >= 1 || window.navigator.userAgent.indexOf("Safari") >= 1 ? window.webkitURL.createObjectURL(this.files[0]) : window.URL.createObjectURL(this.files[0])

        loadImg(src, function(localImg) {
            img = localImg;
            isLoad=true;
            $('.upload').css("display","none")
            imgPosition.w = canvas.width;
            imgPosition.h = img.height*canvas.width/img.width ;
            imgPositionOrigin.w = canvas.width;
            imgPositionOrigin.h = img.height*canvas.width/img.width ;
            enlargePoint.x = canvas.width;
            enlargePointOrigin.x = canvas.width;
            draw();
        })

    });
    $(".changephoto").click(function(){
        $('#myfile').trigger("click");
    })
})

//载入图片
function loadImg(imgsrc,cb){
    var n = new Image;
    n.crossOrigin = "Anonymous";
    n.onload = function() {
        cb(n)
    }
    n.src = imgsrc;
    if(n.complete || void 0 === n.complete) {
        n.src = "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///ywAAAAAAQABAAACAUwAOw==";
        n.src = imgsrc;
    }
}
//将图片载入画布中
function draw(){
    cxt.fillStyle = "#fff";
    cxt.fillRect(0,0,canvas.width,canvas.height);
    if( isLoad ){
        cxt.drawImage(img, 0, 0, img.width, img.height, imgPosition.x, imgPosition.y, imgPosition.w, imgPosition.h);
        cxt.drawImage(img2,0,0,img2.width,img2.height,0,0,canvas.width,canvas.height);//前面的图片
        $(".changephoto").show();
    }else{
        cxt.drawImage(img2,0,0,img2.width,img2.height,0,0,canvas.width,canvas.height);
    }
}
//清空画布
function erase(){
    cxt.clearRect(0, 0, canvas.width, canvas.height);
}



var _downX = 0;
var _downY = 0;
// var _downX1 = 0;
// var _downY1 = 0;
// var _downX2 = 0;
// var _downY2 = 0;
canvas.addEventListener("touchstart" ,function(ev){
    // if(ev.targetTouches.length == 1 ){//单指 - 移动
        _downX = ev.targetTouches[0].pageX - getAbsLeft(canvas);
        _downY = ev.targetTouches[0].pageY - getAbsTop(canvas);


        if((_downX > imgPosition.x) && (_downX < imgPosition.x + imgPosition.w)&& (_downY > imgPosition.y) && (_downY < imgPosition.y + imgPosition.h)){
            canvas.addEventListener("touchmove", MoveFun2, false);
        }
    // }else if(ev.targetTouches.length == 2 ){
    //     _downX1 = ev.targetTouches[0].pageX - getAbsLeft(canvas);
    //     _downY1 = ev.targetTouches[0].pageY - getAbsTop(canvas);
    //     _downX2 = ev.targetTouches[1].pageX - getAbsLeft(canvas);
    //     _downY2 = ev.targetTouches[1].pageY - getAbsTop(canvas);
    //     if((_downX1 > imgPosition.x) && (_downX1 < imgPosition.x + imgPosition.w)&& (_downY1 > imgPosition.y) && (_downY1 < imgPosition.y + imgPosition.h) && (_downX2 > imgPosition.x) && (_downX2 < imgPosition.x + imgPosition.w)&& (_downY2 > imgPosition.y) && (_downY2 < imgPosition.y + imgPosition.h)){
    //         canvas.addEventListener("touchmove", MoveFun3, false);
    //     }
    // }
    canvas.addEventListener("touchend" ,function(ev){
        imgPositionOrigin.x = imgPosition.x;
        imgPositionOrigin.y = imgPosition.y;
        imgPositionOrigin.w = imgPosition.w;
        imgPositionOrigin.h = imgPosition.h;
        enlargePointOrigin.x = enlargePoint.x;
        enlargePointOrigin.y = enlargePoint.y;
        _downX = 0;
        _downY = 0;
        // _downX1 = 0;
        // _downY1 = 0;
        // _downX2 = 0;
        // _downY2 = 0;

        canvas.removeEventListener("touchmove", MoveFun2, false);
        // canvas.removeEventListener("touchmove", MoveFun3, false);
    });
});
// function MoveFun3(ev){
//     ev.preventDefault();
//     ev.stopPropagation();
//     document.removeEventListener("touchmove", MoveFun1, false);
//     document.removeEventListener("touchmove", MoveFun2, false);
//     var x1 = ev.targetTouches[0].pageX - getAbsLeft(canvas) - imgPosition.x;//触摸点距离图片左边的距离
//     var y1 = ev.targetTouches[0].pageY - getAbsTop(canvas) - imgPosition.y;//触摸点距离图片顶部的距离
//     var x2 = ev.targetTouches[1].pageX - getAbsLeft(canvas) - imgPosition.x;
//     var y2 = ev.targetTouches[1].pageY - getAbsTop(canvas) - imgPosition.y;

//     var _n = Math.sqrt(Math.pow(x1-x2,2)+Math.pow(y1-y2,2))/Math.sqrt(Math.pow(_downX1-_downX2,2)+Math.pow(_downY1-_downY2,2)); //缩放比例

//     var x0 = (ev.targetTouches[0].pageX + ev.targetTouches[1].pageX)/2 - imgPosition.x;
//     var y0 = (ev.targetTouches[0].pageY + ev.targetTouches[1].pageY)/2 - imgPosition.y;//中心点-相对图片本身-坐标

//     imgPosition.w = imgPositionOrigin.w*_n;
//     imgPosition.h = imgPositionOrigin.h*_n;
//     imgPosition.x = (imgPosition.x + enlargePoint.x - imgPosition.w)/2;
//     imgPosition.y = (imgPosition.y + enlargePoint.y - imgPosition.h)/2;
//     enlargePoint.x = imgPosition.x + imgPosition.w;
//     enlargePoint.y = imgPosition.y + imgPosition.h;
    // erase();
    // draw();
// }



function MoveFun2(ev){
    ev.preventDefault();
    ev.stopPropagation();
    var x = ev.targetTouches[0].pageX - getAbsLeft(canvas);
    var y = ev.targetTouches[0].pageY - getAbsTop(canvas);

    imgPosition.x = imgPositionOrigin.x + (x - _downX);
    imgPosition.y = imgPositionOrigin.y + (y - _downY);

    enlargePoint.x = enlargePointOrigin.x + (x - _downX);
    enlargePoint.y = enlargePointOrigin.y + (y - _downY);
    erase();
    draw();
}

//合成图片
function convertCanvasToImage(){
    if(!img){
        return;
    }
    var converImg = new Image();
    converImg.src = canvas.toDataURL("image/png",0.1);
    uploadImg = converImg.src;
    document.getElementById('convert').appendChild(converImg);
}


//获取对象到最屏幕最左边的距离
function getAbsLeft(obj){
    var l = obj.offsetLeft;
    while(obj.offsetParent!=null){
        obj = obj.offsetParent;
        l += obj.offsetLeft;
    }
    return l;
}
//获取对象到最屏幕顶部的距离
function getAbsTop(obj){
    var t = obj.offsetTop;
    while(obj.offsetParent!=null){
        obj = obj.offsetParent;
        t += obj.offsetTop;
    }
    return t;
}

代码解析

  1. 准备工作:先初始化画布,并给其设置宽高,载入前景图案影像。
  2. 载入图片:从相册或拍照获得图片,先清除画布,把该图片载入画布,再将上方遮盖的前景图片载入(覆盖),注意宽高。
  3. 移动图片:计算图片左上角到画布左上角应该偏移的距离(坐标)
  4. 缩放图片:计算图片的放大缩小后的宽高以及左上角坐标
  5. 合成图片

后记

签名、图片文字合成也都是类似的方法。其实canvas可以做的事情超级多啊,有空我会慢慢整理一下。

做这个项目的时候参考了很多内容,但是现在已经翻不到了。如有雷同请原谅。

你可能感兴趣的:(前端开发,canvas)