一、canvas像素操作
(1)获取图像像素
imageData = ctx.getImageData(x,y,w,h)
imageData 对象
width
height
data
getImageData返回 ImageData 对象,该对象拷贝了画布指定矩形的像素数据
获取图像像素后的imageData中包含三项:
其中宽高分别为获取图像宽高时的大小,data则存放的是在该大小内所有的像素点的详细信息。data是一个数组,每四位秒数一个像素点,分别代表rgba。例如(创建像素的代码中也会提到):
(2)写入像素数据
ctx.putImageData(imgData,x,y,dirtyX,dirtyY,dirtyWidth,dirtyHeight);
imgData
: 规定要放回画布的 ImageData 对象。x,y
: ImageData 对象左上角的 x y坐标,以像素计。dirtyX,dirtyY
: 可选。水平值(x)、垂直值(y),以像素计,在画布上放置图像的位置。dw,dh
:可选。在画布上绘制图像所使用的宽度与高度。
(3)创建像素数据
ctx.createImageData(width, height);
width : ImageData 新对象的宽度。
height: ImageData 新对象的高度
注意:创建一个ImageData对象,默认创建出来的是个透明的
<script type="text/javascript">
// 获取画布
var canvas = document.getElementById('canvas');
// 设置画布的宽高
canvas.width = 800;
canvas.height = 600;
// 获取画笔
var ctx = canvas.getContext('2d');
ctx.fillStyle = 'red';
/*
imageData
width :横向像素点的个数
height:纵向像素点的个数
data: 数组 Uint8ClampedArray(40000) 类型的一维数组
100x100x4 (40000);
每个像素 的rgba信息
r : 0 -255
g : 0 -255
b : 0 -255
a : 0 -255 //透明度 (0-255)
*/
// 创建imageData对象
var imageData = ctx.createImageData(100, 100); //默认创建出来是一个透明的
for (var i = 0; i < imageData.data.length; i++) {
// 0 3 4 7
// 000 255 000 255
imageData.data[4 * i + 3] = 255; //设置该对象的A 为255 的透明的
}
// 写入像素
// x,y 左上角的 xy坐标位置
ctx.putImageData(imageData, 100, 100);
console.log(imageData);
</script>
(4)小案例,将canvasA画布的图像复制到canvasB中
第一步:分别获取画布
第二步:将第一个画布添加一张图片,让其在加载时渲染
第三部:书写点击事件函数,获取A画布内容,渲染到B画布中
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<style type="text/css">
body {
background-color: #99CCFF;
}
.canvas {
background-color: #fff;
}
button {
margin: 50px;
}
</style>
</head>
<body>
<canvas id="canvasA" class="canvas" width="600" height="360">
您的浏览器不支持,canvas标签
</canvas>
<canvas id="canvasB" class="canvas" width="600" height="360">
您的浏览器不支持,canvas标签
</canvas>
<button type="button" onclick="copyImg()">复制图像</button>
<script type="text/javascript">
// 获取画布 A
var canvasA = document.getElementById('canvasA');
// 获取画笔 A
var ctxA = canvasA.getContext('2d');
// 获取画布 B
var canvasB = document.getElementById('canvasB');
// 获取画笔 B
var ctxB = canvasB.getContext('2d');
/*
imageData
width :横向像素点的个数
height:纵向像素点的个数
data: 数组 Uint8ClampedArray(40000) 类型的一维数组
100x100x4 (40000);
每个像素 的rgba信息
r : 0 -255
g : 0 -255
b : 0 -255
a : 0 -255 //透明度 (0-255)
*/
// 给canvasA 绘制图像
var img = new Image();
// 设置图片路径
img.src = './img/hl.jpg';
// 图片加载完成时绘制
img.onload = function() {
ctxA.drawImage(img, 0, 0, img.width / 1.5, img.height / 1.5)
}
function copyImg() {
// 获取图像像素
var imageData = ctxA.getImageData(0, 0, canvasA.width, canvasA.height);
for (var i = 0; i < imageData.data.length; i++) {
// imageData.data[4*i+0]=0;//r
// imageData.data[4*i+1]=1;//g
imageData.data[4 * i + 2] = 2; //b
}
// 写入图像像素
ctxB.putImageData(imageData, 0, 0, 0, 0, canvasB.width, canvasB.height);
// x,y dx,dy 会进行累加
// ctxB.putImageData(imageData,50,50,50,50,canvasB.width,canvasB.height);
console.log(imageData);
}
</script>
</body>
</html>
二、canvas的save与restore
save
:Canvas状态存储在栈中,每当save()方法被调用后,当前的状态就被推送到栈中保存。可以调用任意多次 save方法
restore
:每一次调用 restore 方法,上一个保存的状态就从栈中弹出,所有设定都恢复。(类似数组的pop())
- canvas的save和restore的存储方式是一个栈(先进后出,后进先出),每次调用save及向栈中压入一个复杂的状态(包含样式等等)–进栈,每次调用一个restore及从栈顶中抽出一个复杂的状态(包含样式等等)–出栈。用户所当前调用的样式等等由当前栈顶状态所决定。结构如下图:
示例:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<style type="text/css">
body {
background-color: #99CCFF;
}
#canvas {
background-color: #fff;
color: rgba(255, 0, 0, 1);
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
margin: auto;
}
</style>
</head>
<body>
<canvas id="canvas">
您的浏览器不支持,canvas标签
</canvas>
<script type="text/javascript">
// 获取画布
var canvas = document.getElementById('canvas');
// 设置画布的宽高
canvas.width = 800;
canvas.height = 600;
// 获取画笔
var ctx = canvas.getContext('2d');
ctx.fillStyle = '#fa6900';
ctx.shadowOffsetX = 10;
ctx.shadowOffsetY = 10;
ctx.shadowBlur = 4;
ctx.shadowColor = 'rgba(204,204,204,0.5)';
ctx.fillRect(10, 10, 15, 150);
ctx.save(); //将第一个状态推入到栈中
ctx.fillStyle = '#f36';
ctx.shadowOffsetX = 10;
ctx.shadowOffsetY = 10;
ctx.shadowBlur = 4;
ctx.shadowColor = 'rgba(204,204,204,0.5)';
ctx.fillRect(50, 10, 30, 150);
ctx.save(); //将第二个状态推入到栈中
ctx.fillStyle = '#a7dbd7';
ctx.shadowOffsetX = 10;
ctx.shadowOffsetY = 10;
ctx.shadowBlur = 4;
ctx.shadowColor = 'rgba(204,204,204,0.5)';
ctx.fillRect(110, 10, 45, 150);
ctx.save(); //将第三个状态推入到栈中
ctx.restore(); //取出堆栈 3 (第三个状态)
ctx.beginPath();
ctx.arc(225, 75, 22, 0, Math.PI * 2, true);
ctx.closePath();
ctx.fill();
ctx.restore(); //取出堆栈 2 (第二个状态)
ctx.beginPath();
ctx.arc(320, 75, 15, 0, Math.PI * 2, true);
ctx.closePath();
ctx.fill();
ctx.restore(); //取出堆栈 3 (第三个状态)
ctx.beginPath();
ctx.arc(400, 75, 8, 0, Math.PI * 2, true);
ctx.closePath();
ctx.fill();
</script>
</body>
</html>
三、canvas的图像合成与变形
(1)canvas的图像合成
在canvas中提供globalCompositeOperation
即Canvas中的合成操作。
值 | 说明 |
---|---|
source-over | 这是默认设置,新图像会覆盖在原有图像。 |
source-in | 仅仅会出现新图像与原来图像重叠的部分,其他区域都变成透明的。 |
source-out | 仅仅显示新图像与老图像没有重叠的部分,其余部分全部透明。 |
source-atop | 新图像仅仅显示与老图像重叠区域。老图像仍然可以显示。 |
destination-over | 新图像会在老图像的下面。 |
destination-in | 仅仅新老图像重叠部分的老图像被显示,其他区域全部透明。 |
destination-out | 仅仅老图像与新图像没有重叠的部分。 注意显示的是老图像的部分区域。 |
destination-atop | 老图像仅仅仅仅显示重叠部分,新图像会显示在老图像的下面。 |
lighter | 新老图像都显示,但是重叠区域的颜色做加处理 |
darken | 保留重叠部分最黑的像素。(每个颜色位进行比较,得到最小的) |
示例:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<style type="text/css">
body {
background-color: #99CCFF;
}
#canvas {
background-color: #fff;
color: rgba(255, 0, 0, 1);
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
margin: auto;
}
</style>
</head>
<body>
<canvas id="canvas">
您的浏览器不支持,canvas标签
</canvas>
<script type="text/javascript">
// 获取画布
var canvas = document.getElementById('canvas');
// 设置画布的宽高
canvas.width = 800;
canvas.height = 600;
// 获取画笔
var ctx = canvas.getContext('2d');
/*
1.source-over 这是默认设置,新图像会覆盖在原有图像。
2.source-in 仅仅会出现新图像与原来图像重叠的部分,其他区域都变成透明的。
3.source-out 仅仅显示新图像与老图像没有重叠的部分,其余部分全部透明。
4.source-atop 新图像仅仅显示与老图像重叠区域。老图像仍然可以显示。
5.destination-over 新图像会在老图像的下面。
6.destination-in 仅仅新老图像重叠部分的老图像被显示,其他区域全部透明。
7.destination-out 仅仅老图像与新图像没有重叠的部分。 注意显示的是老图像的部分区域。
8.destination-atop 老图像仅仅仅仅显示重叠部分,新图像会显示在老图像的下面。
9.lighter 新老图像都显示,但是重叠区域的颜色做加处理
10.darken 保留重叠部分最黑的像素。(每个颜色位进行比较,得到最小的)
*/
ctx.fillStyle = 'blue';
ctx.fillRect(0, 0, 200, 200); //原有矩形 (目标)
// ctx.globalCompositeOperation='source-over';//源对象在在下面,新的图像在上面
// ctx.globalCompositeOperation='source-in';//只留下源与目标重叠的部分
// ctx.globalCompositeOperation='source-out';//只留下源超出目标的部分
// ctx.globalCompositeOperation='source-atop';//砍掉了源超出的部分
// ctx.globalCompositeOperation='destination-over';//目标在上面,旧的图像层级比较高
// ctx.globalCompositeOperation='destination-in';//z只留下源与目标重叠部分(目标的那部分)
// ctx.globalCompositeOperation='destination-out';//只留下目标超过源的部分
// ctx.globalCompositeOperation='destination-atop';// 砍掉目标溢出的部分
// ctx.globalCompositeOperation='lighter';//源与目标重叠,样式处理
ctx.globalCompositeOperation = 'darken'; //源与目标重叠,样式处理(每个颜色位进行比较,得到最小的)
ctx.fillStyle = 'red';
ctx.fillRect(100, 100, 200, 200); //新的矩形 (源)
</script>
</body>
</html>
(2)canvas变形
translate(x,y) 移动
- translate 方法用来移动 canvas 和它的原点到一个不同的位置。
translate(x, y)
translate 方法接受两个参数。x 是左右偏移量,y 是上下偏移量,如图所示。
样例:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<style type="text/css">
body {
background-color: #99CCFF;
}
#canvas {
background-color: #fff;
color: rgba(255, 0, 0, 1);
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
margin: auto;
}
</style>
</head>
<body>
<canvas id="canvas">
您的浏览器不支持,canvas标签
</canvas>
<script type="text/javascript">
// 获取画布
var canvas = document.getElementById('canvas');
// 设置画布的宽高
canvas.width = 800;
canvas.height = 600;
// 获取画笔
var ctx = canvas.getContext('2d');
/*
由于canvas是基于转态绘制的。在我们第一次平移之后,坐标系已经在(100,100),所以需要继续平移,再基于新的坐标系
继续平移坐标系。那怎么解决?
1. 每次使用完变换之后,记得将坐标系平移到原点,调用translate(-x,-y);
2.在每次平移之前使用save(),在每次绘制之后使用restore()恢复
*/
// ctx.save();
// ctx.fillStyle='orange';
// ctx.translate(100,100);
// ctx.fillRect(0,0,200,100);
// ctx.restore();//恢复状态
ctx.fillStyle = 'orange';
ctx.translate(100, 100);
ctx.fillRect(0, 0, 200, 100);
ctx.translate(-100, -100);
ctx.fillStyle = 'green';
ctx.translate(200, 200);
ctx.fillRect(0, 0, 200, 100);
</script>
</body>
</html>
rotate(angle)角度
- rotate 方法用于以原点为中心旋转 canvas。
- rotate(angle)这个方法只接受一个参数:旋转的角度(angle),它是顺时针方向的,以弧度为单位的值。
样例:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<style type="text/css">
body {
background-color: #99CCFF;
}
#canvas {
background-color: #fff;
color: rgba(255, 0, 0, 1);
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
margin: auto;
}
</style>
</head>
<body>
<canvas id="canvas">
您的浏览器不支持,canvas标签
</canvas>
<script type="text/javascript">
// 获取画布
var canvas = document.getElementById('canvas');
// 设置画布的宽高
canvas.width = 800;
canvas.height = 600;
// 获取画笔
var ctx = canvas.getContext('2d');
// rotate 旋转 不是针对图形旋转,而是针对画布本身
// 顺时针 往下旋转,-° 逆时针旋转
ctx.fillStyle = 'orange';
ctx.fillRect(50, 50, 100, 50);
ctx.rotate(30 * Math.PI / 180); //顺时针旋转30°
ctx.fillStyle = 'green';
ctx.fillRect(50, 50, 100, 50);
</script>
</body>
</html>
样例用旋转绘制折扇图:
<script type="text/javascript">
// 获取画布
var canvas = document.getElementById('canvas');
// 设置画布的宽高
canvas.width = 800;
canvas.height = 600;
// 获取画笔
var ctx = canvas.getContext('2d');
// rotate 旋转 不是针对图形旋转,而是针对画布本身
// 顺时针 往下旋转,-° 逆时针旋转
var colors = ['#999933', '#FFFFCC', '#CC99CC', '#CC9966', '#999999', '#9966CC'];
// 利用多次旋转绘制折扇图
for (var i = 0; i < colors.length; i++) {
ctx.fillStyle = colors[i];
ctx.fillRect(0, 0, 200, 50);
ctx.rotate(15 * Math.PI / 180);
}
</script>
scale(x,y)缩放
- 缩放用来增减图形在 canvas 中的像素数目,对形状,位图进行缩小或者放大。
- scale(x, y) 该方法接受两个参数。x,y 分别是横轴和纵轴的缩放因子,它们都必须是正值。 值比 1.0 小表示缩小,比 1.0大表示放大,值为 1.0 时什么效果都没有。
样例:
<script type="text/javascript">
// 获取画布
var canvas = document.getElementById('canvas');
// 设置画布的宽高
canvas.width = 800;
canvas.height = 600;
// 获取画笔
var ctx = canvas.getContext('2d');
ctx.fillStyle = 'orange';
ctx.fillRect(50, 50, 100, 50);
/*
scale 缩放的是画布上1个单位距离,不是缩放的画布;
缩放并非是图像,而是整个坐标系
*/
// ctx.scale(0.5,2);//正数
ctx.scale(-0.5, 2); //负数
ctx.translate(-100, 50);
ctx.fillStyle = 'green';
ctx.fillRect(0, 0, 100, 50);
</script>
(3)使用canvas变形设计小球匀速运动
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<style type="text/css">
body {
background-color: #99CCFF;
}
#canvas {
background-color: #fff;
color: rgba(255, 0, 0, 1);
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
margin: auto;
}
</style>
</head>
<body>
<canvas id="canvas">
您的浏览器不支持,canvas标签
</canvas>
<script type="text/javascript">
// 获取画布
var canvas = document.getElementById('canvas');
// 设置画布的宽高
canvas.width = 800;
canvas.height = 600;
// 获取画笔
var ctx = canvas.getContext('2d');
// 小球左右来回均速运动
var ball = {
x: 10,
y: 50,
direction: 1, //小球方向
draw: function() {
ctx.beginPath();
ctx.arc(this.x, this.y, 10, 0, 360 * Math.PI / 180);
ctx.fillStyle = 'orange';
ctx.closePath();
ctx.fill();
}
}
// 设置小球左右移动
function ball_animate() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.beginPath();
ctx.moveTo(0, 100);
ctx.lineTo(400, 100);
ctx.fillStyle = 'orange';
ctx.stroke();
ball.draw();
// 设置小球从左往右跑
if (ball.direction == 1) {
ball.x += 100 / 60; //16.6
} else {
// 从右 往左边跑
ball.x -= 200 / 60; //3.3
}
// 设置运动的边界
if (ball.x > 400) {
ball.x = 400;
ball.direction = 0;
}
if (ball.x < 0) {
ball.x = 0;
ball.direction = 1;
}
window.requestAnimationFrame(ball_animate);
}
window.requestAnimationFrame(ball_animate);
</script>
</body>
</html>
(4)使用canvas变形设计行星围绕太阳转
从帖子中看到一个非常好的案例分享给大家(以下是原文代码,给大家整理好了): 点击跳转.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>模拟太阳系旋转</title>
<style type="text/css">
body {
background-color: #99CCFF;
}
#canvas {
background-color: #fff;
color: rgba(255, 0, 0, 1);
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
margin: auto;
}
</style>
</head>
<body>
<canvas id="canvas" width="1000" height="1000" style="background:#000">
您的浏览器不支持,canvas标签
</canvas>
<script>
//设置2d绘图环境
var ctx = document.getElementById("canvas").getContext("2d");
//画轨道
function drawTrack() {
for (var i = 0; i < 8; i++) {
ctx.beginPath();
ctx.arc(500, 500, (i + 1) * 50, 0, 360, false);
ctx.closePath();
ctx.strokeStyle = "#fff";
ctx.stroke();
}
}
//画星球的类
function Star(x, y, radius, cycle, sColor, eColor) {
//设置星球类的属性
this.x = x; //星球的坐标点
this.y = y;
this.radius = radius; //星球的半径
this.cycle = cycle; //设置周期
this.sColor = sColor; //星球的颜色,起始颜色和结束颜色
this.eColor = eColor;
this.color = null;
//设置一个计时器
this.time = 0;
//给星球类定义一个方法
this.draw = function() {
ctx.save(); //保存之前的内容
ctx.translate(500, 500); //重置0,0坐标
ctx.rotate(this.time * (360 / this.cycle) * Math.PI / 180); //旋转角度
//画星球
ctx.beginPath();
ctx.arc(this.x, this.y, this.radius, 0, 360, false);
ctx.closePath();
//设置星球的填充颜色
this.color = ctx.createRadialGradient(this.x, this.y, 0, this.x, this.y, this.radius);
this.color.addColorStop(0, this.sColor);
this.color.addColorStop(1, this.eColor);
ctx.fillStyle = this.color;
ctx.fill();
//恢复之前画布的内容
ctx.restore();
this.time += 1;
}
}
//创建一个太阳的构造函数
function Sun() {
Star.call(this, 0, 0, 20, 0, "#FFFF00", "#FF9900");
}
//创建一个水星的构造函数
function Mercury() {
Star.call(this, 0, -50, 10, 87.70, "#A69697", "#5C3E40");
}
//创建一个金星的构造函数
function Venus() {
Star.call(this, 0, -100, 10, 224.701, "#C4BBAC", "#1F1315");
}
//创建一个地球的构造函数
function Earth() {
Star.call(this, 0, -150, 10, 365.2422, "#78B1E8", "#050C12");
}
//创建一个火星的构造函数
function Mars() {
Star.call(this, 0, -200, 10, 686.98, "#CEC9B6", "#76422D");
}
//创建一个木星的构造函数
function Jupiter() {
Star.call(this, 0, -250, 10, 4332.589, "#C0A48E", "#322222");
}
//创建一个土星的构造函数
function Saturn() {
Star.call(this, 0, -300, 10, 10759.5, "#F7F9E3", "#5C4533");
}
//创建一个天王星的构造函数
function Uranus() {
Star.call(this, 0, -350, 10, 30799.095, "#A7E1E5", "#19243A");
}
//创建一个海王星的构造函数
function Neptune() {
Star.call(this, 0, -400, 10, 60152, "#0661B2", "#1E3B73");
}
var sun = new Sun();
var mercury = new Mercury();
var venus = new Venus();
var earth = new Earth();
var mars = new Mars();
var jupiter = new Jupiter();
var saturn = new Saturn();
var uranus = new Uranus();
var neptune = new Neptune();
function Move() {
ctx.clearRect(0, 0, 1000, 1000);
drawTrack();
sun.draw();
mercury.draw();
venus.draw();
earth.draw();
mars.draw();
jupiter.draw();
saturn.draw();
uranus.draw();
neptune.draw();
}
setInterval(Move, 10);
</script>
</body>
</html>
制作整理不易,参考了部分官方文档、帖子和老师整理的案例。如要引用请附上本文链接,如有疑问可以在评论区畅所欲言,作者看到会第一时间回复,欢迎交流!