canvas学习笔记(下篇) -- canvas入门教程--保存状态/变形/旋转/缩放/矩阵变换/综合案例(星空/时钟/小球)
【下篇】 -- 建议学习时间4小时 课程共(上中下)三篇
此笔记是我初次接触canvas的时候的学习笔记,这次特意整理为博客供大家入门学习,几乎涵盖了canvas所有的基础知识,并且有众多练习案例,建议大家学习10~15个小时,里面的案例请挨个敲一遍,这样才能转化为自己的知识。
技术要求:有html/css/js基础。
保存状态
save()restore()
save 和 restore 方法是用来保存和恢复 canvas 状态的,都没有参数。Canvas 的状态就是当前画面应用的所有样式和变形的一个快照。
简单示例:
var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d"); //线条链接处样式
ctx.fillStyle = "darkblue";
ctx.lineWidth = 1; ctx.fillRect(0,0,150,150);
ctx.save(); //第一次保存 保存了 fillStyle "darkblue" ctx.fillStyle = "#09f";
ctx.fillRect(15,15,120,120); ctx.save(); //第二次保存 保存了 fillStyle "#09f"
ctx.fillStyle = "#fff";
ctx.fillRect(30,30,90,90); ctx.restore(); //恢复到第二次保存的状态
ctx.fillRect(45,45,60,60); ctx.restore(); //恢复到第一次保存的状态
ctx.fillRect(60,60,30,30);
结果如下图:后两次 restore之后依次恢复到前面 save的颜色状态
画布变形
偏移
ctx.translate(disX,disY) ,将绘图上下文向x和y方向移动一个距离,然后再作画 (真实的画布并没有移动)
示例:
var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d"); /* 移动位置 */
ctx.fillRect(0,0,50,50); ctx.translate(100,100); //将画布x,y方向分别移动 100px ctx.fillStyle = "#09f";
ctx.fillRect(0,0,50,50);
这里我们可以看到,虽然绘制都是使用 fillRect(0,0,5.,50),但第二次绘制的时候上下文已经偏移了 100像素,所以落到画布上的时候就偏移了100像素
旋转
rotate(angle)
这个方法只接受一个参数:旋转的角度(angle),它是顺时针方向的,以弧度为单位的值。
示例:
var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d"); ctx.translate(100,100); for(var i=1; i<6; i++){
ctx.save();
ctx.fillStyle = 'rgba('+ 51*i +','+ (255-51*i) +',255,1)';
for(var j=0; j<i*6; j++){
ctx.rotate(Math.PI*2/(i*6));
ctx.beginPath();
ctx.arc(0,i*12.5,5,0,Math.PI*2,true);
ctx.fill();
}
ctx.restore()
}
缩放
scale(x, y)
scale 方法接受两个参数。x,y 分别是横轴和纵轴的缩放因子,它们都必须是正值。值比 1.0 小表示缩小,比 1.0 大则表示放大,值为 1.0 时什么效果都没有。
示例:
var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d"); ctx.translate(100,100); ctx.save();
ctx.translate(200,80);
drawSpirograph(ctx,22,30,30);
ctx.restore();
ctx.scale(0.75,1);
drawSpirograph(ctx,22,6,5); function drawSpirograph(ctx,R,r,O){
var x1 = R-O;
var y1 = 0;
var i = 1;
ctx.beginPath();
ctx.moveTo(x1,y1);
do {
if (i>20000) break;
var x2 = (R+r)*Math.cos(i*Math.PI/72) - (r+O)*Math.cos(((R+r)/r)*(i*Math.PI/72))
var y2 = (R+r)*Math.sin(i*Math.PI/72) - (r+O)*Math.sin(((R+r)/r)*(i*Math.PI/72))
ctx.lineTo(x2,y2);
x1 = x2;
y1 = y2;
i++;
} while (x2 != R-O && y2 != 0 );
ctx.stroke();
}
矩阵变换
transform(m11, m12, m21, m22, dx, dy)
这个方法是将当前的变形矩阵乘上一个基于自身参数的矩阵,在这里我们用下面的矩阵:
m11 m21 dx
m12 m22 dy
0 0 1
这个函数的参数各自代表如下:
m11:水平方向的缩放
m12:水平方向的偏移
m21:竖直方向的偏移
m22:竖直方向的缩放
dx:水平方向的移动
dy:竖直方向的移动
示例:
var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d"); ctx.translate(100,100); var sin = Math.sin(Math.PI/6);
var cos = Math.cos(Math.PI/6);
var c = 0;
for(var i=0; i<=12; i++){
c = Math.floor(255/12*i);
ctx.fillStyle = "rgb("+c+","+c+","+c+")";
ctx.fillRect(0,0,100,10);
ctx.transform(cos,sin,-sin,cos,0,0);
}
ctx.setTransform(1,0,0,1,100,100);
ctx.fillStyle = "rgba(255,128,255,0.5)";
ctx.fillRect(0,0,100,50);
综合案例
案例1:星空
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title></title>
<style>
*{margin: 0;padding: 0}
canvas{border: 1px solid #a4e2f9;margin: 10px;}
</style>
</head>
<body>
<canvas height="300" width="600" id="myCanvas"></canvas>
<script>
var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d"); //线条链接处样式
ctx.strokeStyle = "#09f";
ctx.lineWidth = 1; ctx.fillRect(0,0,450,450);
ctx.translate(150,150); ctx.beginPath();
ctx.arc(0,0,140,0,Math.PI*2,true);
ctx.clip(); //这个表示裁剪,只有路径区域中的部分才可见。 var lingrad = ctx.createLinearGradient(0,-175,0,250);
lingrad.addColorStop(0,"#232256");
lingrad.addColorStop(1,"#143778"); //绘制渐变背景
ctx.fillStyle = lingrad;
ctx.fillRect(-175,-175,350,350); //绘制星星
for(var j=1; j<100; j++){
ctx.save();
ctx.fillStyle = "#fff";
ctx.translate(140-Math.floor(Math.random()*280),140-Math.floor(Math.random()*280));
drawStar(ctx, Math.floor(Math.random()*8)+2);
ctx.restore();
} //绘制星星的方法
function drawStar(ctx,r){
ctx.save();
ctx.beginPath();
ctx.moveTo(r,0);
for(var i=0; i<9; i++){
ctx.rotate(Math.PI/5);
if(i%2 == 0){
ctx.lineTo((r/0.525731)*0.200811,0);
}else{
ctx.lineTo(r,0);
}
}
ctx.closePath();
ctx.fill();
ctx.restore();
} </script>
</body>
</html>
案例2:时钟
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title></title>
<style>
*{margin: 0;padding: 0}
canvas{border: 1px solid #a4e2f9;margin: 30px auto;display: block} </style>
</head>
<body>
<canvas height="300" width="600" id="myCanvas"></canvas> <script> function clock(){
var now = new Date();
var ctx = document.getElementById('myCanvas').getContext('2d');
ctx.save();
ctx.clearRect(0,0,150,150);
ctx.translate(75,75);
ctx.scale(0.4,0.4);
ctx.rotate(-Math.PI/2);
ctx.strokeStyle = "black";
ctx.fillStyle = "white";
ctx. lineWidth = 8;
ctx.lineCap = "round"; //时
ctx.save();
for(var i=0; i<12; i++){
ctx.beginPath();
ctx.rotate(Math.PI/6);
ctx.moveTo(100,0);
ctx.lineTo(120,0);
ctx.stroke();
}
ctx.restore(); //分
ctx.save();
ctx.lineWidth = 5;
for(var i=0; i<60; i++){
if(i%5 != 0){
ctx.beginPath();
ctx.moveTo(117,0);
ctx.lineTo(120,0);
ctx.stroke();
} ctx.rotate(Math.PI/30); }
ctx.restore(); var sec = now.getSeconds();
var min = now.getMinutes();
var hr = now.getHours();
hr = hr >= 12 ? hr-12 : hr; ctx.fillStyle = "black"; //时针
ctx.save();
ctx.rotate( hr*(Math.PI/6)+(Math.PI/360)*min+(Math.PI/21600)*sec );
ctx.lineWidth = 14;
ctx.beginPath();
ctx.moveTo(-20,0);
ctx.lineTo(80,0);
ctx.stroke();
ctx.restore(); //分针
ctx.save();
ctx.rotate( (Math.PI/30)*min+(Math.PI/1800)*sec );
ctx.lineWidth = 10;
ctx.beginPath();
ctx.moveTo(-20,0);
ctx.lineTo(112,0);
ctx.stroke();
ctx.restore(); //秒针
ctx.save();
ctx.rotate( (Math.PI/30)*sec );
ctx.strokeStyle = "#D40000";
ctx.fillStyle = "#D40000";
ctx.lineWidth = 6;
ctx.beginPath();
ctx.moveTo(-30,0);
ctx.lineTo(83,0);
ctx.stroke(); ctx.beginPath();
ctx.arc(0,0,10,0,Math.PI*2,true);
ctx.fill(); ctx.beginPath();
ctx.arc(95,0,10,0,Math.PI*2,true);
ctx.stroke(); ctx.restore(); //外圈
ctx.beginPath();
ctx.lineWidth = 14;
ctx.strokeStyle = '#325FA2';
ctx.arc(0,0,142,0,Math.PI*2,true);
ctx.stroke(); ctx. restore(); window.requestAnimationFrame(clock);
} window.requestAnimationFrame(clock); </script>
</body>
</html>
案例3:运动的小球
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title></title>
<style>
*{margin: 0;padding: 0}
canvas{border: 1px solid #a4e2f9;} </style>
</head>
<body>
<canvas height="300" width="600" id="myCanvas"></canvas> <script> var canvas = document.getElementById('myCanvas');
var ctx = canvas.getContext('2d');
var raf; //小球对象
var ball = {
x: 100,
y: 100,
vx: 5,
vy: 1,
radius: 25,
color: '#1895c3',
draw: function() {
ctx.beginPath();
ctx.arc(this.x, this.y, this.radius, 0, Math.PI*2, true);
ctx.closePath();
ctx.fillStyle = this.color;
ctx.fill();
}
}; //清除画布
function clear() {
ctx.fillStyle = 'rgba(255,255,255,0.3)';
ctx.fillRect(0,0,canvas.width,canvas.height);
} //小球运动及绘制的方法
function draw() {
clear();
ball.x += ball.vx;
ball.y += ball.vy; if (ball.y + ball.vy > canvas.height || ball.y + ball.vy < 0) {
ball.vy = -ball.vy;
}
if (ball.x + ball.vx > canvas.width || ball.x + ball.vx < 0) {
ball.vx = -ball.vx;
} ball.draw();
raf = window.requestAnimationFrame(draw);
} //鼠标移动的时候 让小球跟着鼠标走
canvas.addEventListener('mousemove', function(e){
clear();
ball.x = e.clientX;
ball.y = e.clientY;
ball.draw();
}); //鼠标移入停止动画
canvas.addEventListener("mouseenter",function(e){
window.cancelAnimationFrame(raf);
}); //鼠标移出继续动画
canvas.addEventListener("mouseout",function(e){
raf = window.requestAnimationFrame(draw);
}); //开始第一帧动画
draw(); </script>
</body>
</html>
今天就讲到这里,canvas基础部分就结束了,后续会找时间更新一些canvas比较炫酷的综合案例,请期待。
关注公众号,博客更新即可收到推送
canvas学习笔记(下篇) -- canvas入门教程--保存状态/变形/旋转/缩放/矩阵变换/综合案例(星空/时钟/小球)的更多相关文章
- canvas学习笔记:canvas对图片的像素级处理--ImageData的应用
学习了canvas的基本绘图功能后,惊喜的发现canvas对图片数据也有相当强大的处理功能,能够从像素级别操作位图,当然[lte ie8]不支持. 主要的函数有三个: ctx.createImageD ...
- canvas学习笔记、小函数整理
http://bbs.csdn.net/topics/391493648 canvas实例分享 2016-3-16 http://bbs.csdn.net/topics/390582151 html5 ...
- tensorflow学习笔记二:入门基础 好教程 可用
http://www.cnblogs.com/denny402/p/5852083.html tensorflow学习笔记二:入门基础 TensorFlow用张量这种数据结构来表示所有的数据.用一 ...
- Canvas学习:封装Canvas绘制基本图形API
Canvas学习:封装Canvas绘制基本图形API Canvas Canvas学习 从前面的文章中我们了解到,通过Canvas中的CanvasRenderingContext2D对象中的属性和方 ...
- 学习笔记《简明python教程》
学习笔记<简明python教程> 体会:言简意赅,很适合新手入门 2018年3月14日21:45:59 1.global 语句 在不使用 global 语句的情况下,不可能为一个定义于函数 ...
- Hadoop学习笔记(1) ——菜鸟入门
Hadoop学习笔记(1) ——菜鸟入门 Hadoop是什么?先问一下百度吧: [百度百科]一个分布式系统基础架构,由Apache基金会所开发.用户可以在不了解分布式底层细节的情况下,开发分布式程序. ...
- iOS学习笔记-地图MapKit入门
代码地址如下:http://www.demodashi.com/demo/11682.html 这篇文章还是翻译自raywenderlich,用Objective-C改写了代码.没有逐字翻译,如有错漏 ...
- canvas学习笔记(中篇) -- canvas入门教程-- 颜色/透明度/渐变色/线宽/线条样式/虚线/文本/阴影/图片/像素处理
[中篇] -- 建议学习时间4小时 课程共(上中下)三篇 此笔记是我初次接触canvas的时候的学习笔记,这次特意整理为博客供大家入门学习,几乎涵盖了canvas所有的基础知识,并且有众多练习案例, ...
- canvas学习笔记(上篇)-- canvas入门教程 -- canvas标签/方块/描边/路径/圆形/曲线
[上篇] -- 建议学习时间4小时 课程共(上中下)三篇 此笔记是我初次接触canvas的时候的学习笔记,这次特意整理为博客供大家入门学习,几乎涵盖了canvas所有的基础知识,并且有众多练习案例, ...
随机推荐
- 【枚举】【字符串哈希】Gym - 101164K - Cutting
给你A B两个串,让你切B串两刀,问你能否把切开的三个串拼成A. 哈希显然. #include<cstdio> #include<cstring> using namespac ...
- 【set】【Splay】【pb_ds】bzoj1208 [HNOI2004]宠物收养所
每次来的如果是人,且宠物数不为零,就从宠物中选出一个与其差距最小的,累加答案:若为零,就把他放入另一个集合里. 如果是宠物,则同上. 各种平衡树都可过,我蛋疼地用了pb_ds. Code: #incl ...
- 【深度优先搜索】mr353-取奶
应该是USACO的题目,暂时没有找到对应出处. [题目大意] 农夫约翰要量取 Q(1 <= Q <= 20,000)夸脱(夸脱,quarts,容积单位——译者注) 他的最好的牛奶,并把它装 ...
- [JZOJ5425]数论
题目大意: 给你$n,m$,求$\displaystyle{\sum_{i=1}^{n}\sum_{j=1}^{m}}\min(\lfloor\frac{n}{i}\rfloor,\lfloor\fr ...
- JDK源码学习笔记——Iterable/Iterator实现原理
Collection接口继承java.lang.Iterable接口,集合类重写了iterator()方法 public interface Iterable<T> { Iterator& ...
- Java高级架构师(一)第34节:Nginx的Http模块部分的指令
默认长链接的数目在100个 默认长链接的超时时间,一般在75S.
- ATL7窗口类详细剖析
前言: ATL是微软继MFC之后提供的一套C++模板类库,小巧.精妙.效率极高.它的主要作用是为我们编写COM/DOM/COM+程序提供了丰富的支持.但是ATL只能写COM么?我以前只是MFC程序员的 ...
- jquery接收后台数组或集合回显复选框
公司使用的框架比较旧,没有使用el等表达式.如果后台传递的是数组,需要把数组转为以逗号分隔的字符串. <% String context = request.getContextPath(); ...
- socket连接和http连接的区别
socket连接和http连接的区别 HTTP协议:简单对象访问协议,对应于应用层 ,HTTP协议是基于TCP连接的 tcp协议: 对应于传输层 ip协议: 对应于网络层 TCP/IP ...
- Android-经常涉及到的权限
Android中配置权限的方法: 在AndroidMainFest.xml中加上以下代码 Android中一些经常涉及到的权限: 添加WiFi以及访问网络的权限: <uses-permissio ...