canvas绘制太阳系
原文地址:canvas绘制太阳系
学习canvas有一段时间了,顺便写个小项目练手,该项目用到的知识点包括:
- ES6面向对象
- 基本的三角函数
- canvas部分有:坐标变换,渐变,混合模式,线条和图形的绘制。
实际效果: solar system(推荐在chrome或safari下运行)
场景
首先建立场景类,主要用来组织管理对象,统一更新和绘制对象。这里用到了ES6的类语法,构造函数建立对象列表属性planets,绘制背景方法drawBG,使用requestAnimationFrame反复执行的动画方法animate
绘制背景使用到了径向渐变:createRadialGradient(x1,y1,r1,x2,y2,r2); 该渐变主要用于创建两个圆相交过渡效果,如果前后两个圆心相同(x1x2 && y1y2),则会构造同心圆样式的渐变。 这样我们就以太阳为中心的黄色调渐变到黑色,最后用fillRect填充整个背景。
//场景
class Stage {
constructor(){
this.planets=[];
}
init(ctx){
ctx.translate(W/2,H/2);//坐标重置为中间
this.animate(ctx);
}
//绘制背景
drawBG(ctx){
ctx.save();
ctx.globalCompositeOperation = "source-over";
var gradient=ctx.createRadialGradient(0,0,0,0,0,600);
gradient.addColorStop(0,'rgba(3,12,13,0.1)');
gradient.addColorStop(1,'rgba(0,0,0,1');
ctx.fillStyle=gradient;
// ctx.fillStyle='rgba(0,0,0,0.9)';
ctx.fillRect(-W/2,-H/2,W,H);
ctx.restore();
}
//执行动画
animate(ctx){
var that=this,
startTime=new Date();
(function run(){
that.drawBG(ctx);
that.planets.forEach(item=>{
item.update(startTime);
item.draw(ctx);
});
requestAnimationFrame(run);
}());
}
}
星球
然后建立星球基类,除构造函数,还有更新位置角度的方法Update,对象绘制方法draw。之后所有的星球,都会初始化该类或者继承该类建立对应星球。
行星绕太阳做圆周运动,这个可以用三角函数根据角度和半径求出x,y,但还有更加方便的方法,那就是使用canvas提供的坐标旋转方法rotate,以360度为一个周期。
/**
* 星球基类
*/
class Planet{
/**
* @param {Number} x x坐标
* @param {Number} y y坐标
* @param {Number} r 半径
* @param {Number} duration 周期(秒)
* @param {Object} fillStyle
* @param {Object} blurStyle
*/
constructor(x,y,r,duration,fillStyle,blurStyle){
this.x=x;
this.y=y;
this.r=r;
this.duration=duration;
this.angle=0;
this.fillStyle=fillStyle;
this.blurStyle=blurStyle;
}
update(startTime){
this.angle=Tween.linear(new Date()-startTime,0,Math.PI*2,this.duration*1000);
}
draw(ctx){
ctx.save();
ctx.rotate(this.angle);
// ctx.translate(this.x,this.y);
drawCircle(this.x,this.blurStyle.color);
ctx.beginPath();
// ctx.globalCompositeOperation = "lighter";
ctx.fillStyle=this.fillStyle;
ctx.shadowColor=this.blurStyle.color;
ctx.shadowBlur=this.blurStyle.blur;
// ctx.arc(0,0,this.r,Math.PI*2,false);
ctx.arc(this.x,this.y,this.r,0,Math.PI*2,false);
ctx.fill();
ctx.restore();
}
};
太阳
开始建立第一个对象-太阳,继承上面的星球基类Planet,重写draw方法
/**
* 太阳
*/
class Sun extends Planet{
draw(ctx){
ctx.save();
ctx.beginPath();
ctx.globalCompositeOperation = "source-over";
ctx.fillStyle=this.fillStyle;
ctx.shadowColor=this.blurStyle.color;
ctx.shadowBlur=this.blurStyle.blur;
ctx.arc(this.x,this.y,this.r,0,Math.PI*2,false);
ctx.fill();
ctx.restore();
}
}
土星
土星有美丽的土星环,所以也继承出一个单独的类,重写draw方法,其中土星环比较麻烦,建立了很多颜色节点的径向渐变。
/**
* 土星
*/
class Saturn extends Planet{
draw(ctx){
ctx.save();
ctx.rotate(this.angle);
drawCircle(this.x,this.blurStyle.color);
ctx.beginPath();
ctx.fillStyle=this.fillStyle;
ctx.arc(this.x,this.y,this.r,Math.PI*2,false);
ctx.fill();
//土星光环
ctx.globalCompositeOperation = "source-over";
var gradient=ctx.createRadialGradient(this.x,this.y,0,this.x,this.y,this.r+25);
var startStop=(this.r+3)/(this.r+24);
gradient.addColorStop(startStop,'#282421');
gradient.addColorStop(startStop+0.06,'#282421');
gradient.addColorStop(startStop+0.1,'#7e7966');
gradient.addColorStop(startStop+0.18,'#706756');
gradient.addColorStop(startStop+0.24,'#7e7966');
gradient.addColorStop(startStop+0.25,'#282421');
gradient.addColorStop(startStop+0.26,'#282421');
gradient.addColorStop(startStop+0.27,'#807766');
gradient.addColorStop(1,'#595345');
ctx.fillStyle=gradient;
ctx.beginPath();
ctx.arc(this.x,this.y,this.r+24,0,Math.PI*2,true);
ctx.arc(this.x,this.y,this.r+3,0,Math.PI*2,false);
ctx.fill();
ctx.restore();
}
}
建立星球
接着开始初始化星球对象,包括太阳和八大行星,然后所有的星球颜色都使用了径向渐变,这样更加的美观。这里给出太阳,水星,土星的例子,其他的行星如此类推。
// 初始化场景类
var stage=new Stage();
// sun
var sunStyle=ctx.createRadialGradient(0,0,0,0,0,60);
sunStyle.addColorStop(0,'white');
sunStyle.addColorStop(0.5,'white');
sunStyle.addColorStop(0.8,'#ffca1e');
sunStyle.addColorStop(1,'#b4421d');
var sun=new Sun(0,0,60,0,sunStyle,{color:'#b4421d',blur:300});
stage.planets.push(sun);
// mercury
var mercuryStyle=ctx.createRadialGradient(100,0,0,100,0,9);
mercuryStyle.addColorStop(0,'#75705a');
mercuryStyle.addColorStop(1,'#464646');
var mercury=new Planet(100,0,9,8.77,mercuryStyle,{color:'#464646'});
stage.planets.push(mercury);
//saturn
var saturnStyle=ctx.createRadialGradient(500,0,0,500,0,26);
saturnStyle.addColorStop(0,'#f2e558');
saturnStyle.addColorStop(1,'#4c4a3b');
var saturn =new Saturn(500,0,26,1075.995,saturnStyle,{color:'#4c4a3b'});
stage.planets.push(saturn);
小行星带
当然还有火星和木星之间的小行星带,同理继承星球基类,这里用到了图像混合模式globalCompositeOperation,使用xor可以和背景对比度没那么突兀。当然还有其他属性值,比如source-over, lighter等。这里我们随机生成了300个对象,一样填充进场景类的planets属性统一更新绘制。
/**
* 小行星
*/
class Asteroid extends Planet{
draw(ctx){
ctx.save();
ctx.rotate(this.angle);
ctx.beginPath();
ctx.globalCompositeOperation = "xor";
ctx.fillStyle=this.fillStyle;
ctx.arc(this.x,this.y,this.r,0,Math.PI*2,false);
ctx.fill();
ctx.restore();
}
}
function createAsteroids(){
var asteroid=null,
x=300,y=0, r=2,rd=300,
angle=0, d=283,
color='#fff';
for(var i=0;i<400;i++){
rd=Random(300,320);
angle=Random(0,Math.PI*2*1000)/1000;
x=Math.round(Math.cos(angle)*rd);
y=Math.round(Math.sin(angle)*rd);
r=Random(1,3);
d=Random(28.3,511);
color=getAsteroidColor();
// console.log(angle,color);
asteroid = new Asteroid(x,y,r,d,color,{color:color,blur:1});
stage.planets.push(asteroid);
}
}
彗星
基本快完成了,但我们除此之外,可以再添加做椭圆运动的彗星,这样更加酷。一样随机生成20个彗星填充进场景类统一更新绘制。
/**
* 彗星
*/
class Comet {
constructor(cx,cy,a,b,r,angle,color,duration){
this.a=a;
this.b=b;
this.r=r;
this.cx=cx;
this.cy=cy;
this.x=0;
this.y=0;
this.color=color;
this.angle=angle;
this.duration=duration;
}
update(startTime){
var t=Tween.linear(new Date()-startTime,0,Math.PI*2,this.duration*1000);
this.x=this.cx+this.a*Math.cos(this.angle+t);
this.y=this.cy+this.b*Math.sin(this.angle+t);
}
draw(){
ctx.save();
ctx.rotate(this.angle);
//画运动轨迹
ctx.lineWidth=0.5;
ctx.strokeStyle='rgba(15,69,116,0.2)';
Shape.ellipse(ctx,this.cx,this.cy,this.a,this.b);
//画球
ctx.beginPath();
// ctx.globalCompositeOperation = "lighter";
ctx.globalCompositeOperation = "source-atop";
ctx.shadowColor=this.color;
ctx.shadowBlur=1;
ctx.fillStyle=this.color;
ctx.arc(this.x,this.y,this.r,0,Math.PI*2,false);
ctx.fill();
//画尾迹
ctx.restore();
}
}
function createComets(){
var l=180,
a=800,b=300,
cx=a-l,cy=0,
r=3,duration=30,angle=0,color='#fff',
comet = null;
for(var i=0;i<20;i++){
l=Random(120,350)
a=Random(600,1000);
b=a/Random(1,3);
cx=a-l;
r=Random(2,4);
angle=Random(0,Math.PI*2*1000)/1000;
color=getCometColor();
duration=Random(20,100);
stage.planets.push(new Comet(cx,cy,a,b,r,angle,color,duration));
}
}
运动轨迹
最后的细节,就是标识出行星圆周运动的轨道,当然最简单的是按运动半径画个圆。但我们用线性渐变添加好看的尾迹,这样效果更好
function drawCircle(r,color){
var hsl=Color.hexToHsl(color);
ctx.lineWidth=1;
// ctx.strokeStyle='rgba(176,184,203,0.3)';
// ctx.arc(0,0,this.x,Math.PI*2,false);
// ctx.stroke();
var gradient=ctx.createLinearGradient(-r,0,r,0);
gradient.addColorStop(0,'hsla('+hsl[0]+','+hsl[1]+'%,0%,.3)');
gradient.addColorStop(0.6,'hsla('+hsl[0]+','+hsl[1]+'%,50%,.9)');
gradient.addColorStop(1,'hsla('+hsl[0]+','+hsl[1]+'%,80%,1)');
ctx.strokeStyle=gradient;
// ctx.shadowColor=color;
// ctx.shadowBlur=4;
ctx.beginPath();
ctx.arc(0,0,r,0,Math.PI,true);
ctx.stroke();
}
最后
所有的部分都已经完成,我们只需要启动场景类即可
createAsteroids();
createComets();
stage.init(ctx);
canvas绘制太阳系的更多相关文章
- HTML5 之Canvas绘制太阳系
<!DOCTYPE html> <html> <head> <title>HTML5_Canvas_SolarSystem</title> ...
- HTML5学习总结——canvas绘制象棋(canvas绘图)
一.HTML5学习总结——canvas绘制象棋 1.第一次:canvas绘制象棋(笨方法)示例代码: <!DOCTYPE html> <html> <head> & ...
- 用canvas绘制折线图
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- 封装 用canvas绘制直线的函数--面向对象
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- 学习笔记:HTML5 Canvas绘制简单图形
HTML5 Canvas绘制简单图形 1.添加Canvas标签,添加id供js操作. <canvas id="mycanvas" height="700" ...
- canvas绘制经典折线图(一)
最终效果图如下: 实现步骤如下:注-引用了jQuery HTML代码 <!doctype html> <html lang="en"> <head&g ...
- Canvas绘制图形
1.Canvas绘制一个蓝色的矩形 <!DOCTYPE html> <html> <head lang="en"> <meta chars ...
- [canvas]利用canvas绘制自适应的折线图
前段时间学习了用canvas绘制折现图,且当画布变换大小,折现图会随之变化,现附上代码 <!DOCTYPE html> <html lang="en"> & ...
- 使用Canvas绘制背景图
原文 http://www.imququ.com/post/use-canvas-as-background-image.html 最近iCloud Web的Beta版换了UI,整体风格变得和iOS ...
随机推荐
- Junit4学习(六)Junit4参数化设置
一,背景, 有时候会对相同的代码结构做同样的操作,不同的时对参数的设置数据和预期结果:有没有好的办法提取出来相同的代码,提高代码的可重用度,junit4中使用参数化设置,来处理此种场景: 二,代码展示 ...
- 前端基础之HTML
一.HTML 段落是通过 <p> 标签进行定义的 如: <p> hello world! </p> <html> 与 </html> 之间的 ...
- 13.Linux键盘驱动 (详解)
版权声明:本文为博主原创文章,未经博主允许不得转载. 在上一节分析输入子系统内的intput_handler软件处理部分后,接下来我们开始写input_dev驱动 本节目标: 实现键盘驱动,让开发板的 ...
- 再起航,我的学习笔记之JavaScript设计模式23(中介者模式)
中介者模式 概念介绍 中介者模式(Mediator):通过中介者对象封装一系列对象之间的交互,使对象之间不再相互引用降低他们之间的耦合,有时中介者对象也可以改变对象之间的交互. 创建一个中介 中介者模 ...
- Shiro第三篇【授权、自定义reaml授权】
Shiro授权 上一篇我们已经讲解了Shiro的认证相关的知识了,现在我们来弄Shiro的授权 Shiro授权的流程和认证的流程其实是差不多的: Shiro支持的授权方式 Shiro支持的授权方式有三 ...
- 【译】The Accidental DBA:Troubleshooting
最近重新翻看The Accidental DBA,将Troubleshooting部分稍作整理,方便以后查阅.此篇是Part 3Part 1:The Accidental DBA:SQL Server ...
- 在 macOS High Sierra 10.13 搭建 PHP 开发环境
2017 年 9 月 26 日,苹果公司正式发布了新一代 macOS,版本为 High Sierra (11.13). macOS High Sierra 预装了 Ruby(2.3.3).PHP(7. ...
- ios小型服务器环境配置
之前买的一台二手iphone4退役了,上闲鱼上一看,就值200,而且耳机声音也有点轻,估计买不了什么钱 于是网上看看能不能有什么废物利用的法子,看到说做行车记录仪的,有说做git服务器的,感觉挺有兴趣 ...
- c++builder中 扩展c++的关键字 : _published _automated Get/Set指令 _fastcall
C++Builder为C++增加了许多关键字,以适应其快速应用开发(RAD)环境.包括关键字和Get/Set指令. 1._published类似publich权限范围,_published像publi ...
- 化繁为简 经典的汉诺塔递归问题 in Java
问题描述 在世界中心贝拿勒斯(在印度北部)的圣庙里,一块黄铜板上插着三根宝石针.印度教的主神梵天在创造世界的时候,在其中一根针上从下到上地穿好了由大到小的64片金片,这就是所谓的汉诺塔.不论白天黑 ...