前段时间项目上需要做一个合成图片的小活动,参考了很多案例,发现还是用canvas最为简便。
这个活动主要是做一个合影功能,和以前咱们玩的大头贴有点像,哈哈哈。
代码主要是这几部分:
- 确定容器宽高度,给canvas赋值
- 把背景图拽入画布
- 上传图片或拍照,并拽入画布
- 移动图片、或缩小
- 合成图片
接收9个参数,接受值 都是像素,也可缩写成 drawImage(image,dx,dy) 或 drawImage(image, dx,dy,dw,dh)
image object
原图x坐标(裁剪)
原图y坐标(裁剪)
原图宽度(裁剪)
原图高度(裁剪)
绘制图片在画布上的x坐标
绘制图片在画布上的y坐标
绘制图片的宽度
绘制图片的高度
其实根据这个图片示例,很容易可以看出,
- sx sy sw sh这几个参数决定了图片的裁剪
- 改变 dx dy 可以移动图片
- 改变 dw dh 可以放大或缩小图片(dWidth > sWidth && dHeight > sHeight 绘制的是放大的图片,dWidth < sWidth && dHeight < sHeight 绘制的是缩小的图片)
原理就是这么简单。
值得注意的是,当绘制多张图片时,先绘制的图片在下层,后绘制的图片在上层。
在指定图片格式为 image/jpeg 或 image/webp的情况下,可以从 0 到 1 的区间内选择图片的质量。
如果超出取值范围,将会使用默认值 0.92。其他参数会被忽略。
尽管没有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;
}
签名、图片文字合成也都是类似的方法。其实canvas可以做的事情超级多啊,有空我会慢慢整理一下。
做这个项目的时候参考了很多内容,但是现在已经翻不到了。如有雷同请原谅。