1:绘制基本图形
1.矩形绘制-填充模式
<body>
<canvas width="400" height="600" id="main"></canvas>
<script>
const canvas = document.getElementById('main')
const ctx =canvas.getContext('2d');
// 填充颜色
ctx.fillStyle = 'red'
// 绘制一个填充的矩形
ctx.fillRect(10,10,100,100)
</script>
</body>
</html>
2.矩形绘制-路径模式
<body>
<canvas width="400" height="600" id="main"></canvas>
<script>
const canvas = document.getElementById('main')
const ctx =canvas.getContext('2d');
// 绘制一个矩形的边框。
ctx.strokeStyle = 'red'
ctx.strokeRect(130,130,200,200)
</script>
</body>
3.矩形绘制填充-拆分-分段路径-模式
<body>
<canvas width="400" height="600" id="main"></canvas>
<script>
const canvas = document.getElementById('main')
const ctx = canvas.getContext('2d');
// beginPath closePath 可以完成路径的分段
ctx.beginPath()
// 拆开的写法
ctx.rect(10, 10, 100, 100)
//显示路径
ctx.stroke()
ctx.closePath()
ctx.beginPath()
ctx.rect(110, 110, 100, 100)
//填充, 这里会覆盖上面的stroke
ctx.fill()
ctx.closePath()
</script>
</body>
4.矩形绘制-清除模式
<body>
<canvas width="400" height="600" id="main"></canvas>
<script>
const canvas = document.getElementById('main')
const ctx =canvas.getContext('2d');
// 绘制一个填充的矩形
ctx.fillStyle = 'red'
ctx.fillRect(10,10,100,100)
// 绘制一个矩形的边框。
ctx.strokeStyle = 'red'
ctx.strokeRect(130,130,200,200)
// 清除指定的矩形区域,然后这块区域会变的完全透明。
// ctx.clearRect(0,0,canvas.width,canvas.height)
let height = 0
const canvasWidth = canvas.width
const canvasHeight = canvas.height
const timer = setInterval(() => {
height++
//清除指定的矩形区域,然后这块区域会变的完全透明。
ctx.clearRect(0,0,canvasWidth,height)
if(height === canvasHeight){
clearInterval(timer)
}
}, 10);
</script>
</body>
2:绘制圆形
1.圆弧
<body>
<canvas width="400" height="600" id="main"></canvas>
<script>
const canvas = document.getElementById('main')
const ctx = canvas.getContext('2d');
// arc是绘制圆弧的方法
// ctx.arc(圆心x, 圆心y,半径,开始的角度,结束的角度,是否顺时针【默认逆时针false】)
ctx.beginPath()
ctx.arc(300,200,50,0,Math.PI*2)
ctx.fill()
ctx.closePath()
ctx.beginPath()
ctx.arc(120,120,120,0,Math.PI/3,true)
ctx.stroke()
ctx.closePath()
</script>
</body>
2.圆弧绘制笑脸
<body>
<canvas width="400" height="600" id="main"></canvas>
<script>
const canvas = document.getElementById('main')
const ctx = canvas.getContext('2d');
// 绘制脸
ctx.beginPath()
ctx.arc(75,75,50,0,Math.PI*2)
ctx.stroke()
ctx.closePath()
// 绘制嘴巴
ctx.beginPath()
ctx.arc(75,75,35,0,Math.PI)
ctx.stroke()
ctx.closePath()
// 绘制左眼
ctx.beginPath()
ctx.arc(60,65,5,0,Math.PI * 2)
ctx.stroke()
ctx.closePath()
// 绘制右眼
ctx.beginPath()
ctx.arc(90,65,5,0,Math.PI * 2)
ctx.stroke()
ctx.closePath()
</script>
</body>
3.圆弧绘制笑脸-moveTo
<body>
<canvas width="400" height="600" id="main"></canvas>
<script>
const canvas = document.getElementById('main')
const ctx = canvas.getContext('2d');
// 绘制脸
ctx.beginPath()
ctx.arc(75, 75, 50, 0, Math.PI * 2)
ctx.moveTo(110, 75)
// 绘制嘴巴
ctx.arc(75, 75, 35, 0, Math.PI)
ctx.moveTo(65, 65)
// 绘制左眼
ctx.arc(60, 65, 5, 0, Math.PI * 2)
ctx.moveTo(95, 65)
// 绘制右眼
ctx.arc(90, 65, 5, 0, Math.PI * 2)
ctx.stroke()
ctx.closePath()
</script>
</body>
4.arcTo绘制圆弧
void ctx.arcTo(x1, y1, x2, y2, radius);
<body>
<canvas width="600" height="400" id="main"></canvas>
<script>
const canvas = document.getElementById('main')
const ctx = canvas.getContext('2d');
ctx.beginPath()
ctx.moveTo(300, 200)
ctx.arcTo(300, 300, 200, 300, 100)
ctx.stroke()
ctx.closePath()
</script>
</body>
<body>
<canvas width="600" height="400" id="main"></canvas>
<script>
const canvas = document.getElementById('main')
const ctx = canvas.getContext('2d');
ctx.strokeRect(100,100,100,100)
ctx.moveTo(200,100)
ctx.arcTo(200,200,100,200,50)
ctx.stroke()
</script>
</body>
3:绘制线段
<body>
<canvas width="600" height="400" id="main"></canvas>
<script>
const canvas = document.getElementById('main')
const ctx = canvas.getContext('2d');
// 绘制脸
ctx.beginPath()
ctx.moveTo(300,200)
ctx.lineTo(400,200)
ctx.lineTo(400,100)
ctx.lineTo(300,200)
ctx.stroke()
ctx.closePath()
</script>
</body>
4:二次贝塞尔曲线
<body>
<canvas width="600" height="600" id="main"></canvas>
<script>
const canvas = document.getElementById('main')
const ctx = canvas.getContext('2d');
ctx.beginPath()
ctx.moveTo(200,300)
ctx.quadraticCurveTo(150,300,150,200)
ctx.quadraticCurveTo(150,100,300,100)
ctx.quadraticCurveTo(450,100,450,200)
ctx.quadraticCurveTo(450,300,250,300)
ctx.quadraticCurveTo(250,350,150,350)
ctx.quadraticCurveTo(200,350,200,300)
ctx.stroke()
</script>
</body>
5:三次贝塞尔曲线
<body>
<canvas width="600" height="600" id="main"></canvas>
<script>
const canvas = document.getElementById("main");
const ctx = canvas.getContext("2d");
var heartPath = new Path2D();
console.log(heartPath);
// 起点
heartPath.moveTo(300, 200); // 2个控制点、1个终点
heartPath.bezierCurveTo(350, 150, 400, 200, 300, 250);
heartPath.bezierCurveTo(200, 200, 250, 150, 300, 200);
ctx.stroke(heartPath);
var chatPath = new Path2D();
chatPath.moveTo(200, 300);
chatPath.quadraticCurveTo(150, 300, 150, 200);
chatPath.quadraticCurveTo(150, 100, 300, 100);
chatPath.quadraticCurveTo(450, 100, 450, 200);
chatPath.quadraticCurveTo(450, 300, 250, 300);
chatPath.quadraticCurveTo(250, 350, 150, 350);
chatPath.quadraticCurveTo(200, 350, 200, 300);
ctx.stroke(chatPath);
ctx.fill(heartPath); // 创建一条折线
var polyline = new Path2D("M10 10 h 80 v 80 h -80 z");
ctx.stroke(polyline);
</script>
</body>
6:颜色设置
<body>
<canvas width="400" height="600" id="main"></canvas>
<script>
const canvas = document.getElementById('main')
const ctx = canvas.getContext('2d');
// 绘制脸
ctx.beginPath()
ctx.strokeStyle = 'red'
ctx.arc(75,75,50,0,Math.PI*2)
ctx.stroke()
ctx.beginPath()
// 绘制嘴巴
ctx.strokeStyle = 'blue'
ctx.arc(75,75,35,0,Math.PI)
ctx.stroke()
ctx.beginPath()
// 绘制左眼
ctx.fillStyle = 'yellow'
ctx.arc(60,65,5,0,Math.PI * 2)
ctx.fill()
ctx.beginPath()
// 绘制右眼
ctx.fillStyle = '#398009'
ctx.arc(90,65,5,0,Math.PI * 2)
ctx.fill()
ctx.closePath()
</script>
</body>
7:渐变
- 线性渐变
<body>
<canvas width="600" height="600" id="main"></canvas>
<script>
const canvas = document.getElementById("main");
const ctx = canvas.getContext("2d");
// const linearGradient = ctx.createLinearGradient(0, 0, 600, 600);
let inx = 0;
function render() {
ctx.clearRect(100, 100, 500, 300);
if (inx >= 0.99) {
inx = 0;
}
inx += 0.01;
const linearGradient = ctx.createLinearGradient(100, 100, 600, 400);
linearGradient.addColorStop(0, "red");
linearGradient.addColorStop(inx, "yellow");
linearGradient.addColorStop(1, "blue");
ctx.fillStyle = linearGradient;
ctx.fillRect(100, 100, 500, 300);
window.requestAnimationFrame(render);
}
render();
</script>
</body>
2.径向渐变
<body>
<canvas width="600" height="600" id="main"></canvas>
<script>
const canvas = document.getElementById("main");
const ctx = canvas.getContext("2d");
const radialGradient = ctx.createRadialGradient(200, 200, 0, 200, 200, 100);
radialGradient.addColorStop(0, "red");
radialGradient.addColorStop(0.5, "yellow");
radialGradient.addColorStop(1, "blue");
ctx.fillStyle = radialGradient;
ctx.fillRect(0, 0, 400, 400);
</script>
</body>
- 径向渐变-3D球
<body>
<canvas width="600" height="600" id="main"></canvas>
<script>
const canvas = document.getElementById("main");
const ctx = canvas.getContext("2d");
const radialGradient = ctx.createRadialGradient(200, 200, 10, 250, 250, 100);
radialGradient.addColorStop(0, "pink");
radialGradient.addColorStop(1, "red");
ctx.fillStyle = radialGradient;
ctx.arc(300,300,200,0,Math.PI*2)
ctx.fill()
</script>
</body>
8:绘制图片
<body>
<canvas width="1200" height="900" id="main"></canvas>
<script>
const canvas = document.getElementById('main')
const ctx = canvas.getContext('2d');
const img = new Image()
img.src = '../img/1.jpg'
img.onload = function(){
console.log(img.__proto__,img.width,img.height);
// ctx.drawImage(img, 0, 0)
// ctx.drawImage(img, 0, 0, 1200, 900);
ctx.drawImage(img,img.width/4,0,img.width*3/4,img.height/2 ,0, 0,400, 300);
}
</script>
</body>
9. 绘制视频and添加水印
<body>
<canvas width="500" height="500" id="main"></canvas>
<video src="../img/mov_bbb.mp4"></video>
<button onclick="playPause()">播放/暂停</button>
<script>
const canvas = document.getElementById("main");
const ctx = canvas.getContext("2d");
const myVideo = document.querySelector('video')
function playPause() {
if (myVideo.paused){
myVideo.play();
render()
}else{
myVideo.pause();
}
}
const img = new Image()
img.src = '../img/1.jpg'
function render(){
if(!myVideo.paused){
ctx.drawImage(myVideo, 0, 0, 300, 200);
ctx.drawImage(img,250,150,50,50)
requestAnimationFrame(render)
}
}
// const img = new Image()
// img.src = '../img/1.jpg'
// img.onload = function(){
// console.log(img.__proto__,img.width,img.height);
// // ctx.drawImage(img, 0, 0)
// // ctx.drawImage(img, 0, 0, 1200, 900);
// ctx.drawImage(img,img.width/4,0,img.width*3/4,img.height/2 ,0, 0,400, 300);
// }
</script>
</body>
10.文字绘制
<body>
<canvas width="600" height="400" id="main"></canvas>
<script>
const canvas = document.getElementById("main");
const ctx = canvas.getContext("2d");
// 文字,大小/字体
ctx.strokeStyle ="#ff0000";
ctx.font ="100px Microsoft YaHei";
// 填充渲染文字
// fillText(文本,文本的起点x,文本起点的y,绘制文字最大的宽度)
// ctx.fillText("老陈",300,200,100);
// 文本对齐选项textAlign,start(默认),end,left,right,center
ctx.textAlign ="center";
// 文本基线对齐,textBaseline,top,bottom,alphabetic
ctx.textBaseline ="middle";
// 文本的方向
// ctx.direction ="rtl";
// 预测量文本宽度
let text = ctx.measureText("Hello!");
console.log(text);
ctx.strokeText("Hello!",300,200);
</script>
</body>
11.位移变换
- 平移坐标系 translate
<body>
<canvas width="600" height="400" id="main" style="border: 1px solid #000000"></canvas>
<script>
const canvas = document.getElementById("main");
const context = canvas.getContext("2d");
// 会将坐标系平移 100 100 的位置
context.translate(100, 100);
context.fillRect(0,0,100,100);
context.fillStyle='red';
context.fillRect(50,50,100,100);
</script>
</body>
2.旋转坐标系rotate
<body>
<canvas
width="600"
height="400"
id="main"
style="border: 1px solid #000000"
></canvas>
<script>
const canvas = document.getElementById("main");
const ctx = canvas.getContext("2d");
// 3.绘制图形
// 3.1 绘制矩形fillRect(位置x,位置y,宽度,高度)
// scale拉伸坐标系
// ctx.rotate(Math.PI / 4);
// ctx.translate(300, 200);
// ctx.scale(2, 1);
// ctx.fillRect(-250, -25, 500, 50);
ctx.fillRect(200,150, 200, 20);
// 整个坐标系会旋转
ctx.rotate(Math.PI / 10);
ctx.fillRect(200,150, 200, 100);
</script>
</body>
3.坐标系放大 scale
<body>
<canvas width="600" height="400" id="main" style="border: 1px solid #000000"></canvas>
<script>
const canvas = document.getElementById("main");
const context = canvas.getContext("2d");
// x轴放大5倍, Y轴放大2倍, 整个坐标系也是放大 x-->5倍 y-->2倍
context.scale(5, 2);
context.fillRect(0,0,100,100);
</script>
</body>
12:图像合成
美女刮刮卡 ctx.globalCompositeOperation = "destination-out"
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<style>
.content {
position: absolute;
top: 0;
left: 0;
z-index: 1;
width: 600px;
height: 400px;
display: flex;
}
h1 {
margin: auto;
}
canvas {
position: absolute;
top: 0;
left: 0;
z-index: 2;
}
</style>
</head>
<body>
<div class="content">
<h1>谢谢惠顾</h1>
</div>
<canvas width="600" height="400" id="main"></canvas>
<script>
const canvas = document.getElementById("main");
const ctx = canvas.getContext("2d");
let img = new Image();
img.src = '../img/1.jpg'
img.onload = function () {
ctx.drawImage(img, 0, 0, 600, 400);
}
let isDraw = false
canvas.onmousedown = function (){
isDraw = true
}
canvas.onmouseup = function (){
isDraw = false
}
canvas.onmousemove = function (e) {
console.log(isDraw);
if(isDraw){
var x = e.pageX;
var y = e.pageY;
ctx.globalCompositeOperation = "destination-out"
ctx.beginPath();
ctx.arc(x, y, 10, 0, 2 * Math.PI);
ctx.fill();
}
}
// 手机端
canvas.ontouchmove = function(e){
const {clientX:x,clientY:y} = e.touches[0]
ctx.globalCompositeOperation = "destination-out"
ctx.beginPath();
ctx.arc(x, y, 5, 0, 2 * Math.PI);
ctx.fill();
}
</script>
</body>
</html>
13:剪裁 clip
<body>
<canvas width="600" height="600" id="main"></canvas>
<script>
const canvas = document.getElementById('main')
const ctx = canvas.getContext('2d');
ctx.moveTo(200,300)
ctx.quadraticCurveTo(150,300,150,200)
ctx.quadraticCurveTo(150,100,300,100)
ctx.quadraticCurveTo(450,100,450,200)
ctx.quadraticCurveTo(450,300,250,300)
ctx.quadraticCurveTo(250,350,150,350)
ctx.quadraticCurveTo(200,350,200,300)
ctx.stroke()
ctx.clip()
let img = new Image();
img.src = '../img/1.jpg'
img.onload = function () {
ctx.drawImage(img, 0, 0, 600, 400);
}
</script>
</body>
14:状态的恢复与保存
<body>
<canvas width="800" height="800" id="main"></canvas>
<script>
const canvas = document.getElementById('main')
const ctx = canvas.getContext('2d');
ctx.fillStyle='red';
ctx.fillRect(0,0,100,100);
ctx.save()
ctx.fillStyle='yellow';
ctx.fillRect(100,100,100,100);
ctx.save()
ctx.fillStyle='blue';
ctx.fillRect(200,200,100,100);
// ctx.save()
ctx.restore()
ctx.fillRect(300,300,100,100);
ctx.restore()
ctx.fillRect(400,400,100,100);
</script>
</body>
15:像素操作
这个地方有个坑,要使用服务器发开页面,比如http-server,本地打开会报错
from origin 'null' has been blocked by CORS policy: Cross origin requests are only supported for protocol schemes: http, data, isolated-app, chrome-extension, chrome, https, chrome-untrusted
<body>
<canvas width="400" height="300" id="main"></canvas>
<script>
const canvas = document.getElementById('main')
const ctx = canvas.getContext('2d');
const img = new Image()
img.crossOrigin = ''
img.src = '../img/1.jpg'
img.onload = function(){
ctx.drawImage(img,0,0,400,300);
// 使用 http-server 访问,不然会跨域😭
const imgData = ctx.getImageData(0, 0,400, 300);
console.log(imgData);
for (let i = 0; i < imgData.data.length; i+=4) {
const avg =(imgData.data[i]+imgData.data[i+1]+imgData.data[i+2])/3
imgData.data[i] = avg
imgData.data[i+1] = avg
imgData.data[i+2] = avg
}
// ctx.putImageData(imgData, 0, 0);
ctx.putImageData(imgData, 0, 0, 0,0, 200, 150);
}
</script>
</body>
16:结合事件
<body>
<canvas width="600" height="600" id="main"></canvas>
<script>
class A{
constructor(x,y){
const canvas = document.getElementById("main");
this.x = x
this.y = y
this.color = 'red'
this.isIn = false;
this.ctx = canvas.getContext("2d");
canvas.onmousemove = (e) =>{
const {offsetX:x,offsetY:y} = e
this.isIn = this.ctx.isPointInPath(x, y);
if(this.isIn){
this.color = 'blue'
}else{
this.color = 'red'
}
this.draw()
}
}
draw(){
// isPointInPath()不支持canvas自带的两个方法fillRect(),strokeRect();
// this.ctx.save()
// this.ctx.clearRect(this.x, this.y, 200, 200);
// this.ctx.fillStyle = this.color
// this.ctx.fillRect(this.x, this.y,200,200);
// this.ctx.restore();
this.ctx.clearRect(this.x, this.y, 200, 200);
this.ctx.fillStyle = this.color
this.ctx.rect(this.x, this.y,200,200);
this.ctx.fill()
}
};
const a = new A(0,0);
a.draw()
</script>
</body>
鼠标移入移出的效果
2.签名面板
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<style>
.active{
background: red;
}
</style>
</head>
<body>
<canvas id="c1" width="2000" height="600"></canvas>
<hr />
<button id="boldBtn" type="button">粗线条</button>
<button id="thinBtn" type="button">细线条</button>
<button id="saveBtn" type="button">保存签名</button>
<input id="colorBtn" type="color" name=" value="" />
<button id="clearBtn">橡皮擦</button>
<button id="nullBtn">清空画布</button>
<script>
// 1获取canvas画布和绘制的上下文对象
const canvas = document.querySelector("#c1");
const ctx = canvas.getContext("2d");
// 圆润的连接
ctx.lineJoin='round';
// 设置画笔的粗细
const boldBtn = document.querySelector("#boldBtn");
boldBtn.addEventListener('click',function(e){
ctx.lineWidth = 20;
this.classList.add('active')
thinBtn.classList.remove('active')
clearBtn.classList.remove('active')
ctx.globalCompositeOperation='source-over';
})
const thinBtn = document.querySelector("#thinBtn");
thinBtn.addEventListener('click',function(e){
ctx.lineWidth = 1;
this.classList.add('active')
boldBtn.classList.remove('active')
clearBtn.classList.remove('active')
ctx.globalCompositeOperation='source-over';
})
// 保存签名
const saveBtn = document.querySelector("#saveBtn");
saveBtn.addEventListener('click',function(e){
const urlData = canvas.toDataURL()
// const img = new Image()
// img.src = urlData
// document.body.appendChild(img)
const a = document.createElement('a')
a.href = urlData
a.download = 'fux签名'
a.click()
a.remove()
})
// 修改颜色
const colorBtn = document.querySelector("#colorBtn");
colorBtn.addEventListener('change',function(e){
ctx.strokeStyle=this.value
})
// 橡皮擦
const clearBtn = document.querySelector("#clearBtn");
clearBtn.addEventListener('click',function(e){
ctx.globalCompositeOperation='destination-out';
ctx.lineWidth = 20
this.classList.add('active')
thinBtn.classList.remove('active')
boldBtn.classList.remove('active')
})
// 清空画板
const nullBtn = document.querySelector("#nullBtn");
nullBtn.addEventListener('click',function(e){
ctx.clearRect(0, 0, canvas.width, canvas.height);
})
let isDraw = false;
canvas.onmousedown = function(e){
isDraw = true
ctx.beginPath();
const x = e.pageX - canvas.offsetLeft
const y = e.pageY - canvas.offsetTop
ctx.moveTo(x, y);
}
canvas.onmouseleave = function(e){
isDraw = false
ctx.closePath();
}
canvas.onmouseup = function(e){
isDraw = false
ctx.closePath();
}
canvas.onmousemove = function(e){
if(isDraw){
const x = e.pageX - canvas.offsetLeft
const y = e.pageY - canvas.offsetTop
ctx.lineTo(x,y)
ctx.stroke();
}
}
</script>
</body>
</html>