Canvas
canvas 是HTML5新出的标签,可以用来做小游戏,特效,作图等,自己并没有作画能力,只能通过Javascript脚本来操控
Canvas标准
http://www.w3c.org/TR/2dcontext/
https://html.spec.whatwg.org/
创建Canvas
创建canvas几个主要的问题:
1.不能通过CSS设置画布的大小,否则会造成画布拉伸变形等问题,只能设置本身自带width、height属性,也可以在js里设置
2.兼容性:对一些不支持的浏览器,可以在标签内输入提示,不支持的浏览器会显示此提示、支持的浏览器会自动忽略掉
3.创建并设置好宽高后,通过js获取,还要设置其getContext,成功返回一个对象后即可作画,这里用js也可判断其是否支持canvas
语法格式:
<canvas width="1024" height="570" class="canvas">由于您的浏览器版本过低,此图片不能加载</canvas>
<script>
var ctx = document.querySelector(".canvas").getContext("2d|3d");
</script>
线
画布的x、y起点默认在右上角的位置(0,0), 分别对应的最大值是画布的宽和高
绘制线条的函数:
moveTo(x,y) : 开始位置
lineTo(x,y):结束位置
stroke :准备好后,开始画线条
设置线条样式的函数:
lineWidth:设置线条粗细
strokeStyle:设置线条的颜色
lineCap:设置线条首尾处的形状 俗称帽子
lineJoin:设置连接处的样式
miterLimit:内角与外角的距离。默认值是10,此属性只有在lineJoin = “miter”并且有设置线条粗细情况下才有效,且斜接长度大于miterLimit ,线条连接处自动斜切(lineJoin =”bevel”)
另起一条路径的函数:
beginPath:起始一条路径,或重置当前路径
closePath:创建从当前点回到起始点的路径
语法格式:
<canvas width="1024" height="760" class="canvas">由于您的浏览器版本过低,此图片不能加载</canvas><script>
var ctx = document.querySelector(".canvas").getContext("2d|3d");
ctx.beginPath();
ctx.moveTo(100,100);
ctx.lineTo(700,700);
ctx.lineTo(100,700);
ctx.lineTo(100,100);
ctx.lineWidth = 5;
ctx.strokeStyle = "green";
ctx.stroke();
ctx.closePath();
ctx.beginPath();
ctx.lineCap = "round" //butt(default) round圆头 square方头
//lineCap 有时可以填补连接处的空缺
ctx.moveTo(200,200);
ctx.lineTo(400,400);
ctx.lineTo(200,400);
ctx.lineTo(200,200);
ctx.lineWidth = 5;
ctx.strokeStyle = "yellow";
ctx.stroke();
ctx.closePath();
</script>
===============================================================
ctx.lineWidth = 15;
DrawStart(ctx, 100, 20, 400, 400, -30);
ctx.lineJoin = "miter"; //可以设置的样式有:miter(default)、 bevel斜接、 round圆角
ctx.miterLimit = 7; //如果斜接长度超过 miterLimit 的值,边角会以 lineJoin 的 "bevel" 类型来显示。,这里等于7时,边角会以lineJoin的bevel显示,等于8时则会以miter显示
//斜接长度指的是在两条线交汇处内角和外角之间的距离。
ctx.stroke();
function DrawStart(context, R, r, x, y, rotate) {
//绘画对象 大圆的半径 小圆半径 x轴 y轴
ctx.beginPath();
for (var i = 0; i < 5; i++) {
ctx.lineTo(
Math.cos(((18 + 72 * i - rotate) / 180) * Math.PI) * R + x,
-Math.sin(((18 + 72 * i - rotate) / 180) * Math.PI) * R + y
);
ctx.lineTo(
Math.cos(((54 + 72 * i - rotate) / 180) * Math.PI) * r + x,
-Math.sin(((54 + 72 * i - rotate) / 180) * Math.PI) * r + y
);
}
ctx.closePath();
}
填充
填充函数:
fill :填充当前路径 (注意:如果路径未关闭,那么 fill() 方法会从路径结束点到开始点之间添加一条线,以关闭该路径,然后填充该路径)
填充样式:
fillStyle:填充颜色
语法格式:
<canvas width="1024" height="760" class="canvas">由于您的浏览器版本过低,此图片不能加载</canvas><script>
var ctx = document.querySelector(".canvas").getContext("2d|3d");
ctx.beginPath();
ctx.moveTo(100,100);
ctx.lineTo(700,700);
ctx.lineTo(100,700);
ctx.lineTo(100,100);
ctx.lineWidth = 5;
ctx.strokeStyle = "green";
ctx.stroke();
ctx.fillStyle = "red";
ctx.fill()
ctx.closePath();
ctx.beginPath();
ctx.moveTo(200,200);
ctx.lineTo(400,400);
ctx.lineTo(200,400);
ctx.lineTo(200,200);
ctx.lineWidth = 5;
ctx.strokeStyle = "yellow";
ctx.stroke();
ctx.closePath();
</script>
案例:四个正方形组成的大方形
var ctx = document.querySelector(".canvas").getContext("2d");
let box = [
{p:[{x:0,y:0},{x:512,y:0},{x:512,y:380},{x:0,y:380},{x:0,y:0}],color:"red"},
{p:[{x:512,y:0},{x:1024,y:0},{x:1024,y:380},{x:512,y:380},{x:512,y:0}],color:"green"},
{p:[{x:0,y:380},{x:512,y:380},{x:512,y:760},{x:0,y:760},{x:0,y:380}],color:"blue"},
{p:[{x:512,y:380},{x:1024,y:380},{x:1024,y:760},{x:512,y:760},{x:512,y:380}],color:"yellow"},
]
for(let i=0;i<box.length;i++)
{
draw(box[i],ctx);
}
function draw(p,c)
{
c.beginPath();
c.moveTo(p.p[0].x,p.p[0].y);
for(let i=1;i<p.p.length;i++)
{
c.lineTo(p.p[i].x,p.p[i].y);
}
c.lineWidth = 5;
c.strokeStyle = p.color;
c.fillStyle = p.color;
c.stroke();
c.fill();
c.closePath();
}
注意点:
- closePath可以解决闭合图形的空隙问题,也有自动闭合的作用。
- 当边框或者填充被遮挡掉的时候,这时可以先填充在画边框,或者先画边框再画线
- 后绘制的图形会顶替掉前面的图形
圆
画圆的函数:
arc(x,y,r,start,end,true|false):画圆\弧
arcTo(x1,y1,x2,y2,r):绘制圆弧
语法格式:
var ctx = document.querySelector(".canvas").getContext("2d");
var i = 0;
setInterval(() => {
i++;
ctx.fillStyle = "yellow";
ctx.strokeStyle = "red";
ctx.beginPath();
ctx.arc(512, 380, 100, 0, Math.PI * 2 - (Math.PI * 2 * i / 30), true);
// x y 半径 开始位置 结束位置 是否逆时针旋转
ctx.lineWidth = 10;
ctx.stroke();
ctx.fill();
}, 1);
//顺时针:Math.PI * 2 * i / 30
//逆时针:Math.PI * 2 - (Math.PI * 2 * i / 30)
//使用arcTo绘圆角矩形
var ctx = document.querySelector("canvas").getContext("2d");
// ctx.translate(200,200)
// ctx.beginPath();
// ctx.moveTo(100,0);
// ctx.arcTo(400,0,400,800,100);
// ctx.lineTo(400,400);
// ctx.arcTo(400,500,350,500,100);
// ctx.lineTo(100,500);
// ctx.arcTo(0,500,0,450,100);
// ctx.lineTo(0,100);
// ctx.arcTo(0,0,100,0,100)
// ctx.closePath();
function RoundRect(ctx, width, height, r)
{
ctx.beginPath();
ctx.moveTo(r, 0);
ctx.arcTo(width, 0, width, height - r, r);
ctx.lineTo(width, height);
ctx.arcTo(width, height + r, width - 50, height + r, r);
ctx.lineTo(r, height + r);
ctx.arcTo(0, height + r, 0, height - 50, r);
ctx.lineTo(0, r);
ctx.arcTo(0, 0, r, 0, r);
ctx.closePath();
}
RoundRect(ctx, 200, 100, 10);
ctx.stroke();
矩形
矩形函数:
reac:绘制矩形附带填充和边框
fillRect:绘制只填充的矩形
strokeRect:绘制只带边框的矩形
语法格式:
// 参数: x , y , width , height
var ctx = document.querySelector(".canvas").getContext("2d");
ctx.fillStyle = "red";
ctx.strokeStyle = "yellow";
ctx.lineWidth = 5;
var x = 0;
ctx.beginPath();
ctx.rect(100, 100, 300, 300);
ctx.stroke();
ctx.fill();
ctx.beginPath();
ctx.fillStyle = "rgba(0,255,155,0.5)";
ctx.strokeStyle = "yellow";
ctx.fillRect(150, 150, 300, 300);
ctx.strokeRect(100, 100, 400, 400);
案例:绘制五角星
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>Document</title>
</head>
<body>
<canvas width="800" height="800"></canvas>
<script>
var ctx = document.querySelector("canvas").getContext("2d");
ctx.lineWidth = 10;
ctx.beginPath();
for (var i = 0; i <= 5; i++) {
ctx.lineTo(
Math.cos(((18 + 72 * i) / 180) * Math.PI) * 100 + 200,
//步骤:先求出角度在转弧度
//cos(18deg)*R sin(18deg)*R
//角度转弧度公式:角度/180*r
//由于数学中的坐标系与canvas的坐标不同所以y轴要为负
-Math.sin(((18 + 72 * i) / 180) * Math.PI) * 100 + 200
);
ctx.lineTo(
Math.cos(((54 + 72 * i) / 180) * Math.PI) * 50 + 200,
-Math.sin(((54 + 72 * i) / 180) * Math.PI) * 50 + 200
);
}
ctx.closePath();
ctx.stroke();
=================================================================================
//五角星封装函数
DrawStart(ctx, 200, 100, 400, 400 , 30);
ctx.stroke();
function DrawStart(context, R, r, x, y,rotate) {
//绘画对象 大圆的半径 小圆半径 x轴 y轴 旋转角度
ctx.beginPath();
for (var i = 0; i < 5; i++) {
ctx.lineTo(
Math.cos(((18 + 72 * i -rotate) / 180) * Math.PI) * R + x,
-Math.sin(((18 + 72 * i -rotate) / 180) * Math.PI) * R + y
);
ctx.lineTo(
Math.cos(((54 + 72 * i -rotate) / 180) * Math.PI) * r + x,
-Math.sin(((54 + 72 * i -rotate) / 180) * Math.PI) * r + y
);
}
ctx.closePath();
}
</script>
</body>
</html>
随机不重复、不切边五角星
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>Document</title>
</head>
<body>
<canvas width="800" height="800" class="canvas"></canvas>
<script src="../node/jquery.js"></script>
<script>
var ctx = document.querySelector("canvas").getContext("2d");
ctx.fillStyle = "black";
ctx.fillRect(
0,
0,
document.querySelector("canvas").width,
document.querySelector("canvas").height
);
ctx.fillStyle = "#fc1";
ctx.strokeStyle = "#fb5";
ctx.lineWidth = 5;
ctx.lineJoin = "round";
var ele = [];
start(ele);
end(ele);
function start(arr) {
for (var i = 0; i < 200; i++) {
var flag = true,
ran = Math.random() * 10 + 10,
x =
Math.random() *
(document.querySelector("canvas").width - ran * 2) +
ran,
y =
Math.random() *
(document.querySelector("canvas").height - ran * 2) +
ran,
rotate = Math.random() * 360;
for (var j = 0; j < ele.length; j++) {
var oldx = Math.pow(x - arr[j][1], 2);
var oldy = Math.pow(y - arr[j][2], 2);
var oldr = Math.pow(ran + arr[j][0], 2);
if (oldx + oldy < oldr) {
flag = false;
}
}
flag ? arr.push([ran, x, y, rotate]) : i--;
}
}
function end(arr) {
for (var i = 0; i < arr.length; i++) {
DrawStart(
ctx,
arr[i][0],
arr[i][0] / 2,
arr[i][1],
arr[i][2],
arr[i][3]
);
ctx.fill();
ctx.stroke();
}
}
function DrawStart(context, R, r, x, y, rotate) {
//绘画对象 大圆的半径 小圆半径 x轴 y轴
ctx.beginPath();
for (var i = 0; i < 5; i++) {
ctx.lineTo(
Math.cos(((18 + 72 * i - rotate) / 180) * Math.PI) * R + x,
-Math.sin(((18 + 72 * i - rotate) / 180) * Math.PI) * R + y
);
ctx.lineTo(
Math.cos(((54 + 72 * i - rotate) / 180) * Math.PI) * r + x,
-Math.sin(((54 + 72 * i - rotate) / 180) * Math.PI) * r + y
);
}
ctx.closePath();
}
</script>
</body>
</html>
绘画状态保存与还原
状态的保存与还原可以使操作更加简便,快速,主要作用于将绘画的状态来回快速切换,状态保存,可以保存如:线条的样式、颜色、填充、图形变换等,特别是配合图形变换,两者可以互补长短
状态保存和还原的对应函数:
save:保存当前的状态,以便restore还原
restore:还原到之前sava保存的状态
语法格式:
//这里配合图形变换来演示
//图形变换系列的函数,重复使用是叠加效果,而不是替换,所以使用图形变换+状态保存还原可以更快速简便
//正常方式
var ctx = document.querySelector(".canvas").getContext("2d");
ctx.beginPath();
ctx.fillStyle = "red";
ctx.translate(100, 100);
ctx.fillRect(0, 0, 400, 400);
ctx.translate(-100, -100);
ctx.fillStyle = "red";
ctx.fillRect(400, 400, 400, 400);
ctx.closePath();
//图形变换函数连续使用会进行叠加效果,会影响到下面绘制的图形,为了不影响,必须再次使用变换函数来还原
//这里运用了canvas的状态保存还原后,可以更方便使用变换函数
var ctx = document.querySelector(".canvas").getContext("2d");
ctx.beginPath();
ctx.fillStyle = "red";
ctx.save();
ctx.translate(100, 100);
ctx.fillRect(0, 0, 400, 400);
ctx.restore();
ctx.fillRect(400, 400, 400, 400);
ctx.closePath();
图形变换
图形变换系列函数:
translate(x,y):偏移、移动
rotate(?deg):旋转
scale(sx,sy):缩放
变换矩阵:
transform(a,b,c,d,e,f):图形变换矩阵函数是一个把所有变换效果结于一身的函数,连续使用会造成链集和叠加效果
setTransform(a,b,c,d,e,f):可以重置transform函数,使之前的失效
语法格式:
//改用图形变换函数 translate rotate scale 后的无重复五角星
//scale 会有许多副作用如:边框将会变大、长宽比也好增大、x、y也会随之增大
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>Document</title>
</head>
<body>
<canvas width="800" height="800" class="canvas"></canvas>
<script src="../node/jquery.js"></script>
<script>
var ctx = document.querySelector("canvas").getContext("2d");
ctx.fillStyle = "black";
ctx.fillRect(
0,
0,
document.querySelector("canvas").width,
document.querySelector("canvas").height
);
ctx.fillStyle = "#fc1";
ctx.strokeStyle = "#fb5";
ctx.lineWidth = 5;
ctx.lineJoin = "round";
var ele = [];
start(ele);
end(ele);
function start(arr) {
for (var i = 0; i < 200; i++) {
var flag = true,
ran = Math.random() * 10 + 10,
x =
Math.random() *
(document.querySelector("canvas").width - ran * 2) +
ran,
y =
Math.random() *
(document.querySelector("canvas").height - ran * 2) +
ran,
rotate = Math.random() * 360;
for (var j = 0; j < ele.length; j++) {
var oldx = Math.pow(x - arr[j][1], 2);
var oldy = Math.pow(y - arr[j][2], 2);
var oldr = Math.pow(ran + arr[j][0], 2);
if (oldx + oldy < oldr) {
flag = false;
}
}
flag ? arr.push([ran, x, y, rotate]) : i--;
}
}
function end(arr) {
for (var i = 0; i < arr.length; i++) {
ctx.save();
ctx.beginPath();
ctx.translate(arr[i][1], arr[i][2]);
ctx.rotate(arr[i][3]);
ctx.scale(0.5,0.5)
DrawStart(ctx, arr[i][0]);
ctx.fill();
ctx.stroke();
ctx.closePath();
ctx.restore();
}
}
function DrawStart(context, r) {
//绘画对象 大圆的半径 小圆半径 x轴 y轴
for (var i = 0; i <= 5; i++) {
ctx.lineTo(
Math.cos(((18 + 72 * i) / 180) * Math.PI) * r,
-Math.sin(((18 + 72 * i) / 180) * Math.PI) * r
);
ctx.lineTo(
(Math.cos(((54 + 72 * i) / 180) * Math.PI) * r) / 2,
(-Math.sin(((54 + 72 * i) / 180) * Math.PI) * r) / 2
);
}
}
</script>
</body>
</html>
//transform
var ctx = document.querySelector(".canvas").getContext("2d");
ctx.beginPath();
ctx.fillStyle = "red";
ctx.save();
ctx.transform(1.5,0,0,1.5,200,100);
a:水平缩放 b:水平倾斜 c:垂直倾斜 d:垂直缩放 e:水平位移 f:垂直位移
ctx.fillRect(0, 0, 400, 400);
ctx.restore();
ctx.fillRect(400, 400, 400, 400);
ctx.closePath();
渐变
渐变系列函数:
createLinearGradient(x,y,EndX,EndY):创建渐变
addColorStop(0-1,color) :添加颜色
createRadialGradient(x1,y1,r1,x2,y2,r2):创建一个径向的渐变,整个径向渐变发生在俩个圆中间
语法格式:
var ctx = document.querySelector("canvas").getContext("2d");
document.querySelector("canvas").width = document.body.clientWidth;
document.querySelector("canvas").height = document.body.clientHeight;
//创建渐变对象
var grd = ctx.createLinearGradient(
0, //x
0, //y
0, //endX
document.querySelector("canvas").height //endY
//这里的渐变方向是从上往下
);
//下面的是径向渐变,在定义的两个圆之间产生渐变
//第一个圆是外圆,内圆则是第二个
//var grd = ctx.createRadialGradient(document.querySelector("canvas").width/2,document.querySelector("canvas").height/2,400,document.querySelector("canvas").width/2,document.querySelector("canvas").height/2,0)
//添加颜色,取值范围(0,1),必须有起点,终点
grd.addColorStop(0, "black");
grd.addColorStop(1, "#035");
//最后把渐变对象添加到要应用的样式填充上
ctx.fillStyle = grd;
ctx.fillRect(
0,
0,
document.querySelector("canvas").width,
document.querySelector("canvas").height
);
三角函数讲解
绘制圆形
1角度 = 1* Math.PI/ 180弧度
1弧度 = 1* 180 / Math.PI 角度
arc(x,y,r,0,360,false)
x,y 圆心坐标位置
r 圆半径
0,360
从0度到360度所对应的弧度 (弧度: 角度值*Math.PI/180) true/false 逆时针/顺时针绘图
添加图片、视频、canvas
相关函数:
createPattern(ele,repeat|repeat-x|repeat-y|no-repeat):添加图片、视频、画布到指定的场景
语法格式:
//添加图片
var ctx = document.querySelector("canvas").getContext("2d");
var img = new Image();
img.src = "../newIMg.png";
img.onload = () => {
var createImg = ctx.createPattern(img, "repeat");
ctx.fillStyle = createImg;
ctx.fillRect(0, 0, 800, 800);
};
//添加图片时注意:必须图片加载完后在载入createPattern,否则载入失败
//添加画布
function createImgCanvas() {
var c = document.createElement("canvas");
var ctx = c.getContext("2d");
c.width = 400;
c.height = 400;
ctx.fillStyle = "yellow";
ctx.transform(1, 0, 0, 1, c.width / 2, c.height / 2);
DrawStart(ctx, 200);
ctx.fill();
return c;
}
var ctx = document.querySelector("canvas").getContext("2d");
document.querySelector("canvas").width = 1400;
document.querySelector("canvas").height = 1400;
ctx.fillStyle = ctx.createPattern(createImgCanvas(), "repeat");
ctx.fillRect(0, 0, 1400, 1400);
案例:绘制圆角矩形
/*
把圆分为4份,顺时针顺序绘制
Math.PI / 2 半圆分1为2
Math.PI 一个圆中的一半
Math.PI*3/2 || Math.PI*1.5 1个半圆加一个1分为2的半圆
Math.PI*2 完整的圆
*/
var ctx = document.querySelector("canvas").getContext("2d");
ctx.beginPath();
ctx.arc(400 - 50, 400 - 50, 50, 0, Math.PI / 2);
ctx.lineTo(50, 400);
ctx.arc(50, 400 - 50, 50, Math.PI / 2, Math.PI);
ctx.lineTo(0, 50);
ctx.arc(50, 50, 50, Math.PI, (Math.PI * 3) / 2);
ctx.lineTo(400 - 50, 0);
ctx.arc(400 - 50, 50, 50, (Math.PI * 3) / 2, Math.PI * 2);
ctx.closePath();
ctx.stroke();
//封装函数
function PathRoundRect(ctx, width, height, r)
{
ctx.beginPath();
ctx.arc(width - r, height - r, r, 0, Math.PI / 2);
ctx.lineTo(r, height);
ctx.arc(r, height - r, r, Math.PI / 2, Math.PI);
ctx.lineTo(0, r);
ctx.arc(r, r, r, Math.PI, Math.PI * 1.5);
ctx.lineTo(width - r, 0);
ctx.arc(width - r, r, r, (Math.PI * 3) / 2, Math.PI * 2);
ctx.closePath();
}
//升级版
function PathRoundRect(ctx, width, height, r,optionColor,optionStyle)
{
if(r*2>width||r*2>height)return;
if(optionStyle)
{
optionStyle.stroke === true ? (optionColor.stroke ? (ctx.strokeStyle =optionColor.stroke ):null) : null;
optionStyle.fill === true ?(optionColor.fill ? (ctx.fillStyle =optionColor.fill):null) : null;
}
ctx.beginPath();
ctx.arc(width - r, height - r, r, 0, Math.PI / 2);
ctx.lineTo(r, height);
ctx.arc(r, height - r, r, Math.PI / 2, Math.PI);
ctx.lineTo(0, r);
ctx.arc(r, r, r, Math.PI, Math.PI * 1.5);
ctx.lineTo(width - r, 0);
ctx.arc(width - r, r, r, (Math.PI * 3) / 2, Math.PI * 2);
ctx.closePath();
}
//最终版
function RoundRect(ctx, width, height, r, optionColor, optionStyle) {
var flagfill,flagstroke;
if (r*2 > width || r*2 > height) return;
if (optionStyle) {
optionStyle.stroke === true
? optionColor.stroke
? (ctx.strokeStyle = optionColor.stroke)
: null
: null;
optionStyle.fill === true
? optionColor.fill
? (ctx.fillStyle = optionColor.fill)
: null
: null;
flagfill = optionColor.fill ?true:false;
flagstroke = optionColor.stroke ?true:false;
}
ctx.beginPath();
ctx.arc(width - r, height - r, r, 0, Math.PI / 2);
ctx.lineTo(r, height);
ctx.arc(r, height - r, r, Math.PI / 2, Math.PI);
ctx.lineTo(0, r);
ctx.arc(r, r, r, Math.PI, Math.PI * 1.5);
ctx.lineTo(width - r, 0);
ctx.arc(width - r, r, r, (Math.PI * 3) / 2, Math.PI * 2);
ctx.closePath();
flagstroke ? ctx.stroke() : null;
flagfill ? ctx.fill() : null;
}
案例:2048九宫格
var ctx = document.querySelector("canvas").getContext("2d");
ctx.translate(800/4,800/4)
RoundRect(
ctx,
480,
470,
10,
{ fill: "red", stroke: "green" },
{ fill: true, stroke: true }
);
for (var i = 0; i < 4; i++) {
ctx.save();
ctx.translate(10, i * 15);
for (var j = 0; j < 4; j++) {
ctx.save();
ctx.translate(j * 120, i * 100 +15);
RoundRect(
ctx,
100,
100,
10,
{ fill: "green", stroke: "green" },
{ fill: true, stroke: true }
);
ctx.restore();
}
ctx.restore();
}
function RoundRect(ctx, width, height, r, optionColor, optionStyle) {
var flagfill, flagstroke;
if (r > width || r > height) return;
if (optionStyle) {
optionStyle.stroke === true
? optionColor.stroke
? (ctx.strokeStyle = optionColor.stroke)
: null
: null;
optionStyle.fill === true
? optionColor.fill
? (ctx.fillStyle = optionColor.fill)
: null
: null;
flagfill = optionColor.fill ? true : false;
flagstroke = optionColor.stroke ? true : false;
}
ctx.beginPath();
ctx.arc(width - r, height - r, r, 0, Math.PI / 2);
ctx.lineTo(r, height);
ctx.arc(r, height - r, r, Math.PI / 2, Math.PI);
ctx.lineTo(0, r);
ctx.arc(r, r, r, Math.PI, Math.PI * 1.5);
ctx.lineTo(width - r, 0);
ctx.arc(width - r, r, r, (Math.PI * 3) / 2, Math.PI * 2);
ctx.closePath();
flagstroke ? ctx.stroke() : null;
flagfill ? ctx.fill() : null;
}
案例:绘制弯月
pathMoon(ctx, 2, 300, 300, 200);
function pathMoon(ctx, d, x, y, r, deg, color) {
ctx.save();
ctx.translate(x, y);
ctx.scale(r, r);
ctx.rotate(deg || 0);
ctx.beginPath();
Moon(d, ctx);
ctx.fillStyle = color ? color : "yellow";
ctx.fill();
ctx.closePath();
ctx.restore();
function Moon(d, ctx) {
ctx.arc(0, 0, 1, Math.PI * 0.5, Math.PI * 1.5, true);
ctx.moveTo(0, -1);
ctx.arcTo(d, 0, 0, 1, dis(0, -1, d, 0) / d);
}
function dis(x1, y1, x2, y2) {
return Math.sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2));
}
}
贝塞尔曲线
贝塞尔曲线相关函数:
quadraticCurveTo(x1,y1,x2,y2):二次贝塞尔曲线 一个控制点 1
bezierCurveTo(x1,y1,x2,y2,x3,y3):三次贝塞尔曲线 两个控制点 1 2
//起始点不由函数来定,可以用moveTo等画路径的函数来定
//二次贝塞尔曲线
ctx.moveTo(0, 0);
ctx.quadraticCurveTo(800,400,400,800)
ctx.stroke()
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>Document</title>
<style>
html,
body {
width: 100%;
height: 100%;
}
</style>
</head>
<body>
<canvas class="canvas"></canvas>
<script src="../node/jquery.js"></script>
<script>
var ctx = document.querySelector("canvas").getContext("2d");
document.querySelector("canvas").width = document.body.clientWidth;
document.querySelector("canvas").height = document.body.clientHeight;
ctx.save();
var grd = ctx.createLinearGradient(0,0,0,document.querySelector("canvas").height);
grd.addColorStop(0,"black");
grd.addColorStop(1,"#035");
ctx.fillStyle = grd;
ctx.fillRect(0,0,document.querySelector("canvas").width,document.querySelector("canvas").height);
ctx.restore();
ctx.fillStyle = "#fc1";
ctx.strokeStyle = "#fb5";
ctx.lineWidth = 5;
ctx.lineJoin = "round";
var ele = [];
start(ele,200);
end(ele);
pathMoon(ctx, 2, document.querySelector("canvas").width*0.85, document.querySelector("canvas").height/4,80,0,"#fc1");
RectLoda(ctx);
function RectLoda(ctx)
{
ctx.save();
ctx.beginPath();
ctx.moveTo(0,document.querySelector("canvas").height*0.65);
//三次贝塞尔曲线应用 ctx.bezierCurveTo(document.querySelector("canvas").width/2,650,document.querySelector("canvas").width/2,document.querySelector("canvas").height/3,document.querySelector("canvas").width,document.querySelector("canvas").height-200);
ctx.lineTo(document.querySelector("canvas").width,document.querySelector("canvas").height)
ctx.lineTo(0,document.querySelector("canvas").height)
var bgcolor = ctx.createLinearGradient(0,document.querySelector("canvas").height,0,0);
bgcolor.addColorStop(0,"#030");
bgcolor.addColorStop(1,"#580");
ctx.fillStyle = bgcolor;
ctx.fill();
ctx.closePath();
ctx.restore();
}
function pathMoon(ctx, d, x, y, r, deg, color) {
ctx.save();
ctx.translate(x, y);
ctx.scale(r, r);
ctx.rotate(deg || 0);
ctx.beginPath();
Moon(d, ctx);
ctx.fillStyle = color ? color : "yellow";
ctx.fill();
ctx.closePath();
ctx.restore();
function Moon(d, ctx) {
ctx.arc(0, 0, 1, Math.PI * 0.5, Math.PI * 1.5, true);
ctx.moveTo(0, -1);
ctx.arcTo(d, 0, 0, 1, dis(0, -1, d, 0) / d);
}
function dis(x1, y1, x2, y2) {
return Math.sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2));
}
}
function start(arr,num) {
for (var i = 0; i < num; i++) {
var flag = true,
ran = Math.random() * 10 + 5,
x =
Math.random() *
(document.querySelector("canvas").width - ran * 2) +
ran,
y =
(Math.random() *
(document.querySelector("canvas").height - ran * 2) +
ran) *
0.65,
rotate = Math.random() * 360;
for (var j = 0; j < ele.length; j++) {
var oldx = Math.pow(x - arr[j][1], 2);
var oldy = Math.pow(y - arr[j][2], 2);
var oldr = Math.pow(ran + arr[j][0], 2);
if (oldx + oldy < oldr) {
flag = false;
}
if(x>document.querySelector("canvas").width*0.85&&x<document.querySelector("canvas").width*0.85+80*1.15)
{
flag = false;
}
}
flag ? arr.push([ran, x, y, rotate]) : i--;
}
}
function end(arr) {
for (var i = 0; i < arr.length; i++) {
ctx.save();
ctx.beginPath();
ctx.translate(arr[i][1], arr[i][2]);
ctx.rotate(arr[i][3]);
ctx.scale(0.7, 0.7);
DrawStart(ctx, arr[i][0]);
ctx.fill();
ctx.stroke();
ctx.closePath();
ctx.restore();
}
}
function DrawStart(ctx, r) {
//绘画对象 大圆的半径 小圆半径 x轴 y轴
for (var i = 0; i <= 5; i++) {
ctx.lineTo(
Math.cos(((18 + 72 * i) / 180) * Math.PI) * r,
-Math.sin(((18 + 72 * i) / 180) * Math.PI) * r
);
ctx.lineTo(
(Math.cos(((54 + 72 * i) / 180) * Math.PI) * r) / 2,
(-Math.sin(((54 + 72 * i) / 180) * Math.PI) * r) / 2
);
}
}
</script>
</body>
</html>
字体
字体绘制相关函数:
font:设置字体样式属性 五个参数依次是font-style(italic oblique)、font-variant(small-caps)、font-weight(bold lighter bolder)、font-size、font-family
fillText(string,x,y,length):绘制带填充字体
strokeText(string,x,y,length):绘制带边框的字体
textAlign:设置字体水平对齐 参数有:right center left
textBaseline:设置字体垂直对齐 参数有:top middle bottom alphabetic(defalut) ideographic hanging
measureText(String).width:获取文本宽度(会根据font设置的字体、字号来决定)
语法格式:
var ctx = document.querySelector(".canvas").getContext("2d");
var grd = ctx.createLinearGradient(0,0,800,0);
grd.addColorStop(0,"red");
grd.addColorStop(0.5,"yellow");
grd.addColorStop(1,"green");
ctx.fillStyle = grd;
ctx.font = "bold 40px 微软雅黑 ";
ctx.fillText("How are you!",100,100,300);
//字体可以填充渐变色、图形纹理、另一个画布
===================================================================
//只带边框的字体
var ctx = document.querySelector(".canvas").getContext("2d");
var grd = ctx.createLinearGradient(0,0,800,0);
grd.addColorStop(0,"red");
grd.addColorStop(0.5,"yellow");
grd.addColorStop(1,"green");
ctx.lineWidth =2;
ctx.strokeStyle = grd;
ctx.font = "bold 40px 微软雅黑 ";
ctx.strokeText("How are you!",100,100,300);
===============================================
//带纹理的字体
var ctx = document.querySelector(".canvas").getContext("2d");
// var grd = ctx.createLinearGradient(0,0,800,0);
// grd.addColorStop(0,"red");
// grd.addColorStop(0.5,"yellow");
// grd.addColorStop(1,"green");
var img = new Image();
img.src = "../newIMg.png";
img.onload = function() {
var pattern = ctx.createPattern(img, "repeat");
ctx.fillStyle = pattern;
ctx.strokeStyle = "red";
ctx.lineWidth =1;
ctx.font = "bold 40px 微软雅黑 ";
ctx.strokeText("How are you!", 100, 100, 300);
ctx.fillText("How are you!", 100, 100, 300);
};
水平对齐效果:
垂直对齐效果:
案例:水平垂直居中
var ctx = document.querySelector(".canvas").getContext("2d");
ctx.fillStyle = "red";
ctx.strokeStyle = "red";
ctx.moveTo(400, 0);
ctx.lineTo(400, 800);
ctx.moveTo(0, 400);
ctx.lineTo(800, 400);
ctx.stroke();
ctx.textAlign = "center";
ctx.textBaseline = "middle";
ctx.font = "bold 100px 微软雅黑 ";
ctx.strokeText("How are you!", 400, 400);
阴影
阴影相关属性:
shadowColor:设置阴影颜色
shadowOffsetX:设置阴影X偏移
shadowOffsetY:设置阴影Y偏移
shadowBlur:设置阴影模糊扩散程度
语法格式:
var ctx = document.querySelector("canvas").getContext("2d");
ctx.shadowColor = "black";
ctx.fillStyle = "red"
var num = 0;
var flag = true;
setInterval(function() {
ctx.shadowOffsetX = -20+num;
ctx.shadowOffsetY = -20+num;
num == 100 ? (flag = false) : num == 0 ? (flag = true) : null;
flag ? num++ : num--;
ctx.shadowBlur = num;
ctx.clearRect(0, 0, 800, 800);
ctx.fillRect(100, 100, 400, 400);
}, 50);
透明
透明相关函数:
在canvas中要实现透明,可以使用rgba颜色,也可以使用以下方法
globalAlpha:设置透明度 范围(1-0) 全局透明
语法格式:
var ctx = document.querySelector("canvas").getContext("2d");
var color = ["red","green","yellow","blue","black"]
for(var i=0;i<200;i++)
{
var randomX = Math.random()*800;
var randomY = Math.random()*800;
var randomR = Math.random()*30;
var R = Math.floor(Math.random()*255),G = Math.floor(Math.random()*255),B = Math.floor(Math.random()*255);
ctx.save();
ctx.beginPath();
ctx.globalAlpha = 0.7;
ctx.fillStyle = `rgb(${R},${G},${B})`;
ctx.arc(randomX,randomY,randomR,0,Math.PI*2);
ctx.fill();
ctx.closePath();
ctx.restore();
}
遮盖顺序
xx.globalCompositeOperation = “source-over” (默认,后绘制的图形会压在先绘制的图形上) / “destination-over”(先绘制的图形压在后绘制的图形上)
相关属性:
globalCompositeOperation :改变元素显示效果与遮盖顺序
各个参数的实现效果可以参考菜鸟,或有canvas手册的网址,或以下链接
https://blog.csdn.net/fe_dev/article/details/81985367
此链接,有水滴扩散、刮刮卡案例参考
参数:
语法格式:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>Document</title>
<style>
html,
body {
width: 100%;
height: 100%;
}
</style>
</head>
<body>
<canvas class="canvas" width="800" height="800"></canvas>
<script src="../node/jquery.js"></script>
<script>
var ctx = document.querySelector("canvas").getContext("2d");
var color = ["red","green","yellow","blue","black"]
var Balls = [];
for(var i=0;i<200;i++)
{
var randomR = Math.random()*30+10;
var randomX = Math.random()*(800-randomR*2)+randomR;
var randomY = Math.random()*(800-randomR*2)+randomR;
var R = Math.floor(Math.random()*255),G = Math.floor(Math.random()*255),B = Math.floor(Math.random()*255);
let balls ={
x:randomX,
y:randomY,
r:randomR,
color:`rgb(${R},${G},${B})`,
vx:(Math.random()*5+5)*(Math.pow(-1,Math.floor(Math.random()*100))),
vy:(Math.random()*5+5)*(Math.pow(-1,Math.floor(Math.random()*100))),
}
Balls.push(balls);
}
setInterval(()=>{
draw(ctx)
},50)
function draw(ctx)
{
ctx.clearRect(0,0,800,800)
for(let i=0;i<Balls.length;i++)
{
ctx.save();
ctx.beginPath();
ctx.globalCompositeOperation = "xor";
ctx.fillStyle = Balls[i].color;
ctx.arc(Balls[i].x,Balls[i].y,Balls[i].r,0,Math.PI*2);
ctx.fill();
ctx.closePath();
ctx.restore();
Balls[i].x+=Balls[i].vx;
Balls[i].y+=Balls[i].vy;
if(Balls[i].x-Balls[i].r<=0)
{
Balls[i].vx = -Balls[i].vx;
Balls[i].x = Balls[i].r;
}
if(Balls[i].x+Balls[i].r >= 800)
{
Balls[i].vx = -Balls[i].vx;
Balls[i].x = 800-Balls[i].r;
}
if(Balls[i].y-Balls[i].r<=0)
{
Balls[i].vy = -Balls[i].vy;
Balls[i].y = Balls[i].r;
}
if(Balls[i].y+Balls[i].r>=800)
{
Balls[i].vy = -Balls[i].vy;
Balls[i].y = 800-Balls[i].r;
}
}
}
</script>
</body>
</html>
剪切
相关函数:
clip:相对于上个填充路径做剪切效果
语法格式:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>Document</title>
<style>
html,
body {
width: 100%;
height: 100%;
}
</style>
</head>
<body>
<canvas class="canvas" width="800" height="800"></canvas>
<script src="../node/jquery.js"></script>
<script>
var ctx = document.querySelector("canvas").getContext("2d");
var r = 150;
ctx.fillStyle = "black";
ctx.fillRect(0, 0, 800, 800);
ctx.canvas.onmousemove = e => {
ctx.clearRect(0, 0, 800, 800);
ctx.beginPath();
ctx.fillStyle = "black";
ctx.fillRect(0, 0, 800, 800);
ctx.closePath();
ctx.save();
ctx.beginPath();
ctx.fillStyle = "rgba(255,255,255,1)";
var x = e.pageX - r <= 0 ? r : e.pageX;
var y = e.pageY - r <= 0 ? r : e.pageY;
x = e.pageX + r > 800 ? 800 - r : x;
y = e.pageY + r > 800 ? 800 - r : y;
ctx.arc(x, y, r, 0, Math.PI * 2);
ctx.fill();
ctx.clip();
ctx.closePath();
ctx.beginPath();
ctx.fillStyle = "red";
ctx.font = "200px bold 微软雅黑";
ctx.textAlign = "center";
ctx.textBaseline = "middle";
ctx.fillText("Canvas", 400, 400);
ctx.closePath();
ctx.restore();
document.onkeyup = ev => {
ev.keyCode === 38 ? (r += 20) : null;
ev.keyCode === 40 ? (r -= 20) : null;
if (r >= 390) r = 390;
ctx.canvas.onmousemove(e);
return false;
};
};
ctx.canvas.onmouseout = () => {
ctx.fillStyle = "black";
ctx.fillRect(0, 0, 800, 800);
document.onkeyup = null;
};
</script>
</body>
</html>
实现剪纸效果
剪纸效果参考非零环绕原侧,自动识别里、外、面,一个面不同时出现顺时针和逆时针就填充,如果同时出现顺时针和逆时针就不填充,这也就说明了图形的绘制方向,会影响其是否填充
语法格式:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>Document</title>
<style>
html,
body {
width: 100%;
height: 100%;
}
</style>
</head>
<body>
<canvas class="canvas" width="800" height="800"></canvas>
<script src="../node/jquery.js"></script>
<script>
var ctx = document.querySelector("canvas").getContext("2d");
ctx.beginPath();
ctx.rect(200, 200, 400, 200);
drawRect(ctx, 220, 220, 100, 100);
ctx.arc(400, 270, 50, Math.PI * 2, 0, true);
Triangle(ctx, 510, 220, 460, 320, 560, 320);
ctx.closePath();
ctx.fillStyle = "red";
ctx.shadowColor = "black";
ctx.shadowOffsetX = 10;
ctx.shadowOffsetY = 10;
ctx.fill();
function drawRect(ctx, x, y, w, h) {
ctx.moveTo(x, y);
ctx.lineTo(x, y + h);
ctx.lineTo(x + w, y + h);
ctx.lineTo(x + w, y);
ctx.lineTo(x, y);
}
function Triangle(ctx, x1, y1, x2, y2, x3, y3) {
ctx.moveTo(x1, y1);
ctx.lineTo(x2, y2);
ctx.lineTo(x3, y3);
ctx.lineTo(x1, y1);
}
</script>
</body>
</html>
交互
canvas交互相关函数:
isPointInPath(x,y):检测指定的坐标是否在绘制元素内,只能判断最后一个绘制的封闭路径
语法格式:
//点击填充颜色
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>Document</title>
<style>
* {
padding: 0;
margin: 0;
}
html,
body {
width: 100%;
height: 100%;
}
</style>
</head>
<body>
<canvas class="canvas" width="800" height="800"></canvas>
<script src="../node/jquery.js"></script>
<script>
var ctx = document.querySelector("canvas").getContext("2d");
var Balls = [];
for (var i = 0; i < 10; i++) {
ctx.beginPath();
Balls.push({
x: Math.random() * 800,
y: Math.random() * 800,
r: Math.random() * 100
});
}
Draw(ctx);
function Draw(ctx) {
for (let i = 0; i < Balls.length; i++) {
ctx.beginPath();
ctx.arc(Balls[i].x, Balls[i].y, Balls[i].r, 0, Math.PI * 2);
ctx.stroke();
}
ctx.canvas.addEventListener("click", function(event) {
for (let i = 0; i < Balls.length; i++) {
ctx.beginPath();
ctx.arc(Balls[i].x, Balls[i].y, Balls[i].r, 0, Math.PI * 2);
if (ctx.isPointInPath(event.pageX, event.pageY)) {
ctx.fillStyle = "yellow";
ctx.fill();
}
}
});
}
</script>
</body>
</html>
//鼠标滑入填充颜色,滑出清空颜色
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>Document</title>
<style>
* {
padding: 0;
margin: 0;
}
html,
body {
width: 100%;
height: 100%;
}
</style>
</head>
<body>
<canvas class="canvas" width="800" height="800"></canvas>
<script src="../node/jquery.js"></script>
<script>
var ctx = document.querySelector("canvas").getContext("2d");
var Balls = [];
for (var i = 0; i < 10; i++) {
ctx.beginPath();
Balls.push({
x: Math.random() * 800,
y: Math.random() * 800,
r: Math.random() * 100
});
}
Draw(ctx);
function Draw(ctx) {
for (let i = 0; i < Balls.length; i++) {
ctx.beginPath();
ctx.arc(Balls[i].x, Balls[i].y, Balls[i].r, 0, Math.PI * 2);
ctx.stroke();
}
ctx.canvas.addEventListener("mousemove", function(event) {
ctx.clearRect(0, 0, 800, 800);
for (let i = 0; i < Balls.length; i++) {
ctx.beginPath();
ctx.arc(Balls[i].x, Balls[i].y, Balls[i].r, 0, Math.PI * 2);
if (ctx.isPointInPath(event.pageX, event.pageY)) {
ctx.fillStyle =
"rgb(" +
Math.random() * 255 +
"," +
Math.random() * 255 +
"," +
Math.random() * 255 +
")";
ctx.fill();
} else ctx.stroke();
}
});
}
</script>
</body>
</html>
案例:小球滚动(面向对象+控制面板)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>Document</title>
<style>
html,
body {
width: 100%;
height: 100%;
}
.canvasBox {
width: 800px;
height: 800px;
position: relative;
}
.canvasBox > .SetupPanel {
width: 300px;
height: 200px;
background: #99b4e487;
border-radius: 30px;
position: absolute;
left: 0;
top: 0;
color: white;
}
.canvasBox > .SetupPanel > h1 {
text-align: center;
line-height: 25px;
}
.canvasBox > .SetupPanel > a {
float: left;
color: #478aff87;
background: #e5eeff;
border-radius: 5px;
padding: 10px 0;
margin: 3px 0;
text-decoration: none;
text-align: center;
line-height: 15px;
width: 100%;
}
</style>
</head>
<body>
<div class="canvasBox">
<canvas class="canvas" width="800" height="800"></canvas>
<div class="SetupPanel">
<h1>控制面板</h1>
<a href="javascript:void(0)">点击停止</a>
<a href="javascript:void(0)">点击切换背景颜色</a>
<a href="javascript:void(0)">点击切换覆盖模式</a>
</div>
</div>
<script src="../node/jquery.js"></script>
<script>
class Animate {
constructor(canvas) {
this.canvas = document.querySelector(canvas);
this.ctx = this.canvas.getContext("2d");
this.initAttr();
this.initDraw();
this.initEvent();
}
initAttr() {
this.color = ["red", "green", "yellow", "blue", "black"];
this.bgcolor = null;
this.Balls = [];
this.flag = true;
this.module = ["xor", "lighter", null];
this.option = null;
this.time = null;
}
initEvent() {
document.querySelectorAll(
".canvasBox>.SetupPanel>a"
)[0].onclick = () => {
if (this.flag) {
clearInterval(this.time);
this.flag = false;
document.querySelectorAll(
".canvasBox>.SetupPanel>a"
)[0].innerHTML = "点击开始";
document.querySelectorAll(
".canvasBox>.SetupPanel>a"
)[0].style.background = "red";
} else {
this.time = setInterval(() => {
this.draw(this.bgcolor);
}, 50);
this.flag = true;
document.querySelectorAll(
".canvasBox>.SetupPanel>a"
)[0].innerHTML = "点击停止";
document.querySelectorAll(
".canvasBox>.SetupPanel>a"
)[0].style.background = "";
}
};
document.querySelectorAll(
".canvasBox>.SetupPanel>a"
)[1].onclick = () => {
this.bgcolor = this.color[
parseInt(Math.random() * this.color.length - 1)
];
};
document.querySelectorAll(
".canvasBox>.SetupPanel>a"
)[2].onclick = () => {
this.option = this.module[
Math.ceil(Math.random() * this.module.length - 1)
];
};
}
initDraw() {
for (let i = 0; i < 200; i++) {
let randomR = Math.random() * 30 + 10;
let randomX = Math.random() * (800 - randomR * 2) + randomR;
let randomY = Math.random() * (800 - randomR * 2) + randomR;
let R = Math.floor(Math.random() * 255),
G = Math.floor(Math.random() * 255),
B = Math.floor(Math.random() * 255);
let balls = {
x: randomX,
y: randomY,
r: randomR,
color: `rgb(${R},${G},${B})`,
vx:
(Math.random() * 5 + 5) *
Math.pow(-1, Math.floor(Math.random() * 100)),
vy:
(Math.random() * 5 + 5) *
Math.pow(-1, Math.floor(Math.random() * 100))
};
this.Balls.push(balls);
}
}
draw(bgcolor) {
this.ctx.clearRect(0, 0, 800, 800);
this.bgDraw(bgcolor);
for (let i = 0; i < this.Balls.length; i++) {
this.ctx.save();
this.ctx.beginPath();
this.ctx.globalCompositeOperation = this.option;
this.ctx.fillStyle = this.Balls[i].color;
this.ctx.arc(
this.Balls[i].x,
this.Balls[i].y,
this.Balls[i].r,
0,
Math.PI * 2
);
this.ctx.fill();
this.ctx.closePath();
this.ctx.restore();
this.Balls[i].x += this.Balls[i].vx;
this.Balls[i].y += this.Balls[i].vy;
if (this.Balls[i].x - this.Balls[i].r <= 0) {
this.Balls[i].vx = -this.Balls[i].vx;
this.Balls[i].x = this.Balls[i].r;
}
if (this.Balls[i].x + this.Balls[i].r >= 800) {
this.Balls[i].vx = -this.Balls[i].vx;
this.Balls[i].x = 800 - this.Balls[i].r;
}
if (this.Balls[i].y - this.Balls[i].r <= 0) {
this.Balls[i].vy = -this.Balls[i].vy;
this.Balls[i].y = this.Balls[i].r;
}
if (this.Balls[i].y + this.Balls[i].r >= 800) {
this.Balls[i].vy = -this.Balls[i].vy;
this.Balls[i].y = 800 - this.Balls[i].r;
}
}
}
bgDraw(bgcolor) {
this.ctx.beginPath();
this.ctx.fillStyle = bgcolor;
this.ctx.fillRect(0, 0, 800, 800);
this.ctx.closePath();
}
}
//初始化对象
let animate = new Animate("canvas");
var ctx = animate.ctx;
//开始执行动画
animate.time = setInterval(() => {
animate.draw(animate.bgcolor);
}, 50);
</script>
</body>
</html>
清除
清除也称重绘,在动画应用当中是不可分割的,动画是一帧一帧的播放,中间少不了清除的步骤,否则将达不到动画的播放帧数标准
清除画布相关函数:
clearRect(startX,startY,endX,endY):清除参数指定范围内绘制的元素
语法格式:
ctx.clearRect(0,0,canvas.width,canvas.height);
扩充Canvas 2d方法
在有些时候,我们自定义的函数,不能像canvas自带的函数一样,不用传入绘制上下午对象,而我们自定义的必须把绘制对象传入函数里才可以使用,那么有没有方法可以解决呢,在canvas实例对象的原型上添加方法即可
语法格式:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>Document</title>
<style>
html,
body {
width: 100%;
height: 100%;
}
.canvasBox {
width: 800px;
height: 800px;
position: relative;
}
.canvasBox > .SetupPanel {
width: 300px;
height: 200px;
background: #99b4e487;
border-radius: 30px;
position: absolute;
left: 0;
top: 0;
color: white;
}
.canvasBox > .SetupPanel > h1 {
text-align: center;
line-height: 25px;
}
.canvasBox > .SetupPanel > a {
float: left;
color: #478aff87;
background: #e5eeff;
border-radius: 5px;
padding: 10px 0;
margin: 3px 0;
text-decoration: none;
text-align: center;
line-height: 15px;
width: 100%;
}
</style>
</head>
<body>
<div class="canvasBox">
<canvas class="canvas" width="800" height="800"></canvas>
</div>
<script src="../node/jquery.js"></script>
<script>
let ctx = document.querySelector(".canvas").getContext("2d");
ctx.__proto__.fillStar = function(r)
//ctx.__proto__ === CanvasRenderingContext2D.prototype
{
this.beginPath();
for(let i=0;i<=5;i++)
{
this.lineTo(
Math.cos((18+i*72)/180*Math.PI)*r+this.lastMoveTo.x,
-Math.sin((18+i*72)/180*Math.PI)*r+this.lastMoveTo.y
);
this.lineTo(
Math.cos((54+i*72)/180*Math.PI)*r/2+this.lastMoveTo.x,
-Math.sin((54+i*72)/180*Math.PI)*r/2+this.lastMoveTo.y
);
}
this.closePath();
this.fill();
}
ctx.lastMoveTo = {};
CanvasRenderingContext2D.prototype.oldMoveTo = CanvasRenderingContext2D.prototype.moveTo;
CanvasRenderingContext2D.prototype.moveTo = function(x,y)
{
this.oldMoveTo(x,y);
this.lastMoveTo.x = x;
this.lastMoveTo.y = y;
}
ctx.moveTo(400,400)
ctx.fillStar(100);
</script>
</body>
</html>
图像处理
图像不同于图形,图像是位图是由无数个彩色像素点组成,图形是点线面结合而成,两者的处理方式也不相同,所干涉的区域也不相同
图形处理相关函数:
drawImage(img,sx,sy,sw,sh,dx,dy,dw,dh) :导入指定图片到canvas当中。 九个参数 首个是图片资源,s开头的是原图像的参数,d开头的是指在canvas绘制的参数
语法格式:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>Document</title>
</head>
<body>
<canvas
class="canvas"
width="800"
height="800"
style="border:1px solid blue"
></canvas>
<script>
var ctx = document.querySelector(".canvas").getContext("2d");
var img = new Image();
img.src = "../PHP操作MySQL.png";
img.onload = () => {
ctx.drawImage(img, 740, 450, 260, 70, 0, 0, 800, 800);
};
</script>
</body>
</html>
案例:图像放大
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>Document</title>
<style>
body,
html {
background: black;
}
input[type=range] {
-webkit-appearance: none;
width: 300px;
border-radius: 10px; /*这个属性设置使填充进度条时的图形为圆角*/
margin-top: 10px;
}
input[type=range]::-webkit-slider-thumb {
-webkit-appearance: none;
}
input[type=range]::-webkit-slider-runnable-track {
height: 10px;
border-radius: 10px; /*将轨道设为圆角的*/
box-shadow: 0 1px 1px #def3f8, inset 0 .125em .125em #0d1112; /*轨道内置阴影效果*/
}
input[type=range]::-webkit-slider-thumb {
-webkit-appearance: none;
height: 25px;
width: 25px;
margin-top: -5px; /*使滑块超出轨道部分的偏移量相等*/
background: #ffffff;
border-radius: 50%; /*外观设置为圆形*/
border: solid 0.125em rgba(205, 224, 230, 0.5); /*设置边框*/
box-shadow: 0 .125em .125em #3b4547; /*添加底部阴影*/
}
</style>
</head>
<body>
<canvas
class="canvas"
style="border:1px solid blue;margin: 0 auto;display: block"
></canvas>
<input
type="range"
style="display:block;width:100%;"
max="3.0"
min="0.5"
step="0.1"
value="1"
class="scale"
/>
<script>
var ctx = document.querySelector(".canvas").getContext("2d");
var img = new Image();
var scale = document.querySelector(".scale");
img.src = "../1.jpg";
img.onload = () => {
ctx.canvas.width = img.width;
ctx.canvas.height = img.height;
drawImageByScale(ctx, scale.value, img.width, img.height);
// scale.onchange = function() {
// ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
// drawImageByScale(ctx, scale.value, img.width, img.height);
// };
scale.onmousemove = function() {
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
drawImageByScale(ctx, scale.value, img.width, img.height);
};
};
function drawImageByScale(ctx, scale, w, h) {
var imageWidth = w * scale;
var imageHeight = h * scale;
let x = ctx.canvas.width / 2 - imageWidth / 2,
y = ctx.canvas.height / 2 - imageHeight / 2;
ctx.drawImage(img, x, y, imageWidth, imageHeight);
}
</script>
</body>
</html>
//水印版
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>Document</title>
<style>
body,
html {
background: black;
}
.bottom {
width: 100%;
position: fixed;
bottom: 30px;
}
input[type="range"] {
-webkit-appearance: none;
width: 300px;
border-radius: 10px; /*这个属性设置使填充进度条时的图形为圆角*/
margin-top: 10px;
position: relative;
}
input[type="range"]::-webkit-slider-thumb {
-webkit-appearance: none;
}
input[type="range"]::-webkit-slider-runnable-track {
height: 10px;
border-radius: 10px; /*将轨道设为圆角的*/
box-shadow: 0 1px 1px #def3f8, inset 0 0.125em 0.125em #0d1112; /*轨道内置阴影效果*/
}
input[type="range"]::-webkit-slider-thumb {
-webkit-appearance: none;
height: 25px;
width: 25px;
margin-top: -5px; /*使滑块超出轨道部分的偏移量相等*/
background: #ffffff;
border-radius: 50%; /*外观设置为圆形*/
border: solid 0.125em rgba(205, 224, 230, 0.5); /*设置边框*/
box-shadow: 0 0.125em 0.125em #3b4547; /*添加底部阴影*/
}
</style>
</head>
<body>
<canvas
class="canvas"
style="border:1px solid blue;margin: 0 auto;display: block"
></canvas>
<div class="bottom">
<input
type="range"
style="display:block;width:100%;"
max="3.0"
min="0.5"
step="0.1"
value="1"
class="scale"
/>
</div>
<script>
var ctx = document.querySelector(".canvas").getContext("2d");
var img = new Image();
var scale = document.querySelector(".scale");
img.src = "./img.jpg";
var shuiying = document.createElement("canvas").getContext("2d");
shuiying.canvas.width = 400;
shuiying.canvas.height = 100;
shuiying.font = "bold 50px 微软雅黑";
shuiying.fillStyle = "white";
shuiying.textAlign = "center";
shuiying.textBaseline = "middle";
shuiying.fillText("xuyuxin", 200, 50, 400);
img.onload = () => {
ctx.canvas.width = img.width;
ctx.canvas.height = img.height;
drawImageByScale(ctx, scale.value, img.width, img.height,shuiying);
// scale.onchange = function() {
// ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
// drawImageByScale(ctx, scale.value, img.width, img.height);
// };
scale.onmousemove = function() {
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
drawImageByScale(ctx, scale.value, img.width, img.height, shuiying);
};
};
function drawImageByScale(ctx, scale, w, h, c) {
var imageWidth = w * scale;
var imageHeight = h * scale;
let x = ctx.canvas.width / 2 - imageWidth / 2,
y = ctx.canvas.height / 2 - imageHeight / 2;
ctx.drawImage(img, x, y, imageWidth, imageHeight);
if(scale>=1)
{
ctxdrawImage(c.canvas, -40, ctx.canvas.height - 100);
}
else
{
ctx.drawImage(c.canvas, x, imageHeight-50);
}
}
</script>
</body>
</html>
案例:放大镜
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>Document</title>
<style>
body,
html {
background: black;
}
.bottom {
width: 100%;
position: fixed;
bottom: 30px;
}
input[type="range"] {
-webkit-appearance: none;
width: 300px;
border-radius: 10px; /*这个属性设置使填充进度条时的图形为圆角*/
margin-top: 10px;
position: relative;
}
input[type="range"]::-webkit-slider-thumb {
-webkit-appearance: none;
}
input[type="range"]::-webkit-slider-runnable-track {
height: 10px;
border-radius: 10px; /*将轨道设为圆角的*/
box-shadow: 0 1px 1px #def3f8, inset 0 0.125em 0.125em #0d1112; /*轨道内置阴影效果*/
}
input[type="range"]::-webkit-slider-thumb {
-webkit-appearance: none;
height: 25px;
width: 25px;
margin-top: -5px; /*使滑块超出轨道部分的偏移量相等*/
background: #ffffff;
border-radius: 50%; /*外观设置为圆形*/
border: solid 0.125em rgba(205, 224, 230, 0.5); /*设置边框*/
box-shadow: 0 0.125em 0.125em #3b4547; /*添加底部阴影*/
}
</style>
</head>
<body>
<canvas
class="canvas"
style="border:1px solid blue;margin: 0 auto;display: block"
></canvas>
<script>
var ctx = document.querySelector(".canvas").getContext("2d");
var offsetCanvas = document.createElement("canvas").getContext("2d");
var img = new Image();
var scale;
img.src = "./img-lg.jpg";
img.onload = () => {
ctx.canvas.width = 1152;
ctx.canvas.height = 768;
offsetCanvas.canvas.width = img.width;
offsetCanvas.canvas.height = img.height;
scale = offsetCanvas.canvas.width / ctx.canvas.width;
ctx.drawImage(img, 0, 0, ctx.canvas.width, ctx.canvas.height);
offsetCanvas.drawImage(img, 0, 0);
};
ctx.canvas.onmousedown = function(e) {
var x = e.clientX - this.getBoundingClientRect().left,
y = e.clientY - this.getBoundingClientRect().top;
Draw(ctx, true, x, y);
this.onmousemove = e => {
var x = e.clientX - this.getBoundingClientRect().left,
y = e.clientY - this.getBoundingClientRect().top;
Draw(ctx, true, x, y);
};
this.onmouseup = () => {
ctx.canvas.onmousemove = null;
Draw(ctx, false);
};
this.onmouseout = () => {
ctx.canvas.onmousemove = null;
Draw(ctx, false);
};
};
function Draw(ctx, flag, x, y) {
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
ctx.drawImage(img, 0, 0, ctx.canvas.width, ctx.canvas.height);
if (flag) {
drawImageRect(ctx, x, y);
}
}
function drawImageRect(ctx, x, y) {
var imageLgX = x * scale,
imageLgY = y * scale;
var r = 200;
var sx = imageLgX - r,
sy = imageLgY - r;
var dx = x - r,
dy = y - r;
ctx.save();
ctx.beginPath();
ctx.arc(x,y,r,0,Math.PI*2);
ctx.stroke();
ctx.clip()
ctx.drawImage(offsetCanvas.canvas, sx, sy, r * 2, r * 2, dx, dy, r * 2, r * 2);
ctx.closePath();
ctx.restore();
}
</script>
</body>
</html>
像素处理
相关函数:
getImageData(x,y,w,h):获取指定图像像素数据 返回一个对象,里面有 data像素数据
putImageData(img,dx,dy,dirtx,dirty,dirtw,dirth):输出处理后的图像到指定位置
createImageData(w,h):创建一个空的图像
像素处理算法:
语法格式:
//像素自动变紫
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>Document</title>
<style>
body,
html {
/* background: black; */
height: 2000px;
}
</style>
</head>
<body>
<canvas
class="canvas"
style="border:1px solid blue;float:left"
width="500"
height="500"
></canvas>
<canvas
class="canvas"
style="border:1px solid blue;float:right"
width="500"
height="500"
></canvas>
<div class="btnbox" style="display: block;">
<a href="javascript:void(0)" onclick="fillter()">fillter</a>
</div>
<script>
var ctx1 = document.querySelectorAll(".canvas")[0].getContext("2d");
var ctx2 = document.querySelectorAll(".canvas")[1].getContext("2d");
var n =0;
var imageData;
var data;
var img = new Image();
img.src = "./img.jpg";
img.onload = function() {
ctx1.drawImage(img, 0, 0, 500, 500);
};
function fillter() {
imageData = ctx1.getImageData(0, 0, 500, 500);
data = imageData.data;
for (let i = 0; i < ctx2.canvas.width * ctx2.canvas.height; i++) {
data[4 * i + 0] += n;
// data[4 * i + 1] += n;
data[4 * i + 2] += n;
// data[4 * i + 3] = Math.ceil(Math.random()*255);
}
ctx2.putImageData(imageData, 0, 0, 0, 0, 500, 500);
}
setInterval(function(){
n++;
if(n===255)
{
n=Math.ceil(Math.random()*255)
}
fillter(n);
},1);
</script>
</body>
</html>
案例:像素滤镜
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>Document</title>
<style>
body,
html {
/* background: black; */
height: 2000px;
}
.btnbox > a {
float: left;
margin: 10px 20px;
}
</style>
</head>
<body>
<canvas class="canvas" style="float:left" width="500" height="500"></canvas>
<canvas
class="canvas"
style="float:right"
width="500"
height="500"
></canvas>
<div class="btnbox" style="display: block;">
<a href="javascript:void(0)" onclick="grey()">灰度 Grey Effect</a>
<a href="javascript:void(0)" onclick="black()">黑白 black&white Effect</a>
<a href="javascript:void(0)" onclick="revers()">反色 revers Effect</a>
<a href="javascript:void(0)" onclick="Blur()">模糊 blur Effect</a>
<a href="javascript:void(0)" onclick="mosaice()">马赛克 mosaice Effect</a>
</div>
<script>
var ctx1 = document.querySelectorAll(".canvas")[0].getContext("2d");
var ctx2 = document.querySelectorAll(".canvas")[1].getContext("2d");
var imageData;
var data;
var img = new Image();
img.src = "../1.jpg";
img.onload = function() {
ctx1.drawImage(img, 0, 0, 500, 500);
};
function mosaice() {
var imgData = ctx1.getImageData(0, 0, 500, 500);
var data = imgData.data;
var tmpimgData = ctx1.getImageData(0, 0, 500, 500);
var tmpData = tmpimgData.data;
var size = 16,
totalnum = Math.pow(size, 2);
for (var i = 0; i < ctx2.canvas.height; i += size) {
for (var j = 0; j < ctx2.canvas.width; j += size) {
var totalr = 0,
totalg = 0,
totalb = 0;
for (var dx = 0; dx < size; dx++) {
for (var dy = 0; dy < size; dy++) {
let x = i + dx,
y = j + dy;
let p = x * ctx2.canvas.width + y;
totalr += tmpData[p * 4 + 0];
totalg += tmpData[p * 4 + 1];
totalb += tmpData[p * 4 + 2];
}
}
var p = i * ctx2.canvas.width + j;
var resr = totalr / totalnum;
var resg = totalg / totalnum;
var resb = totalb / totalnum;
for (var dx = 0; dx < size; dx++) {
for (var dy = 0; dy < size; dy++) {
var x = dx + i,
y = dy + j;
var p = x * ctx2.canvas.width + y;
data[4 * p + 0] = resr;
data[4 * p + 1] = resg;
data[4 * p + 2] = resb;
}
}
}
}
ctx2.putImageData(imgData, 0, 0, 0, 0, 500, 500);
}
function revers() {
imageData = ctx1.getImageData(0, 0, 500, 500);
data = imageData.data;
for (let i = 0; i < ctx2.canvas.width * ctx2.canvas.height; i++) {
let r = 255 - data[4 * i + 0],
g = 255 - data[4 * i + 1],
b = 255 - data[4 * i + 2];
data[4 * i + 0] = r;
data[4 * i + 1] = g;
data[4 * i + 2] = b;
}
ctx2.putImageData(imageData, 0, 0, 0, 0, 500, 500);
}
function Blur() {
var imgData = ctx1.getImageData(0, 0, 500, 500);
var data = imgData.data;
var tmpimgData = ctx1.getImageData(0, 0, 500, 500);
var tmpData = tmpimgData.data;
var blurR = 2,
totalnum = Math.pow(2 * blurR + 1, 2);
for (var i = blurR; i < ctx2.canvas.height - blurR; i++) {
for (var j = blurR; j < ctx2.canvas.width - blurR; j++) {
var totalr = 0,
totalg = 0,
totalb = 0;
for (var dx = -blurR; dx <= blurR; dx++) {
for (var dy = -blurR; dy <= blurR; dy++) {
let x = i + dx,
y = j + dy;
let p = x * ctx2.canvas.width + y;
totalr += tmpData[p * 4 + 0];
totalg += tmpData[p * 4 + 1];
totalb += tmpData[p * 4 + 2];
}
}
var p = i * ctx2.canvas.width + j;
data[p * 4 + 0] = totalr / totalnum;
data[p * 4 + 1] = totalg / totalnum;
data[p * 4 + 2] = totalb / totalnum;
}
}
ctx2.putImageData(imgData, 0, 0, 0, 0, 500, 500);
}
function grey() {
imageData = ctx1.getImageData(0, 0, 500, 500);
data = imageData.data;
for (let i = 0; i < ctx2.canvas.width * ctx2.canvas.height; i++) {
let r = data[4 * i + 0],
g = data[4 * i + 1],
b = data[4 * i + 2];
let grey = r * 0.3 + g * 0.59 + b * 0.11;
data[4 * i + 0] = grey;
data[4 * i + 1] = grey;
data[4 * i + 2] = grey;
}
ctx2.putImageData(imageData, 0, 0, 0, 0, 500, 500);
}
function black() {
imageData = ctx1.getImageData(0, 0, 500, 500);
data = imageData.data;
for (let i = 0; i < ctx2.canvas.width * ctx2.canvas.height; i++) {
let r = data[4 * i + 0],
g = data[4 * i + 1],
b = data[4 * i + 2];
let grey = r * 0.3 + g * 0.59 + b * 0.11;
if (grey > 255 / 2) {
v = 255;
} else {
v = 0;
}
data[4 * i + 0] = v;
data[4 * i + 1] = v;
data[4 * i + 2] = v;
}
ctx2.putImageData(imageData, 0, 0, 0, 0, 500, 500);
}
</script>
</body>
</html>
案例:颜色板
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>Document</title>
<style>
body,
html {
/* background: black; */
height: 2000px;
}
.btnbox > a {
float: left;
margin: 10px 20px;
}
</style>
</head>
<body>
<canvas class="canvas" style="float:left" width="800" height="800"></canvas>
<div class="btnbox" style="display: block;"></div>
<script>
var ctx = document.querySelectorAll(".canvas")[0].getContext("2d");
var img = ctx.createImageData(800, 800);
var data = img.data;
for (var i = 0; i < ctx.canvas.height; i++) {
for (var j = 0; j < ctx.canvas.width; j++) {
var p = i * ctx.canvas.width + j;
data[4 * p + 0] = parseInt(
Math.pow(Math.cos(Math.atan2(j - 400, i - 400) / 2), 2) * 255
);
data[4 * p + 1] = parseInt(
Math.pow(
Math.cos(
Math.atan2(j - 400, i - 400) / 2 - (2 * Math.acos(-1)) / 3
),
2
) * 255
);
data[4 * p + 2] = parseInt(
Math.pow(
Math.cos(
Math.atan2(j - 400, i - 400) / 2 + (2 * Math.acos(-1)) / 3
),
2
) * 255
);
data[4 * p + 3] = 255;
}
}
ctx.putImageData(img, 0, 0, 0, 0, 800, 800);
</script>
</body>
</html>