引言:网上最近太空人表盘很火,之前看到有个兄弟用svg写的,但是我也不会这个啊,我就琢磨着用canvas写了一个,效果感觉还不错,拿出来大家唠唠!
效果图:
思路
-
分两个画布来绘制,画布1用来放置不动的东西(背景、表盘、文字信息);
-
画布2用来绘制太空人的转动和时间的更新(经常要重新绘制);
-
太空人的转动的话就是用很多图片来切换,达到转动的效果。
绘制表盘
圆形构造函数
//构造函数
function Circle(o){
this.x=0,//圆心X坐标
this.y=0,//圆心Y坐标
this.r=0,//半径
this.startAngle=0,//开始角度
this.endAngle=0,//结束角度
this.anticlockwise=false;//顺时针,逆时针方向指定
this.stroke=false;//是否描边
this.fill=false;//是否填充
this.scaleX=1;//缩放X比例
this.scaleY=1;//缩放Y比例
this.rotate=0;
this.init(o);
}
//初始化
Circle.prototype.init=function(o){
for(var key in o){
this[key]=o[key];
}
}
//绘制
Circle.prototype.render=function(context){
var ctx=context;//获取上下文
ctx.save();
ctx.beginPath();
ctx.translate(this.x,this.y);
if(this.fill){
ctx.moveTo(0,0);
}
//ctx.moveTo(this.x,this.y);
ctx.scale(this.scaleX,this.scaleY);//设定缩放
ctx.arc(0,0,this.r,this.startAngle,this.endAngle);//画圆
if(this.lineWidth){//线宽
ctx.lineWidth=this.lineWidth;
}
if(this.fill){//是否填充
this.fillStyle?(ctx.fillStyle=this.fillStyle):null;
ctx.fill();
}
if(this.stroke){//是否描边
this.strokeStyle?(ctx.strokeStyle=this.strokeStyle):null;
ctx.stroke();
}
ctx.restore();
return this;
}
绘制代码
//绘制表盘
SpaceMan.prototype.drawClock=function(){
var x=y=0,cilcle;
x=this.w/2;y=this.h/2;
//绘制外面的大圆
cilcle = new Circle({
x:x,//圆心X坐标
y:y,//圆心X坐标
r:250,//半径
startAngle:0,//开始角度
endAngle:2*Math.PI,//结束角度
lineWidth:2,
fill:true,
fillStyle:'#444444'
});
this.renderArr.push(cilcle);
//绘制第2个圆
cilcle = new Circle({
x:x,//圆心X坐标
y:y,//圆心X坐标
r:220,//半径
startAngle:0,//开始角度
endAngle:2*Math.PI,//结束角度
lineWidth:2,
fill:true,
fillStyle:'#DFE6F0'
});
this.renderArr.push(cilcle);
}
此时页面的效果:
绘制分隔线
构造函数
//直线的构造
function Line(o){
this.x=0,//x坐标
this.y=0,//y坐标
this.startX=0,//开始点x位置
this.startY=0, //开始点y位置
this.endX=0,//结束点x位置
this.endY=0;//结束点y位置
this.thin=false;//设置变细系数
this.init(o);
}
Line.prototype.init=function(o){
for(var key in o){
this[key]=o[key];
}
}
Line.prototype.render=function(ctx){
innerRender(this);
function innerRender(obj){
ctx.save()
ctx.beginPath();
ctx.translate(obj.x,obj.y);
if(obj.thin){
ctx.translate(0.5,0.5);
}
if(obj.lineWidth){//设定线宽
ctx.lineWidth=obj.lineWidth;
}
if(obj.strokeStyle){
ctx.strokeStyle=obj.strokeStyle;
}
//划线
ctx.moveTo(obj.startX, obj.startY);
ctx.lineTo(obj.endX, obj.endY);
ctx.stroke();
ctx.restore();
}
return this;
}
绘制代码
//分隔线的 绘制
SpaceMan.prototype.drawClockLine=function(){
var x=y=0;
var line = new Line({
x:x,
y:y,
startX:70,
startY:120,
endX:430,
endY:120,
strokeStyle:'#030609',
lineWidth:3
})
this.renderArr.push(line);
line = new Line({
x:x,
y:y,
startX:220,
startY:30,
endX:220,
endY:120,
strokeStyle:'#030609',
lineWidth:3
})
this.renderArr.push(line);
line = new Line({
x:x,
y:y,
startX:58,
startY:360,
endX:442,
endY:360,
strokeStyle:'#030609',
lineWidth:3
})
this.renderArr.push(line);
line = new Line({
x:x,
y:y,
startX:180,
startY:410,
endX:180,
endY:460,
strokeStyle:'#030609',
lineWidth:3
})
this.renderArr.push(line);
line = new Line({
x:x,
y:y,
startX:178,
startY:410,
endX:342,
endY:410,
strokeStyle:'#030609',
lineWidth:3
})
this.renderArr.push(line);
line = new Line({
x:x,
y:y,
startX:340,
startY:410,
endX:340,
endY:360,
strokeStyle:'#030609',
lineWidth:3
})
this.renderArr.push(line);
}
此时效果
绘制文字
构造函数
//文字的构造函数
function Text(o){
this.x=0,//x坐标
this.y=0,//y坐标
this.disX=0,//x坐标偏移量
this.disY=0,//y坐标偏移量
this.text='',//内容
this.font=null;//字体
this.textAlign=null;//对齐方式
this.init(o);
}
Text.prototype.init=function(o){
for(var key in o){
this[key]=o[key];
}
}
Text.prototype.render=function(context){
this.ctx=context;
innerRender(this);
function innerRender(obj){
var ctx=obj.ctx;
ctx.save()
ctx.beginPath();
ctx.translate(obj.x,obj.y);
if(obj.angle){//根据旋转角度来执行旋转
ctx.rotate(-obj.angle*Math.PI/180);
}
if(obj.font){
ctx.font=obj.font;
}
if(obj.textAlign){
ctx.textAlign=obj.textAlign;
}
if(obj.fill){//是否填充
obj.fillStyle?(ctx.fillStyle=obj.fillStyle):null;
ctx.fillText(obj.text,obj.disX,obj.disY);
}
if(obj.stroke){//是否描边
obj.strokeStyle?(ctx.strokeStyle=obj.strokeStyle):null;
ctx.strokeText(obj.text,obj.disX,obj.disY);
}
ctx.restore();
}
return this;
}
绘制
//组装文字信息
SpaceMan.prototype.drawText=function(){
var content="",x=y=0;
//天气
x=230;y=60,content="空气良好";
var text = new Text({
x:x,
y:y,
text:content,
font:'bold 20px ans-serif',
textAlign:'left',
fill:true,
fillStyle:'#44444'
});
this.renderArr.push(text);
x=230;y=85,content="晴天";
var text = new Text({
x:x,
y:y,
text:content,
font:'20px ans-serif',
textAlign:'left',
fill:true,
fillStyle:'#44444'
});
this.renderArr.push(text);
//气温
x=230;y=110,content="23°C";
var text = new Text({
x:x,
y:y,
text:content,
font:'18px ans-serif',
textAlign:'left',
fill:true,
fillStyle:'#44444'
});
this.renderArr.push(text);
//绘制最小温度
x=285;y=110,content="18°";
var text = new Text({
x:x,
y:y,
text:content,
font:'18px ans-serif',
textAlign:'left',
fill:true,
fillStyle:'#44444'
});
this.renderArr.push(text);
//绘制最大温度
x=285;y=85,content="26°";
var text = new Text({
x:x,
y:y,
text:content,
font:'18px ans-serif',
textAlign:'left',
fill:true,
fillStyle:'#44444'
});
this.renderArr.push(text);
//电量
x=120;y=115,content="70%";
var text = new Text({
x:x,
y:y,
text:content,
font:'bold 35px ans-serif',
textAlign:'left',
fill:true,
fillStyle:'#44444'
});
this.renderArr.push(text);
//心率
x=65;y=305,content="80~128";
var text = new Text({
x:x,
y:y,
text:content,
font:'20px ans-serif',
textAlign:'left',
fill:true,
fillStyle:'#44444'
});
this.renderArr.push(text);
x=130;y=345,content="92";
var text = new Text({
x:x,
y:y,
text:content,
font:'bold 30px ans-serif',
textAlign:'left',
fill:true,
fillStyle:'#44444'
});
this.renderArr.push(text);
//步数
x=370;y=345,content="7032";
var text = new Text({
x:x,
y:y,
text:content,
font:'bold 26px ans-serif',
textAlign:'left',
fill:true,
fillStyle:'#44444'
});
this.renderArr.push(text);
//睡眠
x=110;y=395,content="睡眠";
var text = new Text({
x:x,
y:y,
text:content,
font:'30px ans-serif',
textAlign:'left',
fill:true,
fillStyle:'#44444'
});
this.renderArr.push(text);
x=190;y=400,content="8h30m";
var text = new Text({
x:x,
y:y,
text:content,
font:'BOLD 34px ans-serif',
textAlign:'left',
fill:true,
fillStyle:'#44444'
});
this.renderArr.push(text);
//距离
x=350;y=395,content="距离";
var text = new Text({
x:x,
y:y,
text:content,
font:'30px ans-serif',
textAlign:'left',
fill:true,
fillStyle:'#44444'
});
this.renderArr.push(text);
x=210;y=445,content="9.22km";
var text = new Text({
x:x,
y:y,
text:content,
font:'BOLD 32px ans-serif',
textAlign:'left',
fill:true,
fillStyle:'#44444'
});
this.renderArr.push(text);
}
效果如下:
绘制修饰图片
图片构造函数
//图片对象ImageDraw构造函数
function ImageDraw(o,obj){
this.id='',
this.image=0,//图片对象(必填)
this.sx=0,//图片切片开始x位置(显示整个图片的时候不需要填)
this.sy=0,//图片切片开始y位置(显示整个图片的时候不需要填)
this.sWidth=0, //图片切片开始宽度(显示整个图片的时候不需要填)
this.sHeight=0,//图片切片开始高度(显示整个图片的时候不需要填)
this.dx=0, //图片目标x位置(必填)
this.dy=0, //图片目标y位置(必填)
this.dWidth=0,//图片目标显示宽度(宽度不缩放时不必填)
this.dHeight=0//图片目标高度高度(高度不缩放时不必填)
this.init(o,obj);
}
ImageDraw.prototype.init=function(o,obj){
this.lol=obj;
for(var key in o){
this[key]=o[key];
}
return this;
}
ImageDraw.prototype.render=function(context){
draw(context,this);
function draw(context,obj) {
var ctx=context;
ctx.save();
if(!obj.image || getType(obj.dx)=='undefined' || getType(obj.dy)=='undefined'){
throw new Error("绘制图片缺失参数");
return;
}
ctx.translate(obj.dx,obj.dy);
if(getType(obj.sx)!='undefined' && getType(obj.sy)!='undefined' && obj.sWidth && obj.sHeight && obj.dWidth && obj.dHeight){
//裁剪图片,显示时候有缩放
ctx.drawImage(obj.image, obj.sx, obj.sy, obj.sWidth, obj.sHeight, 0, 0, obj.dWidth, obj.dHeight);
}else if(obj.dWidth && obj.dHeight){
ctx.drawImage(obj.image, 0, 0, obj.dWidth, obj.dHeight);//原始图片,显示时候有缩放
}else{
ctx.drawImage(obj.image,0, 0);//原始图片,显示时候无缩放
}
ctx.restore();
}
}
ImageDraw.prototype.isPoint=function(pos){
//鼠标位置的x、y要分别大于dx、dy 且x、y要分别小于 dx+dWidth、dy+dHeight
if(pos.x>this.dx && pos.y>this.dy && pos.x<this.dx+this.dWidth && pos.y<this.dy+this.dHeight ){//表示处于当前图片对象范围内
return true;
}
return false;
}
绘制
//组装图片对象信息
SpaceMan.prototype.drawOtherImg=function(){
//绘制电量
var image = this.imgObj[66];
var img,x=y=0,sWidth=200,sHeight=200,dx=170,dy=45,dWidth=50,dHeight=50;
img = new ImageDraw({image:image,sx:x,sy:y,sWidth:sWidth,sHeight:sHeight, dx:dx, dy:dy ,dWidth:dWidth,dHeight:dHeight},this);
this.renderArr.push(img);
//绘制太阳
image = this.imgObj[62];
sWidth=200,sHeight=200,dx=340,dy=70,dWidth=50,dHeight=50;
img = new ImageDraw({image:image,sx:x,sy:y,sWidth:sWidth,sHeight:sHeight, dx:dx, dy:dy ,dWidth:dWidth,dHeight:dHeight},this);
this.renderArr.push(img);
//绘制最大温度
image = this.imgObj[65];
var img,x=y=0,sWidth=200,sHeight=200,dx=315,dy=70,dWidth=20,dHeight=20;
img = new ImageDraw({image:image,sx:x,sy:y,sWidth:sWidth,sHeight:sHeight, dx:dx, dy:dy ,dWidth:dWidth,dHeight:dHeight},this);
this.renderArr.push(img);
//绘制最小温度
image = this.imgObj[64];
var img,x=y=0,sWidth=200,sHeight=200,dx=315,dy=90,dWidth=20,dHeight=20;
img = new ImageDraw({image:image,sx:x,sy:y,sWidth:sWidth,sHeight:sHeight, dx:dx, dy:dy ,dWidth:dWidth,dHeight:dHeight},this);
this.renderArr.push(img);
//绘制心率
image = this.imgObj[61];
sWidth=200,sHeight=200,dx=70,dy=305,dWidth=60,dHeight=60;
img = new ImageDraw({image:image,sx:x,sy:y,sWidth:sWidth,sHeight:sHeight, dx:dx, dy:dy ,dWidth:dWidth,dHeight:dHeight},this);
this.renderArr.push(img);
//绘制步数
image = this.imgObj[63];
sWidth=200,sHeight=200,dx=320,dy=310,dWidth=50,dHeight=50;
img = new ImageDraw({image:image,sx:x,sy:y,sWidth:sWidth,sHeight:sHeight, dx:dx, dy:dy ,dWidth:dWidth,dHeight:dHeight},this);
this.renderArr.push(img);
}
此时的效果
绘制太空人
代码
//组装太空人图片对象信息
SpaceMan.prototype.drawImg=function(){
var image = this.imgObj[this.imageKey];
var img,x=y=0,sWidth=534,sHeight=598,dx=190,dy=200,dWidth=120,dHeight=134;
img = new ImageDraw({image:image,sx:x,sy:y,sWidth:sWidth,sHeight:sHeight, dx:dx, dy:dy ,dWidth:dWidth,dHeight:dHeight},this);
this.renderArr2.push(img);
}
此时效果
绘制时间
入口代码
//绘制日期
SpaceMan.prototype.drawDateTime=function(){
this.drawHour();//绘制小时
this.drawMinute();//绘制分钟
this.drawSecond();//绘制秒钟
this.drawWeekDay();//绘制周
this.drawMonthDate();//绘制日
this.drawMonthDate2();//绘制农历
}
绘制小时
//绘制小时
SpaceMan.prototype.drawHour=function(){
var content=this.hour+"",x=y=0;
//小时
x=120;y=190,
content=(content.length==1?(0+content):content);
var text = new Text({
x:x,
y:y,
text:content,
font:'bold 80px ans-serif',
textAlign:'left',
fill:true,
fillStyle:'#44444'
});
this.renderArr2.push(text);
this.hourObj=text;
//绘制 :号
x=220;y=175,
content=":";
text = new Text({
x:x,
y:y,
text:content,
font:'bold 60px ans-serif',
textAlign:'left',
fill:true,
fillStyle:'#44444'
});
this.renderArr2.push(text);
}
绘制分钟、秒钟
//绘制分钟
SpaceMan.prototype.drawMinute=function(){
var content=this.minute+"",x=y=0;
//分
x=235;y=190,
content=(content.length==1?(0+content):content);
var text = new Text({
x:x,
y:y,
text:content,
font:'bold 80px ans-serif',
textAlign:'left',
fill:true,
fillStyle:'#44444'
});
this.renderArr2.push(text);
this.minuteObj=text;
}
//绘制秒钟
SpaceMan.prototype.drawSecond=function(){
var content=this.second+"",x=y=0;
//秒
x=340;y=190,
content=(content.length==1?(0+content):content);
var text = new Text({
x:x,
y:y,
text:content,
font:'bold 35px ans-serif',
textAlign:'left',
fill:true,
fillStyle:'#44444'
});
this.renderArr2.push(text);
this.secondObj=text;
}
绘制星期、日期
//绘制星期
SpaceMan.prototype.drawWeekDay=function(){
var x=y=0;
x=350;y=270,
content=this.weekArr[this.day];
var text = new Text({
x:x,
y:y,
text:content,
font:'20px ans-serif',
textAlign:'left',
fill:true,
fillStyle:'#44444'
});
this.renderArr2.push(text);
this.weekDayObj=text;
}
//绘制日
SpaceMan.prototype.drawMonthDate=function(){
var content=this.month+"-"+this.date,x=y=0;
//小时
x=400;y=270,
content=(content.length==1?(0+content):content);
var text = new Text({
x:x,
y:y,
text:content,
font:'20px ans-serif',
textAlign:'left',
fill:true,
fillStyle:'#44444'
});
this.renderArr2.push(text);
this.monthDateObj=text;
}
绘制农历
//绘制农历日
SpaceMan.prototype.drawMonthDate2=function(){
this.lunarObj = new LunarDay();
var lunarDay = this.lunarObj.getLunarDay(this.year,this.month,this.date);
var lunarDayArr = lunarDay.split(" ");
var content= lunarDayArr[1],x=y=0;
x=350;y=240;
text = new Text({
x:x,
y:y,
text:content,
font:'20px ans-serif',
textAlign:'left',
fill:true,
fillStyle:'#44444'
});
this.renderArr2.push(text);
this.lunarDateObj=text;
}
效果如下 :
到现在的话整体效果已经完成了,接下来就是要动起来
太空人转动
-
设置定时任务
-
每次更换一张图片绘制
代码
//太空人转动
SpaceMan.prototype.humanRotate=function(){
//不断的更新图片即可
this.imageKey--;
if(this.imageKey<this.startCount){
this.imageKey=60;
}
var img = this.renderArr2[0];
img.image = this.imgObj[this.imageKey];
this.render2();
}
效果
时间更新
更新秒
//秒钟转动
SpaceMan.prototype.updateSecond=function(){
this.second++;
if(this.second==60){//到达60以后设置为0
this.second=0;
this.updateMinute();//更新分钟
}
var content = this.second+"";
content=(content.length==1?(0+content):content);
this.secondObj.text=content;
}
更新分钟
//分钟更新
SpaceMan.prototype.updateMinute=function(){
this.minute++;
if(this.minute==60){
this.minute=0;
this.updateHour();
}
var content = this.minute+"";
content=(content.length==1?(0+content):content);
this.minuteObj.text=content;
}
更新小时
//时钟更新
SpaceMan.prototype.updateHour=function(){
this.hour+=1;
if(this.hour==24){
this.hour=0;
this.updateDate();
}
var content = this.hour+"";
content=(content.length==1?(0+content):content);
this.hourObj.text=content;
}
更新日、农历日
//更新日
SpaceMan.prototype.updateDate=function(){
var that=this,dateUpdateFlag=false,lastMonthDayLength,angleDis;
this.date+=1;//日期递增1
this.updateWeekDay();//更新周几
if(this.date>this.currentMonthDayLength){//大于本月最大天数就切换到1日
this.date=1;
this.updateMonth();//更新月份
}
this.monthDateObj.text = this.month+"-"+this.date;
this.updateMonthDate2();
}
//月更新
SpaceMan.prototype.updateMonth=function(){
var that=this;
this.month+=1;
if(this.month>12){
this.month=1;
}
}
//农历日期更新
SpaceMan.prototype.updateMonthDate2=function(){
var lunarDay = this.lunarObj.getLunarDay(this.year,this.month,this.date);
var lunarDayArr = lunarDay.split(" ");
var content= lunarDayArr[1];
this.lunarDateObj.text=content;
}
更新星期
//更新星期几
SpaceMan.prototype.updateWeekDay=function(){
this.day+=1;
//大于6则重置
if(this.day>6){
this.day=0;
}
//数组中获取周几
this.weekDayObj.text=this.weekArr[this.day];
}
最终效果
兄弟们给个三连支持一下,谢谢哇!
源码下载
关注下方公众号,回复
121
下载代码
★
更多源码
♥ 老父亲给女儿做的下雪特效,满足女儿看雪的愿望(附源码)♥
♥ 香港黄金配角吴孟达去世,80后程序员以轮播图来悼念达叔,达叔一路走好!(附源码)♥
♥ 原生js写的左侧飞入拼图特效,你是喜欢美女单飞还是双飞(附源码)♥
♥ 用js写的旋转木马,在新年献给各位刚登基的皇帝,让你的后宫转起来!程序员就是可以为所欲为!(附源码)♥
♥ 用js写的轮播图,八位女明星,你翻谁的牌,程序员就是可以为所欲为!(附源码)♥
♥ 原生js实现美女拼图,把美女老婆抱回家,5个美女够不够!程序员就是可以为所欲为!(附源码)♥
♥ 用js仿探探拖拽卡片的效果、飞卡片的效果,感觉挺酷,最后有美女看哦!(附源码)♥