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所有的基础知识,并且有众多练习案例, ...
随机推荐
- Problem A: 深入浅出学算法022-汉诺塔问题II
#include<stdio.h> void hanio(int n,char a,char b,char c) { ) printf("%c->%c\n",a, ...
- [NOIP2011]聪明的质检员
[问题描述] 小 T 是一名质量监督员,最近负责检验一批矿产的质量.这批矿产共有$n$个矿石,从 1 到$n$逐一编号,每个矿石都有自己的重量$w_i$以及价值$v_i$.检验矿产的流程是: 1. 给 ...
- 20172333 2017-2018-2 《Java程序设计》第5周学习总结
20172333 2017-2018-2 <Java程序设计>第5周学习总结 教材学习内容 1.if语句.if-else语句.switch语句 都是通过对于布尔表达式的结果来进行是否执行下 ...
- php红包
/** 转http://www.oschina.net/code/snippet_1392428_54532 谢谢 php_fangting * @param $total [你要发的红包 ...
- CentOS 6.9下KVM虚拟机网络Bridge(网桥)方式与NAT方式详解(转)
摘要:KVM虚拟机网络配置的两种方式:NAT方式和Bridge方式.Bridge方式的配置原理和步骤.Bridge方式适用于服务器主机的虚拟化.NAT方式适用于桌面主机的虚拟化. NAT的网络结构图: ...
- HDU 4631 Sad Love Story (2013多校3 1011题 平面最近点对+爆搞)
Sad Love Story Time Limit: 40000/20000 MS (Java/Others) Memory Limit: 131072/65536 K (Java/Others ...
- 通过dbf文件恢复oracle
可能有些人会由于各种原因经常重装系统,但是系统里面总是有一些比较重要的软件在重装后就不能用了.比如oracle数据库,众所周知数据库如果通过dmp导入导出麻烦不说,还很耗时,特别是像笔者一个数据库就有 ...
- Android-Universal-Image-Loader载入图片
直接看代码:MainActivity: package com.example.textwsjdemo; import com.nostra13.universalimageloader.cache. ...
- iOS:多线程同步加锁的简单介绍
多线程同步加锁主要方式有3种:NSLock(普通锁).NSCondition(状态锁).synchronized同步代码块 还有少用的NSRecursiveLock(递归锁).NSConditionL ...
- c#中的数组、ArrayList、List区别
首先说明C#中的Array类:Array 类是 C# 中所有数组的基类,它是在 System 命名空间中定义.Array 类提供了各种用于数组的属性和方法.关于Array类的一些属性及方法详见博文:C ...