今天做一个刮奖的项目,要用到canvas,在用户进行刮奖时,从后台请求数据并且用canvas将奖品名字画在画布上,但是问题来了,用户在进行刮奖操作时,从后台请求的数据要画在画布上,好像是需要重新绘制图形,但是那样用户第一步刮奖的操作就无效了,请问是否能够只更新奖品名字那一部分?或者有其他更好的办法?
附代码:
$(function() {
//定义Lottery类
function Lottery(id, cover, coverType, width, height, drawPercentCallback) {
this.conId = id;
this.conNode = document.getElementById(this.conId);
this.cover = cover;
this.coverType = coverType;
this.background = null;
this.backCtx = null;
this.mask = null;
this.maskCtx = null;
this.lottery = null;
this.lotteryType = 'image';
this.width = width || 800;
this.height = height || 100;
this.clientRect = null;
this.drawPercentCallback = drawPercentCallback;
}
Lottery.prototype = {
createElement: function (tagName, attributes) {
var ele = document.createElement(tagName);
for (var key in attributes) {
ele.setAttribute(key, attributes[key]);
}
return ele;
},
//涂抹区域百分比
getTransparentPercent: function (ctx, width, height) {
var imgData = ctx.getImageData(0, 0, width, height),
pixles = imgData.data,
transPixs = [];
for (var i = 0, j = pixles.length; i < j; i += 4) {
var a = pixles[i + 3];
if (a < 128) {
transPixs.push(i);
}
}
return (transPixs.length / (pixles.length / 4) * 100).toFixed(2);
},
//改变canvas大小的工具方法。
resizeCanvas: function (canvas, width, height) {
canvas.width = width;
canvas.height = height;
canvas.getContext('2d').clearRect(0, 0, width, height);
},
//绘制点击和涂抹区域
drawPoint: function (x, y) {
this.maskCtx.beginPath();
var radgrad = this.maskCtx.createRadialGradient(x, y, 0, x, y, 30);
radgrad.addColorStop(0, 'rgba(0,0,0,0.6)');
radgrad.addColorStop(1, 'rgba(255, 255, 255, 0)');
this.maskCtx.fillStyle = radgrad;
this.maskCtx.arc(x, y, 15, 0, Math.PI * 2, true);
this.maskCtx.fill();
if (this.drawPercentCallback) {
this.drawPercentCallback.call(null, this.getTransparentPercent(this.maskCtx, this.width, this.height));
}
},
//绑定事件
bindEvent: function () {
var _this = this;
var device = (/android|webos|iphone|ipad|ipod|blackberry|iemobile|opera mini/i.test(navigator.userAgent.toLowerCase()));
var clickEvtName = device ? 'touchstart' : 'mousedown';
var moveEvtName = device ? 'touchmove' : 'mousemove';
if (!device) {
var isMouseDown = false;
document.addEventListener('mouseup', function (e) {
isMouseDown = false;
}, false);
} else {
document.addEventListener("touchmove", function (e) {
if (isMouseDown) {
e.preventDefault();
}
}, false);
document.addEventListener('touchend', function (e) {
isMouseDown = false;
}, false);
}
this.mask.addEventListener(clickEvtName, function (e) {
isMouseDown = true;
var docEle = document.documentElement;
if (!_this.clientRect) {
_this.clientRect = {
left: 0,
top: 0
};
}
var x = (device ? e.touches[0].clientX : e.clientX) - _this.clientRect.left + docEle.scrollLeft - docEle.clientLeft;
var y = (device ? e.touches[0].clientY : e.clientY) - _this.clientRect.top + docEle.scrollTop - docEle.clientTop;
_this.drawPoint(x, y);
}, false);
this.mask.addEventListener(moveEvtName, function (e) {
if (!device && !isMouseDown) {
return false;
}
var docEle = document.documentElement;
if (!_this.clientRect) {
_this.clientRect = {
left: 0,
top: 0
};
}
var x = (device ? e.touches[0].clientX : e.clientX) - _this.clientRect.left + docEle.scrollLeft - docEle.clientLeft;
var y = (device ? e.touches[0].clientY : e.clientY) - _this.clientRect.top + docEle.scrollTop - docEle.clientTop;
_this.drawPoint(x, y);
}, false);
},
//添加两个canvas到刮奖容器,并获取2d上下文
drawLottery: function () {
this.background = this.background || this.createElement('canvas', {
style: 'position:absolute;left:0;top:0;'
});
this.mask = this.mask || this.createElement('canvas', {
style: 'position:absolute;left:0;top:0;'
});
if (!this.conNode.innerHTML.replace(/[\w\W]| /g, '')) {
this.conNode.appendChild(this.background);
this.conNode.appendChild(this.mask);
this.clientRect = this.conNode ? this.conNode.getBoundingClientRect() : null;
this.bindEvent();
}
this.backCtx = this.backCtx || this.background.getContext('2d');
this.maskCtx = this.maskCtx || this.mask.getContext('2d');
//绘制第一个canvas
if (this.lotteryType == 'image') {
var image = new Image(),
_this = this;
image.onload = function () {
_this.width = this.width;
_this.height = this.height;
_this.resizeCanvas(_this.background, this.width, this.height);
_this.backCtx.drawImage(this, 0, 0);
_this.drawMask();
}
image.src = this.lottery;
} else if (this.lotteryType == 'text') {
this.width = this.width;
this.height = this.height;
this.resizeCanvas(this.background, this.width, this.height);
this.backCtx.save();
this.backCtx.fillStyle = '#FFF';
this.backCtx.fillRect(0, 0, this.width, this.height);
this.backCtx.restore();
this.backCtx.save();
var fontSize = 20;
this.backCtx.font = 'Bold ' + fontSize + 'px Arial';
this.backCtx.textAlign = 'center';
this.backCtx.fillStyle = '#da345f';
this.backCtx.fillText(this.lottery, this.width / 2, this.height / 2 + fontSize / 2);
this.backCtx.restore();
this.drawMask();
}
},
//绘制第二个canvas
drawMask: function () {
this.resizeCanvas(this.mask, this.width, this.height);
if (this.coverType == 'color') {
this.maskCtx.fillStyle = this.cover;
this.maskCtx.fillRect(0, 0, this.width, this.height);
this.maskCtx.globalCompositeOperation = 'destination-out';
} else if (this.coverType == 'image') {
var image = new Image(),
_this = this;
image.onload = function () {
_this.maskCtx.drawImage(this, 0, 0);
_this.maskCtx.globalCompositeOperation = 'destination-out';
}
image.src = this.cover;
}
},
//调用入口init
init: function (lottery, lotteryType) {
this.lottery = lottery;
this.lotteryType = lotteryType || 'image';
this.drawLottery();
}
};
//加载页面绘制空白数据
window.onload = function () {
var lottery = new Lottery('scratch-area', '#092f57', 'color', 300, 102,drawPercent);
lottery.init("",'text');
//判断刮开的区域
function drawPercent(percent) {
$("#drawPercent").html(percent);
var area = $("#drawPercent").html();
//console.log(area);
}
//开始刮奖请求数据,并重新绘制canvas
$("#scratch-area").data("flag", true).on("touchstart", function (e) {
e.preventDefault();
if ($(this).data("flag")) {
$.ajax({
url: '/scratchcard/lottery?eventId=' + eventId + '&mappId=' + appId,
dataType: 'json',
success: function (data) {
console.log(data);
$(".frequency-total").text(data.prizeChance);
var prize = data.prizeName;
//抽奖次数
var prizeChance = data.prizeChance;
//是否抽中奖品
var prizeType = parseInt(data.prizeType);
//判断抽奖机会
if (prizeChance > 0) {
var lottery = new Lottery('scratch-area', '#092f57', 'color', 300, 102, drawPercent);
lottery.init(prize, 'text');
if (prizeType == 0) {
$(".look-prize").css("visibility","visible").on("click",function(){
window.location.href="/center/coupon?mappId="+mappId;
});
}
function drawPercent(percent) {
$("#drawPercent").html(percent);
var area = $("#drawPercent").html();
if (area > 9.5 && prizeType == 2) {
//app.alert('恭喜您获得' + '"' + prize + '"!', '刮刮乐', function () {
// //location.reload();
// console.log("in")
//});
$(".look-prize").css("visibility","visible").html("点我刷新").on("click",function(){
location.reload();
});
}
}
} else {
app.alert('您的剩余刮奖次数不足!', '刮刮乐',function(){
location.reload();
});
}
},
error: function (error) {
app.alert(error.responseText, '刮刮乐');
}
});
$(this).data("flag", false);
console.log("in")
}
});
};
});
讲下大致的思路,其中dom结构这么放
然后position布局放canvas覆盖在img上面,然后canvas监听touch和mouseMove事件,根据event的layerX/layerY或者offsetX/offsetY得出当前运动的坐标点,然后清理掉canvas上面的图案,清理方法在下面。
可以做个回掉的监测,当清理的面积(根据半径和移动的xy坐标可算)大于整个canvas面积的70%(随便举例)就算刮奖完成。
别看讲的这么简单,写完这部分代码,加上各种边界检查等容错得弄几个小时吧。
以下是原答案:
建议做两层,然后重叠,canvas默认是png类型有透明通道的,直接刮开canvas下面就看到了奖品的那个图片。
canvas里面有cleanRect的方法,清理掉矩形区域很好用,但是圆形区域没有直接的接口,但是利用globalCompositeOperation可以hack一个清理圆形区域的方法出来。
补充一下关于clean arc 方面的一点东西:
http://stackoverflow.com/ques...