需求:端午节搞一个画蛋的微信游戏,照虚线画好后判定谁最像,画的越好分数越高。
分析:canvas可以实现画蛋,用透明度0的两条线按标准鸡蛋的内径和外径分别画两个椭圆,使用isPointInPath方法可以判断点是否落在某个路径中,因为手绘鸡蛋的线条有粗细,所以要设定落在画板上线条的宽度,在指定宽度内落在内圆与外圆中线中间的就算成功的。每移动一下都记录成功和失败的点,最后计算成功率。为了防止某些怪异画法,又设定了几个边界检查。
demo地址:http://www.free361.com/demo.php?id=1
egg.js
function canvasApp(id,call){
if(!canvasSupport()){
call(0,"您的浏览器不支持当前页面");
return;
}
var canvas_obj = document.getElementById(id);
var context = canvas_obj.getContext("2d"); //2d上下文对象
context.lineCap = 'square'; //端点样式
var is_mobile = checkMobile();
var canvas_top = $("#"+id).offset().top; //画布外框定位
var canvas_left = $("#"+id).offset().left;
var count_out = 0; //圆以外
var count_in = 0; //圆以内
var count_in_out = 0; //内圆以内
var egg_element; //蛋蛋定时器
var egg_cur_pos = [0,0]; //当前笔画坐标
var egg_org_pos = [138,146]; //中心位置坐标
var egg_w = 90; //蛋宽半径
var egg_h = 118; //蛋高半径
var egg_color = "rgba(211,153,131,0.0)";
var egg_step_num = 0; //起始计数
var egg_flag = 0; //0绘画未开始,1绘画中,2已完成
//手动画蛋
var touch_flag = 0; //手动画的状态0鼠标松开1鼠标按下
var start_x = 0; //手动画开始位置
var start_y = 0;
var current_x = 0; //临时变量,当前位置
var current_y = 0;
var touch_status = 0; //0表示未结束1回到起点结束2超出边界失败
var touch_success_range = 10; //成功检查范围
var touch_edge = [0,0,0,0,0,0,0,0]; //手画蛋边界
//计算实际边界
var egg_edge = [egg_org_pos[0],egg_org_pos[1]-egg_h,egg_org_pos[0]+egg_w,egg_org_pos[1],egg_org_pos[0],egg_org_pos[1]+egg_h,egg_org_pos[0]-egg_w,egg_org_pos[1]];
function canvasSupport(){
return Modernizr.canvas; //检查是否支持canvas
}
function checkMobile(){
return Modernizr.touchevents; //检查是否手机浏览器
}
function drawEgg(){
context.strokeStyle = egg_color; //边框颜色
context.lineWidth = 10;
var x=egg_org_pos[0];
var y=egg_org_pos[1]-egg_h;
if(egg_cur_pos[0] == 0){
egg_cur_pos[0] = x;
egg_cur_pos[1] = y;
}
context.beginPath();
context.moveTo(egg_cur_pos[0],egg_cur_pos[1]);
var angle = egg_step_num*Math.PI/180;
x = egg_org_pos[0]+egg_w*Math.sin(angle);
y = egg_org_pos[1]-egg_h*Math.cos(angle);
context.lineTo(x,y);
context.stroke();
context.closePath();
egg_cur_pos[0] = x;
egg_cur_pos[1] = y;
egg_step_num++;
if(egg_step_num>=360){
clearInterval(egg_element);
egg_step_num = 0;
egg_cur_pos = [0,0];
egg_flag = 2;
}
}
//自动画蛋
function startDrawEgg(){
if(egg_flag == 1){
return;
}
//初始化
egg_flag = 1;
context.clearRect(0,0,270,295);
count_out = 0;
count_in = 0;
count_in_out = 0;
touch_status = 0;
start_x = 0;
start_y = 0;
egg_element = setInterval(drawEgg,33);
}
//蛋底框
function drawEggOnce(){
context.strokeStyle = egg_color; //边框颜色
context.lineWidth = 10;
var x=egg_org_pos[0];
var y=egg_org_pos[1]-egg_h;
if(egg_cur_pos[0] == 0){
egg_cur_pos[0] = x;
egg_cur_pos[1] = y;
}
for(var i=0;i<360;i++){
context.beginPath();
context.moveTo(egg_cur_pos[0],egg_cur_pos[1]);
var angle = i*Math.PI/180;
x = egg_org_pos[0]+egg_w*Math.sin(angle);
y = egg_org_pos[1]-egg_h*Math.cos(angle);
context.lineTo(x,y);
context.stroke();
egg_cur_pos[0] = x;
egg_cur_pos[1] = y;
context.closePath();
}
egg_cur_pos = [0,0];
egg_flag = 2;
}
//绑定
if(is_mobile){
canvas_obj.addEventListener("touchstart",startDrawSelf,false);
canvas_obj.addEventListener("touchend",stopDrawSelf,false);
}else{
canvas_obj.addEventListener("mousedown",startDrawSelf,false);
canvas_obj.addEventListener("mouseup",stopDrawSelf,false);
}
function startDrawSelf(e){
if(egg_flag<2 || touch_status==1){
//自动蛋未画完之前不可以手动画
return;
}
touch_flag = 1; //手动画开始
if(is_mobile){
if (e.targetTouches.length == 1) {
event.preventDefault();// 阻止浏览器默认事件,重要
var touch = e.targetTouches[0];
current_x = touch.pageX-canvas_left;
current_y = touch.pageY-canvas_top;
canvas_obj.addEventListener("touchmove",drawSelf,false);
}
}else{
current_x = e.x-50;
current_y = e.y-50;
canvas_obj.addEventListener("mousemove",drawSelf,false);
}
//记录点击的初始位置
if(start_x == 0){
start_x = current_x;
start_y = current_y;
touch_edge = [current_x,current_y,current_x,current_y,current_x,current_y,current_x,current_y];
}
call(1,{"x":current_x,"y":current_y});
}
function stopDrawSelf(){
if(egg_flag<2){
return;
}
touch_flag = 0; //手动画结束
if(is_mobile){
canvas_obj.removeEventListener("touchmove",drawSelf,false);
}else{
canvas_obj.removeEventListener("mousemove",drawSelf,false);
}
//判断结果
var total_in = count_in-count_in_out;
var total_out = count_out+count_in_out;
var scale = Math.ceil(total_in*100 / (total_in+total_out));
//console.log(count_in,count_out,count_in_out,scale);
if(touch_status == 1){
if(scale>90){
//赢了
// console.log("赢"+scale+"%");
}else{
//输了
// console.log("输"+scale+"%");
}
call(3,{"in":total_in,"out":total_out,"scale":scale});
}else if(touch_status == 2){
//犯规
// console.log("失败");
call(4,{"in":total_in,"out":total_out,"scale":scale});
}else{
//没完成
// console.log("还没画完"+scale+"%");
call(5,{"in":total_in,"out":total_out,"scale":scale});
}
}
function drawSelf(e){
if(egg_flag<2 || touch_flag!=1 || touch_status>0){
return;
}
context.strokeStyle = "red";
context.lineWidth = 8;
if(is_mobile){
//手机触屏
if (e.targetTouches.length == 1) {
event.preventDefault();// 阻止浏览器默认事件,重要
var touch = e.targetTouches[0];
//目标坐标
var mx = touch.pageX-canvas_left;
var my = touch.pageY-canvas_top;
}else{
call(0,"不支持多点触摸");
return;
}
}else{
var mx = e.x-50;
var my = e.y-50;
}
context.beginPath();
context.moveTo(current_x,current_y);
context.lineTo(mx,my);
context.stroke();
context.closePath();
current_x = mx;
current_y = my;
//context,"arc","150,250,120,0,"+2*Math.PI
var event1=new addEvent(context,"egg","136,146,96,126",mx,my,function(n) {
//因为beginPath清掉了前面路径的所有绑定,所以这里重新绑定区域的路径
//大圈
if(n==1){
count_in++;
// div_count_box1.innerHTML=count_in-count_in_out;
}else{
count_out++;
// div_count_box2.innerHTML=count_out+count_in_out;
}
var total_in = count_in-count_in_out;
var total_out = count_out+count_in_out;
var scale = Math.ceil(total_in*100 / (total_in+total_out));
call(2,{"in":total_in,"out":total_out,"scale":scale});
});
event1.add(mx,my);
var event2=new addEvent(context,"egg","136,146,84,114",mx,my,function(n) {
//因为beginPath清掉了前面路径的所有绑定,所以这里重新绑定区域的路径
//小圈
if(n==1){
count_in_out++;
// div_count_box1.innerHTML=count_in-count_in_out;
// div_count_box2.innerHTML=count_out+count_in_out;
}
var total_in = count_in-count_in_out;
var total_out = count_out+count_in_out;
var scale = Math.ceil(total_in*100 / (total_in+total_out));
call(2,{"in":total_in,"out":total_out,"scale":scale});
});
event2.add(mx,my);
checkOver();
//记录当前边界
if(current_y < touch_edge[1] && current_y <touch_edge[3] && current_y < touch_edge[5] && current_y < touch_edge[7]){
//顶点
touch_edge[0] = current_x;
touch_edge[1] = current_y;
}
if(current_x > touch_edge[0] && current_x >touch_edge[2] && current_x > touch_edge[4] && current_x > touch_edge[6]){
//右点
touch_edge[2] = current_x;
touch_edge[3] = current_y;
}
if(current_y > touch_edge[1] && current_y >touch_edge[3] && current_y > touch_edge[5] && current_y > touch_edge[7]){
//顶点
touch_edge[4] = current_x;
touch_edge[5] = current_y;
}
if(current_x < touch_edge[0] && current_x <touch_edge[2] && current_x < touch_edge[4] && current_x < touch_edge[6]){
//右点
touch_edge[6] = current_x;
touch_edge[7] = current_y;
}
}
function checkOver(){
if(count_in+count_out>50 && touch_status<2){
//检查起始点
if(start_x <= current_x+touch_success_range && start_x>=current_x-touch_success_range && start_y <= current_y+touch_success_range && start_y>=current_y-touch_success_range){
//回到原点标记为结束
touch_status = 1;
}
if(touch_status == 1){
//边界检查
var status = 1;
for(var i =0 ;i<8;i++){
if(egg_edge[i]<=touch_edge[i]+50 && egg_edge[i]>=touch_edge[i]-50){
}else{
//有一个不符合就不通过
touch_status = 2;
}
}
}
}
}
function addEvent (cobj,type,data,mx,my,callback) {
this.cobj=cobj; //给addEvent增加属性和方法
this.type=type;
this.data=data;
this.callback=callback;
this.x = mx;
this.y = my;
this.redraw();
}
addEvent.prototype={ //给addEvent原型增加方法
redraw:function () { //重绘
if(this.type=="arc"){
this.cobj.beginPath();
this.cobj.strokeStyle = "rgba(255,0,0,0)";
this.cobj.lineWidth = 8;
var arr=this.data.split(",");
this.cobj.arc(arr[0],arr[1],arr[2],arr[3],arr[4]);
this.cobj.stroke();
}else if("egg"){
this.cobj.beginPath();
this.cobj.strokeStyle = "rgba(0,0,0,0.0)";
this.cobj.lineWidth = 8;
var arr=this.data.split(",");
var x = arr[0]*1;
var y = arr[1]*1;
var a = arr[2]*1;
var b = arr[3]*1;
var step = (a > b) ? 1 / a : 1 / b;
this.cobj.moveTo(x + a, y); //从椭圆的左端点开始绘制
for (var i = 0; i < 2 * Math.PI; i += step)
{
//参数方程为x = a * cos(i), y = b * sin(i),
//参数为i,表示度数(弧度)
this.cobj.lineTo(x + a * Math.cos(i), y + b * Math.sin(i));
}
this.cobj.stroke();
}
},
add:function (mx,my) {
if(this.cobj.isPointInPath(mx,my)){
this.callback(1);
}else{
this.callback(2);
}
}
}
drawEggOnce();
}
调用:
canvasApp("cas",function(s,msg){
//status:0 失败,msg:失败原因
//status 1按下,msg:x,y
//status:2移动,msg:in,out,scale
//status:3结束,msg:in,out,scale
//status:4犯规失败 in,out,scale
//status:5没完成,松开msg: in,out,scale
console.log(s,msg);
if(s == 3){
current_scale = msg.scale;
gameOver();
}else if(s == 4){
gameOver();
}
});