<!DOCTYPE html> <html> <head> <meta http-equiv="content-type" content="text/html; charset=UTF-8"> <title> - jsFiddle demo by artwl</title> </head> <body> <!-- Author : Yaodongxin Date : 2016-08-31 Tips : 如果蒙层是图片需要放到服务前环境测试 --> <button id="freshBtn">刷新</button><label>已刮开 <span id="drawPercent">0%</span> 区域。</label> <div style="position:relative" id = "card"></div> <script type='text/javascript'> var Card = function(options){ this.id = options.id; this.width = options.width;//如果底层图层是图片,这个值会更新成图片尺寸 this.height = options.height;//如果底层图层是图片,这个值会更新成图片尺寸 this.wrap = null;//包裹canvas的对象 this.mask = null;//蒙层canvas对象 this.bacCtx = null; //context 对象 this.maskCtx = null; //context 对象 this.backType = options.backType || 'text'; // 'image' || 'text' this.backImg = options.backImg; //刮开后的图层 this.texts = options.texts;//文字串码 this.fontSize = options.fontSize || 36;//文字串码字体大小 this.maskType = options.maskType || 'color';// 'color' || 'image' this.maskImg = options.maskImg; this.maskColor = options.maskColor || '#ccc'; this.callback = options.callback || false; //是否执行刮开百分比函数 boollean类型 this.percentDom = options.percentDom || document.getElementById('drawPercent'); } Card.prototype = { init : function(){ this.percentDom.innerHTML = '0%'; this.draw(); }, draw : function(){ this.wrap = document.getElementById(this.id); this.drawMask(); }, // 画底层内容 drawCard : function(){ var _this = this; _this.resizeCanvas(_this.card, _this.width, _this.height); _this.bacCtx = _this.card.getContext('2d'); if(_this.backType == 'image'){ if(!_this.backImg){ console.error('A back image url is need.'); }else{ var image = new Image(); image.src = _this.backImg; image.onload = function(){ _this.width = this.width; _this.height = this.height; _this.resizeCanvas(_this.card, this.width, this.height); _this.bacCtx.drawImage(this, 0, 0); //绘背景图 } image.error = function(){ console.error('image load failed. Check your url or network.'); } } }else{ _this.texts = _this.texts || getRandomStr(6); _this.bacCtx.font = 'Bold ' + _this.fontSize + 'px "microsoft yahei"'; _this.bacCtx.fillStyle = '#ff6600'; _this.bacCtx.textAlign = 'center'; _this.bacCtx.textBaseline = 'middle'; _this.bacCtx.fillText(_this.texts, _this.width/2, _this.height/2); } }, // 绘制蒙层 drawMask : function(){ var _this = this; // _this.wrap.innerHTML = '' if(_this.wrap.innerHTML == ''){ _this.mask = document.createElement('canvas'); _this.wrap.appendChild(_this.mask); _this.mask.setAttribute('style','position:absolute;left:0;top:0;'); _this.card = document.createElement('canvas'); _this.wrap.appendChild(_this.card); this.bindEvents(); } _this.resizeCanvas(_this.mask, _this.width, _this.height); _this.maskCtx = _this.mask.getContext('2d'); if(_this.maskType == 'image'){ if(!_this.maskImg){ console.error('A mask image url is need.'); }else{ var image = new Image(); image.src = _this.maskImg; image.onload = function(){ _this.maskCtx.drawImage(this, 0, 0); //绘蒙层 _this.drawCard();//绘制随机码 } image.error = function(){ console.error('image load failed. Check your url or network.'); } } }else{ _this.maskCtx.fillStyle = this.maskColor; _this.maskCtx.fillRect(0, 0, _this.width, _this.height); _this.drawCard();//绘制随机码 } _this.clientRect = _this.wrap ? _this.wrap.getBoundingClientRect() : null; }, // 绘制擦除圆 drawPoint : function(x, y){ this.maskCtx.globalCompositeOperation = 'destination-out'; this.maskCtx.beginPath(); // var radgrad = this.maskCtx.createRadialGradient(x, y, 0, x, y, 30); // radgrad.addColorStop(0, 'rgba(0, 0, 0, 1)'); // radgrad.addColorStop(1, 'rgba(0, 0, 0, 1)'); radgrad = '#fff'; this.maskCtx.fillStyle = radgrad; this.maskCtx.arc(x, y, 30, 0, Math.PI * 2, true); this.maskCtx.fill(); }, //绑定事件 bindEvents : function(){ var _this = this, device = (/android|webos|iphone|ipad|ipod|blackberry|iemobile|opera mini/i.test(navigator.userAgent.toLowerCase())), clickEvtName = device ? 'touchstart' : 'mousedown', moveEvtName = device? 'touchmove': 'mousemove', isMouseDown = false, start ={x:0, y:0}, end = {x:0, y:0}; if (!device) {//pc isMouseDown = false; document.addEventListener('mouseup', function(e) { isMouseDown = false; start ={x:0, y:0}; end = {x:0, y:0}; }, false); document.addEventListener('mouseout', function(e) { isMouseDown = false; start ={x:0, y:0}; end = {x:0, y:0}; }, false); }else{//mobile document.addEventListener('touchmove', function(e) { if (isMouseDown) { e.preventDefault(); } }, false); document.addEventListener('touchend', function(e) { isMouseDown = false; start ={x:0, y:0}; end = {x:0, y:0}; }, false); } // 绑定擦除事件 var docEle = document.documentElement || document.body; _this.mask.addEventListener(clickEvtName, function(e){ // console.log(device) start ={x:0, y:0}; end = {x:0, y:0}; isMouseDown = true; 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 (!isMouseDown) { return false; } isMouseDown = true; 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; if(start.x!=0){ end.x = start.x; end.y = start.y; } start.x = x; start.y = y; _this.drawPoint(x, y); if(end.x!=0){ _this.addDraw(start, end); } if(_this.callback){ var percent = _this.drawPercent(_this.maskCtx); _this.percentDom.innerHTML = percent + '%'; } }, false); }, /** * 这个函数是用来补充move的时候中间的空白 * @param {object} start 形式{x:10,y:10}的对象,画线开始坐标 * @param {object} end 形式{x:20,y:20}的对象,画线结束坐标 */ addDraw : function(start, end){ var _this = this; _this.maskCtx.lineWidth = 60; _this.maskCtx.lineStyle = '#f00'; _this.maskCtx.beginPath(); _this.maskCtx.moveTo(start.x, start.y); _this.maskCtx.lineTo(end.x, end.y); _this.maskCtx.stroke(); }, /** * 重置canvas的宽高 * @param {object} canvas canvas对象 * @param {number} w canvas宽 * @param {number} h canvas高 */ resizeCanvas : function(canvas, w, h){ canvas.width = w; canvas.height = h; }, /** * 计算刮开的百分比 * @param {object} ctx context2d对象 * @return {number} 刮开图层所占百分比 */ drawPercent : function(ctx){ var imgData = ctx.getImageData(0, 0, this.width, this.height), pixles = imgData.data, transPixs = [], len = pixles.length; for(var i = 0; i<len; i+=4){ if(pixles[i+3] == 0){ transPixs.push(pixles[i+3]); } } return (transPixs.length/(len/4)*100).toFixed(2); } } /** * 生成随机串码 * @param {number} len 生成随机串码的长度 * @return {string} 生成的串码 */ function getRandomStr(len) { var text = ''; var possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; for( var i=0; i < len; i++ ) text += possible.charAt(Math.floor(Math.random() * possible.length)); return text; } window.onload = function(){ var card = new Card({ id : 'card', backType : 'text', width : '325', height : '174' }); card.init(); // 刷新 document.getElementById('freshBtn').addEventListener('click', function(){ card.init(); }, false); } </script> </body> </html>
demo:预览地址