canvas 专门用来进行2d绘图的元素
怎么用
1.首先需要有一个canvas元素 ,也就是画布
2.当浏览器不支持画布的时候,会显示canvas中的内容
3.canvas是行元素,默认宽300 高150
4.设置canvas的大小,只能通过标签的width和height属性来设置,不能通过css设置
2d渲染上下文的画笔,他是绘图中最重要的一个元素
canvas的中坐标系是 笛卡尔坐标系,也就是原点在左上角
<canvas id="can" width="500" height="500">您的浏览器太low了</canvas>
<script type="text/javascript">
//canvas代码提示
/**@type{HTMLCanvasElement}*/
let can = document.querySelector('#can');
let ctx = can.getContext('2d'); //获取画笔
ctx.beginPath();
// moveTo() 画笔开始移动的位置,参数都是number 第一个是x轴的位置,第二个是y轴位置
ctx.moveTo(100,100);
// lineTo() 画笔结束的位置,参数都是number 第一个是x轴的位置,第二个是y轴位置
ctx.lineTo(400,400);
// ctx.lineWidth = 10;
ctx.strokeStyle = '#04be02';
ctx.fillStyle = '#04be02'; // 设置封闭图形的填充颜色
ctx.stroke();
ctx.fill(); // 给封闭图形填充颜色
ctx.closePath(); //关闭
使用save()和restore()进行属性的保存,底层是以栈的形式保存内容(先进后出)
// 画矩形
//strokeRect(矩形左上角的x坐标,矩形左上角的y坐标,矩形宽,矩形高)
ctx.strokeRect(100,100,200,100);
//fillRect(矩形左上角的x坐标,矩形左上角的y坐标,矩形宽,矩形高)
ctx.fillRect(120,120,200,100);
//清空画布clearRect(矩形左上角的x坐标,矩形左上角的y坐标,矩形宽,矩形高); 此方法清空画布以后,给ctx设置的属性没有被清除,不会对2d渲染进行重置,可以清除固定内容的部分画布
ctx.clearRect(10,10,400,400);
// 清空所有内容,并且画笔重置, 重新设置宽和高即可
can.width = can.height = 600;
画弧
1.canvas中的角度都是弧度
2.顺时针为正,逆时针为负
arc(圆心x,圆心y,半径长度,起始角度,结束角度,是否是逆时针[可选])
是否是逆时针: 默认是false 也就是顺时针旋转
ctx.arc(300,300,100,0,Math.PI/2,true);
strokeText('文本',开始位置的x,开始位置的y,最大宽度[可选]);
fillText('文本',开始位置的x,开始位置的y,最大宽度[可选]);
//设置文字的大小以及样式
ctx.font = "italic bold 30px arial";
/*
textAlign 设置左右对齐的方式、
left top right start end center
left/start 起始点在文字的左侧
right/end 起始点在文字的右侧
center 起始点在文字的中间
ctx.textAlign = "center";
textBaseline 设置垂直方向的对齐方式
top 起始点在文字的顶部
middle 起始点在文字的中间
bottom 起始点在文字的底部
ctx.textBaseline = "bottom";
ctx.strokeText('公侯顶上能跑马',100,100,200);
shadowOffsetX 阴影x轴的偏移量,向右正,向左负
shadowOffsetY 阴影y轴的偏移量,向下正,向上负
shadowColor 阴影颜色
shadowBlur 阴影模糊范围
ctx.beginPath();
ctx.shadowOffsetX = 30;
ctx.shadowOffsetY = 30;
ctx.shadowColor = "#04be02";
ctx.shadowBlur = 10;
ctx.fillRect(100,100,200,100);
ctx.closePath();
坐标轴的形变
translate 平移
scale 缩放
rotate 旋转
先形变,再绘图
ctx.beginPath();
ctx.translate(300,300);
ctx.arc(0,0,100,0,2*Math.PI);
ctx.font = '20px arial';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.rotate(Math.PI);
ctx.scale(2,2);
ctx.fillText('百万雄师过大江',0,0);
ctx.stroke();
ctx.closePath();
</script>
2.小球碰壁反弹
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>小球碰壁反弹</title>
<style type="text/css">
* {
margin:0;
padding:0;
}
#can {
display: block;
border:1px #f00 solid;
margin:50px auto 0;
}
</style>
</head>
<body>
<canvas id="can" width="600" height="600"></canvas>
</body>
</html>
<script type="text/javascript">
let can = document.querySelector('#can');
let ctx = can.getContext('2d'); //画笔,笔触
function draw(x,y){
ctx.save();
ctx.fillStyle = '#04be02';
ctx.arc(x,y,20,0,2*Math.PI);
ctx.fill();
ctx.restore();
}
// draw(20,20);
var x1 = 20;
var y1 = 20;
var durX = true; //设置x轴运动方向 true向右,false向左
var durY = true; //设置y轴运动方向 true向下,false向上
setInterval(function(){
can.width = can.height = 600;
draw(x1,y1);
if(durX){
x1++;
if(x1>=580){
durX=false;
}
}else{
x1--;
if(x1<=20){
durX=true;
}
}
if(durY){
y1++;
if(y1>=580){
durY=false;
}
}else{
y1--;
if(y1<=20){
durY=true;
}
}
},20);
</script>
3. 修改图形合并方式
globalCompositeOperation
source-over 老图和新图都保留,新图在上
source-in 保留新图的重叠部分,其他全部清除
source-out 保留新图的非重叠部分,其他全部清除
source-atop 去掉新图的非重叠部分,其他全部保留
destination-over 老图和新图都保留,老图在上
destination-in 保留老图的重叠部分,其他全部清除
destination-out 保留老图的非重叠部分,其他全部清除
destination-atop 去掉老图的非重叠部分,其他全部保留
copy 只保留新图,其他全部清空
lighter 新图和老图都保留,并且重叠部分颜色叠加
xor 保留老图和新图非重叠部分,其他全部清除
4.drawImage()
1.drawImage(图片资源,开始点x,开始点y) 把图从具体位置开始绘制,图片不会被压缩
2.drawImage(图片资源,开始点x,开始点y,矩形宽,矩形高) 把图片绘制到cans中的一个矩形里面,图片可能会被压缩
3.drawImage(图片资源,图像开始点x,图像开始点y,图像矩形宽,图像矩形高,canvas开始点x,canvas开始点y,canvas矩形宽,canvas矩形高) 把图片中指定的内容,绘制到canvas中指定的内容中
注意:必须在图片资源加载完毕以后才可以执行绘制图片的操作
var img = new Image();
img.src = './images/a.jpg';
img.onload = function(){
ctx.drawImage(img,100,100,400,200,100,200,400,200);
};
5.刮刮乐
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>刮刮乐</title>
<style type="text/css">
* {
margin:0;
padding:0;
}
.wp {
width:640px;
height:480px;
background: url(images/ggl.png) no-repeat 0 0;
margin:50px auto 0;
position:relative;
}
.txt {
width:540px;
height:246px;
position: absolute;
left:56px;
bottom:10px;
background-color:#04be02;
color:#ff0;
font-size:50px;
line-height:246px;
text-align: center;
}
#can {
position: absolute;
left:55px;
bottom:10px;
z-index:100;
}
</style>
</head>
<body>
<div class="wp">
<p class="txt">喜中一个亿</p>
<canvas id="can" width="542" height="246"></canvas>
</div>
</body>
</html>
<script type="text/javascript">
let can = document.querySelector('#can');
let ctx = can.getContext('2d'); //画笔,笔触
let wp = document.querySelector('.wp');
ctx.fillStyle = '#ccc';
ctx.fillRect(0,0,542,246);
can.onmousedown = function(){
can.onmousemove = function(){
let x1 = event.clientX - can.offsetLeft - wp.offsetLeft;
let y1 = event.clientY - can.offsetTop - wp.offsetTop;
//更改合并方式
ctx.globalCompositeOperation = "destination-out";
ctx.beginPath();
ctx.arc(x1,y1,20,0,2*Math.PI);
ctx.fillStyle = '#ff0';
ctx.fill();
ctx.closePath();
/*
getImageData(起始位置的x,起始位置的y,宽度,高)
返回 imgData 对象,该对象包含了指定区域的图像信息
每一个imgData中有一个data属性,该属性存放的是像素点信息
每4个是一组 存储了rgba信息
r 红色(0-255)
g 绿色 (0-255)
b 蓝色(0-255)
a alpha 通道(0-255) 0是透明,255完全不透明
*/
// 当透明点超过 80%的时候,让所以点都透明
let imgData = ctx.getImageData(0,0,can.width,can.height);
// console.log(imgData);
let arr = imgData.data;
let conut = 0; //记录透明点的个数
for(var i=0;i<arr.length;i+=4){
if(arr[i+3]==0){
conut++;
}
}
// 判断透明的点除以所有像素点的总数,是否大于80%
if(conut/(arr.length/4)>=0.8){
can.width = 542;
can.height = 246;
}
};
};
document.onmouseup = function(){
can.onmousemove = null;
};
</script>
6.太极
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>太极</title>
<style type="text/css">
* {
margin:0;
padding:0;
}
#can {
display: block;
border:1px #f00 solid;
margin:50px auto 0;
}
</style>
</head>
<body>
<canvas id="can" width="600" height="600"></canvas>
</body>
</html>
<script type="text/javascript">
let can = document.querySelector('#can');
let ctx = can.getContext('2d'); //画笔,笔触
function taiji(){
ctx.save();
//画大圆
ctx.beginPath();
ctx.arc(0,0,300,0,Math.PI);
ctx.stroke();
ctx.fillStyle = '#000';
ctx.fill();
ctx.closePath();
ctx.beginPath();
ctx.arc(0,0,300,0,Math.PI,true);
ctx.stroke();
ctx.fillStyle = '#fff';
ctx.fill();
ctx.closePath();
//画半圆
ctx.beginPath();
ctx.arc(150,0,150,0,2*Math.PI,true);
ctx.fillStyle = '#000';
ctx.fill();
ctx.closePath();
ctx.beginPath();
ctx.arc(-150,0,150,0,2*Math.PI);
ctx.fillStyle = '#fff';
ctx.fill();
ctx.closePath();
ctx.beginPath();
ctx.arc(150,0,50,0,2*Math.PI);
ctx.fillStyle = '#fff';
ctx.fill();
ctx.closePath();
ctx.beginPath();
ctx.arc(-150,0,50,0,2*Math.PI);
ctx.fillStyle = '#000';
ctx.fill();
ctx.closePath();
ctx.restore();
}
taiji();
var num = 0;
function move(){
can.width = can.height = 600;
// 移动坐标轴
ctx.translate(300,300);
ctx.rotate(num*(Math.PI/180));
num++;
taiji();
requestAnimationFrame(move);
};
move();
</script>
7.放大镜
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>小球碰壁反弹</title>
<style type="text/css">
* {
margin:0;
padding:0;
}
#can {
display: block;
border:1px #f00 solid;
margin:50px auto 0;
}
</style>
</head>
<body>
<canvas id="can" width="960" height="540"></canvas>
</body>
</html>
<script type="text/javascript">
let can = document.querySelector('#can');
let ctx = can.getContext('2d'); //画笔,笔触
var img = new Image();
img.src = "./images/a.jpg";
img.onload = function(){
ctx.drawImage(img,0,0,1920,1080,0,0,960,540);
// 鼠标进入canvas
can.onmouseenter = function(){
can.onmousemove = move;
};
can.onmouseleave = function(){
//清除画布
can.width = 960;
can.height = 540;
ctx.drawImage(img,0,0,1920,1080,0,0,960,540);
};
};
//鼠标移动
function move(){
//清除画布
can.width = 960;
can.height = 540;
ctx.drawImage(img,0,0,1920,1080,0,0,960,540);
//获取鼠标的坐标
let x1 = event.clientX - can.offsetLeft - 1;
let y1 = event.clientY - can.offsetTop - 1;
ctx.globalCompositeOperation = "destination-out";
//绘制圆
ctx.beginPath();
ctx.arc(x1,y1,100,0,2*Math.PI);
ctx.fill();
ctx.closePath();
ctx.globalCompositeOperation = "destination-over";
//把图片绘制到圆上
ctx.drawImage(img,x1*2-100,y1*2-100,200,200,x1-100,y1-100,200,200);
}
</script>
8.钟表
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>钟表</title>
<style type="text/css">
* {
margin: 0;
padding: 0;
}
#can {
display: block;
border: 1px #f00 solid;
margin: 50px auto 0;
}
</style>
</head>
<body>
<canvas id="can" width="600" height="600"></canvas>
</body>
</html>
<script type="text/javascript">
let can = document.querySelector('#can');
let ctx = can.getContext('2d'); //画笔,笔触
function Clock() {
}
// 设置原型方法
Clock.prototype = {
constructor: Clock,
//画表盘
drawDial() {
//外面的大盘
ctx.save();
ctx.beginPath();
ctx.arc(300, 300, 300, 0, 2 * Math.PI);
ctx.fillStyle = '#04BE02';
ctx.fill();
ctx.closePath();
ctx.restore();
//画小盘
ctx.save();
ctx.beginPath();
ctx.arc(300, 300, 280, 0, 2 * Math.PI);
ctx.fillStyle = '#fff';
ctx.shadowColor = '#f00';
ctx.shadowBlur = 20;
ctx.fill();
ctx.closePath();
ctx.restore();
//绘制刻度
ctx.save();
ctx.beginPath();
//移动原点
ctx.translate(300, 300);
ctx.rotate(-Math.PI / 3);
// canvas的旋转,实际上是笛卡尔坐标系的旋转
for (var i = 0; i < 12; i++) {
ctx.beginPath();
ctx.lineWidth = 10;
ctx.moveTo(270, 0);
ctx.lineTo(240, 0);
ctx.stroke();
ctx.closePath();
// 绘制数字
ctx.save();
ctx.translate(210, 0);
ctx.rotate((Math.PI / 180) * (60 - 30 * i));
ctx.font = "30px arial";
ctx.textAlign = "center";
ctx.textBaseline = "middle";
ctx.fillText(i + 1, 0, 0);
ctx.restore();
ctx.rotate(Math.PI / 6);
}
ctx.closePath();
ctx.restore();
},
//画表针
drawPin() {
ctx.save();
let date = new Date();
let h = date.getHours();
let m = date.getMinutes();
let s = date.getSeconds();
ctx.translate(300, 300);
ctx.rotate(-Math.PI / 2);
//绘制秒针
ctx.save();
ctx.rotate((Math.PI / 180) * 6 * s);
ctx.beginPath();
ctx.moveTo(-100, 0);
ctx.lineTo(190, 0);
ctx.lineWidth = 3;
ctx.strokeStyle = '#04BE02';
ctx.stroke();
ctx.closePath();
ctx.restore();
//绘制分针
ctx.save();
ctx.rotate((Math.PI / 180) * (m * 6 + s / 10));
ctx.beginPath();
ctx.moveTo(-60, 0);
ctx.lineTo(170, 0);
ctx.lineWidth = 5;
ctx.strokeStyle = '#f00';
ctx.stroke();
ctx.closePath();
ctx.restore();
//绘制时针
ctx.save();
ctx.rotate((Math.PI / 180) * (h * 30 + m / 2 + s / 120));
ctx.beginPath();
ctx.moveTo(-40, 0);
ctx.lineTo(150, 0);
ctx.lineWidth = 8;
ctx.strokeStyle = '#000';
ctx.stroke();
ctx.closePath();
ctx.restore();
//绘制中间的圆扣
ctx.beginPath();
ctx.arc(0, 0, 20, 0, 2 * Math.PI);
ctx.fillStyle = "#000";
ctx.fill();
ctx.closePath();
ctx.restore();
}
};
let c = new Clock();
c.drawDial();
c.drawPin();
function move() {
ctx.clearRect(0, 0, 600, 600);
c.drawDial();
c.drawPin();
requestAnimationFrame(move);
var img = new Image();
img.src = "./images/01.jpg";
img.onload = function () {
ctx.drawImage(img, 0, 0, 600, 600, 0, 0, 600, 600);
}
}
move();
</script>
9.视频播放
<!DOCTYPE html>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>视频播放</title>
<style type="text/css">
* {
margin: 0;
padding: 0;
}
#can {
display: block;
border: 1px #f00 solid;
margin: 50px auto 0;
}
</style>
</head>
<body>
<video width="800" height="500" src="./images/8.mp4" controls="controls" id="v"></video>
<button id="play">播放</button>
<button id="stop">暂停</button>
<canvas id="can" width="800" height="500"></canvas>
</body>
</html>
<script type="text/javascript">
let can = document.querySelector('#can');
let ctx = can.getContext('2d'); //画笔,笔触
let v = document.querySelector('#v');
let play = document.querySelector('#play');
let stop = document.querySelector('#stop');
//视频播放,必须要先等视频加载完毕以后才可以播放
v.oncanplay = function () {
//视频播放的方法
// v.play();
play.onclick = function () {
v.play();
//setInterval(movie,1);
movie();
};
stop.onclick = function () {
v.pause(); //暂停播放
cancelAnimationFrame(movie);
};
};
function movie() {
can.width = 800;
can.height = 500;
//绘制
ctx.drawImage(v, 0, 0, can.width, can.height);
requestAnimationFrame(movie);
}
</script>
10.视频马赛克
<!DOCTYPE html>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>视频播放</title>
<style type="text/css">
* {
margin: 0;
padding: 0;
}
#can {
display: block;
border: 1px #f00 solid;
margin: 50px auto 0;
}
</style>
</head>
<body>
<video width="800" height="500" src="./images/8.mp4" controls="controls" id="v" style="display: none;"></video>
<canvas id="can" width="800" height="500"></canvas>
<button id="play">播放</button>
<button id="stop">暂停</button>
<button id="square">方形马赛克</button>
<button id="circle">圆形马赛克</button>
<button id="wuma">高清无码</button>
</body>
</html>
<script type="text/javascript">
let can = document.querySelector('#can');
let ctx = can.getContext('2d'); //画笔,笔触
let v = document.querySelector('#v');
let play = document.querySelector('#play');
let stop = document.querySelector('#stop');
let square = document.querySelector('#square');
let circle = document.querySelector('#circle');
let wuma = document.querySelector('#wuma');
//设置马赛克的默认形状
let masico = 'square';
//设置是否有码
let isMa = false;
v.oncanplay = function () {
//点击播放
play.onclick = function () {
v.play();
movie();
};
// 点击暂停
stop.onclick = function () {
v.pause(); //暂停播放
cancelRequestAnimationFrame(movie);
};
//点击方形
square.onclick = function () {
isMa = true;
masico = 'square';
};
//点击圆形
circle.onclick = function () {
isMa = true;
masico = 'circle';
};
//点击无码
wuma.onclick = function () {
isMa = false;
masico = 'square';
};
};
function movie() {
can.width = 800;
can.height = 500;
//绘制
ctx.drawImage(v, 0, 0, can.width, can.height);
//判断是否有马赛克,马赛克原理:把图片分隔成一个一个的小块儿,小块中的像素都一样
if (isMa) { // isMa为true则有马赛克
// 获取canvas中所有的像素点
let imageData = ctx.getImageData(0, 0, can.width, can.height);
let arr = imageData.data;
//清空高清图
ctx.clearRect(0, 0, can.width, can.height);
//设置马赛克小格子的宽和高
let width = 5;
let height = 5;
let row = Math.floor(can.height / height); //行
let col = Math.floor(can.width / width); // 列
//遍历每一个小格子
for (var i = 0; i < row; i++) {
for (var j = 0; j < col; j++) {
//获取每一个小格子中心点的坐标
let x = j * width + Math.floor(width / 2) + 1;
let y = i * height + Math.floor(height / 2) + 1;
//找到该点在像素数组中的下标
let index = (y - 1) * can.width * 4 + x * 4;
// 获取像素点的rgb
let r = arr[index];
let g = arr[index + 1];
let b = arr[index + 2];
let a = arr[index + 3];
// 设置填充颜色
ctx.fillStyle = 'rgba(' + r + ',' + g + ',' + b + ',' + a + ')';
//判断是哪一种马赛克
if (masico == 'square') {
ctx.fillRect(x - Math.floor(width / 2), y - Math.floor(height / 2), width, height);
} else {
ctx.beginPath();
ctx.arc(x, y, width / 2, 0, 2 * Math.PI);
ctx.fill();
ctx.closePath();
}
}
}
}
requestAnimationFrame(movie);
}
</script>