实现刮刮卡刮奖效果的JavaScript,基于HTML5 Canvas,采用原生js编写,不依赖任何类库。
用法
HTML结构
<div id="scratch">
<div id="card">¥5000000元</div>
</div>
css
#scratch{ position: relative;}
#cover{ position: absolute; top:0; left:0;}
#card{ -webkit-user-select:none; -moz-user-select:none; -ms-user-select:none; user-select:none;}
#card { height: 100%; font-weight: bold;font-size: 40px;line-height: 100px;text-align: center;background: #FAFAFA;}
#scratch {width: 300px;height: 100px;margin: 50px auto 0;border: 1px solid #ccc;}
js
(function(window, document, undefined) {
'use strict';
/**
* Instantiate parameters
*
* @constructor
*/
function LuckyCard(settings, callback) {
this.cover = null;
this.ctx = null;
this.scratchDiv = null;
this.cardDiv = null;
this.cHeight = 0;
this.cWidth = 0;
this.supportTouch = false;
this.events = [];
this.startEventHandler = null;
this.moveEventHandler = null;
this.endEventHandler = null;
this.opt = {
coverColor: '#C5C5C5',
coverImg: '',
ratio: .8,
callback: null
};
this.init(settings, callback);
};
function _calcArea(ctx, callback, ratio) {
var pixels = ctx.getImageData(0, 0, this.cWidth, this.cHeight);
var transPixels = [];
_forEach(pixels.data, function(item, i) {
var pixel = pixels.data[i + 3];
if (pixel === 0) {
transPixels.push(pixel);
}
});
if (transPixels.length / pixels.data.length > ratio) {
callback && typeof callback === 'function' && callback();
}
}
function _forEach(items, callback) {
return Array.prototype.forEach.call(items, function(item, idx) {
callback(item, idx);
});
}
function _isCanvasSupported(){
var elem = document.createElement('canvas');
return !!(elem.getContext && elem.getContext('2d'));
}
/**
* touchstart/mousedown event handler
*/
function _startEventHandler(event) {
event.preventDefault();
this.moveEventHandler = _moveEventHandler.bind(this);
this.cover.addEventListener(this.events[1],this.moveEventHandler,false);
this.endEventHandler = _endEventHandler.bind(this);
document.addEventListener(this.events[2],this.endEventHandler,false);
};
/**
* touchmove/mousemove event handler
*/
function _moveEventHandler(event) {
event.preventDefault();
var evt = this.supportTouch?event.touches[0]:event;
var coverPos = this.cover.getBoundingClientRect();
var pageScrollTop = document.documentElement.scrollTop || document.body.scrollTop;
var pageScrollLeft = document.documentElement.scrollLeft || document.body.scrollLeft;
var mouseX = evt.pageX - coverPos.left - pageScrollLeft;
var mouseY = evt.pageY - coverPos.top - pageScrollTop;
this.ctx.beginPath();
this.ctx.fillStyle = '#FFFFFF';
this.ctx.globalCompositeOperation = "destination-out";
this.ctx.arc(mouseX, mouseY, 10, 0, 2 * Math.PI);
this.ctx.fill();
};
/**
* touchend/mouseup event handler
*/
function _endEventHandler(event) {
event.preventDefault();
if (this.opt.callback && typeof this.opt.callback === 'function') _calcArea.call(this,this.ctx, this.opt.callback, this.opt.ratio);
this.cover.removeEventListener(this.events[1],this.moveEventHandler,false);
document.removeEventListener(this.events[2],this.endEventHandler,false);
};
/**
* Create Canvas element
*/
LuckyCard.prototype.createCanvas = function() {
this.cover = document.createElement('canvas');
this.cover.id = 'cover';
this.cover.height = this.cHeight;
this.cover.width = this.cWidth;
this.ctx = this.cover.getContext('2d');
if (this.opt.coverImg) {
var _this = this;
var coverImg = new Image();
coverImg.src = this.opt.coverImg;
coverImg.onload = function() {
_this.ctx.drawImage(coverImg, 0, 0, _this.cover.width, _this.cover.height);
}
} else {
this.ctx.fillStyle = this.opt.coverColor;
this.ctx.fillRect(0, 0, this.cover.width, this.cover.height);
}
this.scratchDiv.appendChild(this.cover);
this.cardDiv.style.opacity = 1;
}
/**
* To detect whether support touch events
*/
LuckyCard.prototype.eventDetect = function() {
if('ontouchstart' in window) this.supportTouch = true;
this.events = this.supportTouch ? ['touchstart', 'touchmove', 'touchend'] : ['mousedown', 'mousemove', 'mouseup'];
this.addEvent();
};
/**
* Add touchstart/mousedown event listener
*/
LuckyCard.prototype.addEvent = function() {
this.startEventHandler = _startEventHandler.bind(this);
this.cover.addEventListener(this.events[0],this.startEventHandler,false);
};
/**
* Clear pixels of canvas
*/
LuckyCard.prototype.clearCover = function() {
this.ctx.clearRect(0, 0, this.cover.width, this.cover.height);
this.cover.removeEventListener(this.events[0],this.startEventHandler);
this.cover.removeEventListener(this.events[1],this.moveEventHandler);
this.cover.removeEventListener(this.events[2],this.endEventHandler);
};
/**
* LuckyCard initializer
*
* @param {Object} settings Settings for LuckyCard
* @param {function} callback callback function
*/
LuckyCard.prototype.init = function(settings, callback) {
if(!_isCanvasSupported()){
alert('对不起,当前浏览器不支持Canvas,无法使用本控件!');
return;
}
var _this = this;
_forEach(arguments, function(item) {
if (typeof item === "object") {
for (var k in item) {
if (k === 'callback' && typeof item[k] === 'function') {
_this.opt.callback = item[k].bind(_this);
} else {
k in _this.opt && (_this.opt[k] = item[k]);
}
}
} else if (typeof item === "function") {
_this.opt.callback = item.bind(_this);
}
});
this.scratchDiv = document.getElementById('scratch');
this.cardDiv = document.getElementById('card');
if (!this.scratchDiv || !this.cardDiv) return;
this.cHeight = this.cardDiv.clientHeight;
this.cWidth = this.cardDiv.clientWidth;
this.cardDiv.style.opacity = 0;
this.createCanvas();
this.eventDetect();
};
/**
* To generate an instance of object
*
* @param {Object} settings Settings for LuckyCard
* @param {function} callback callback function
*/
LuckyCard.case = function(settings, callback) {
return new LuckyCard(settings, callback);
};
if (typeof define === 'function' && typeof define.amd === 'object' && define.amd) {
define(function() {
return LuckyCard;
});
} else if (typeof module !== 'undefined' && module.exports) {
module.exports = LuckyCard.case;
module.exports.LuckyCard = LuckyCard;
} else {
window.LuckyCard = LuckyCard;
}
})(window, document);
在确保页面相关DOM加载完毕( 如写在页面底部,或document的DOMContentLoaded事件处理函数中 )之后,初始化控件
LuckyCard.case();
LuckyCard.case({
ratio: .7
}, function() {
//alert('至于你信不信,我反正不信!');
this.clearCover();
});
初始化lucky-card控件时,支持传入一个JSON对象和(或)一个回调函数,用于配置控件功能/设置回调函数
LuckyCard.case(settings,callback);
参数settings是一个JSON对象,可选,用于配置控件功能
参数callback是回调函数,可选,也可以写在settings中
基本用法
LuckyCard.case({coverColor:'#CCCCCC',ratio:.6,callback:function(){alert('中奖啦!')}});
刮开层支持使用图片,但图片不能跨域,如果跨域可以考虑将先其转成Data URI,再设置
LuckyCard.case({coverImg:'./demo.jpg'});
callback可作为一个独立的参数存在
LuckyCard.case(function(){
//清除掉刮开层的所有像素
this.clearCover();
});