canvas
只能在标签中用width。heigth修改大小
基本方法:
先得到canvas:
let canvas = document.querySelector('#canvas') // 得到canvas
let gd = canvas.getContext('2d')//得到canvas上下文环境
gd.moveTo(a,b): 从一个点开始绘图
gd.lineTo(c,d):绘图终点
gd.stroke():绘制
gd.fill():填充
gd.strokeStyle = ‘#555’ : 绘图颜色
gd.fillStyle = ‘#666’: 填充颜色
canvas.lineWidth=100:修改线宽
gd,beginPath(): 清除原来的痕迹
ge.closePath(): 自动完成闭合
绘制完毕后就不能修改颜色等属性,所以绘制是最后一步
绘制图之前一定先 beginPath()
绘制矩形
gd.strokeRect(x, y, 矩形width,矩形height)
gd.fillRect(x, y, 矩形width,矩形height)
clearRect(x, y, 矩形width,矩形height) 清除指定的矩形区域,然后这块区域会变的完全透明。
绘制圆形(弧)
gd.arc(cX, cY, R, 起始弧度, 终点弧度,是否逆时针) //弧度:Math.PI*角度
gd.stroke()
绘制文字
gd.font = '100px 宋体‘’ // 设置字体大小 型号
gd.strokeText(‘abc’, x, y) // 实心
gd.fillText(‘abc’, x, y) // 空心
gd.stroke()
canvas绘图以左上角的(0, 0)为基准原点 但是文字以基线为基点
画图
window.onload = function() {
let canvas = document.querySelector('#canvas')
let gd = canvas.getContext('2d')
let obj = {}
canvas.onmousedown = function (event) {
obj.lastX = event.offsetX
obj.lastY = event.offsetY
}
canvas.onmousemove = function (event) {
gd.beginPath()
gd.moveTo(obj.lastX, obj.lastY)
gd.lineTo(event.offsetX, event.offsetY)
obj.lastX = event.offsetX
obj.lastY = event.offsetY
gd.stroke()
}
canvas.onmouseup = function () {
canvas.onmousemove = null
canvas.onmouseup = null
}
}
圆弧案例:
function draw(){
var canvas = document.getElementById('tutorial');
if (!canvas.getContext) return;
var ctx = canvas.getContext("2d");
ctx.beginPath();
ctx.moveTo(50, 50); // 创建起点
//参数1、2:控制点1坐标 参数3、4:控制点2坐标 参数5:圆弧半径
ctx.arcTo(200, 50, 200, 200, 100); // 画弧
ctx.lineTo(200, 200) // 创建终点
ctx.stroke();
ctx.beginPath();
ctx.rect(50, 50, 10, 10);
ctx.rect(200, 50, 10, 10)
ctx.rect(200, 200, 10, 10)
ctx.fill()
}
draw();
这个方法可以这样理解。绘制的弧形是由两条切线所决定。
第 1 条切线:起始点和控制点1决定的直线。
第 2 条切线:控制点1 和控制点2决定的直线。
其实绘制的圆弧就是与这两条直线相切的圆弧。
饼图绘制
window.onload = function() {
function draw(cX, cY, r){
var canvas = document.getElementById('canvas');
if (!canvas.getContext('2d')) return;
var ctx = canvas.getContext("2d");
//数据
let data = [100,50,25,25]
//求和
let sum = data.reduce((temp, item, index) => {return temp + item})
// 角度占比
let angs = data.map(item => {return 360 * item / sum})
console.log(angs)
// 绘制
let last = 0
angs.forEach(ang => {
pie(cX, cY, r, last, last+ang)
last += ang
})
function pie (cX, cY, r, startAng, endAng) {
ctx.beginPath()
ctx.moveTo(cX, cY) // 圆心
let x = cX + Math.sin(startAng* Math.PI / 180) * r // 求起始点
let y = cY - Math.cos(startAng* Math.PI / 180) * r
ctx.lineTo(x, y)
ctx.arc(cX, cY, r, (startAng - 90) * Math.PI / 180, (endAng - 90) * Math.PI / 180, false)
ctx.stroke()
ctx.closePath()
}
}
draw(200, 300, 100);
}
绘制贝塞尔曲线
2次贝塞尔曲线
quadraticCurveTo(cp1x, cp1y, x, y):
说明:
参数1和2:控制点坐标
参数3和4:结束点坐标
3次贝塞尔曲线
bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y):
说明:
参数1和2:控制点1的坐标
参数3和4:控制点2的坐标
参数5和6:结束点的坐标
线条样式
lineCap = type
线条末端样式。
共有3个值:
butt:线段末端以方形结束
round:线段末端以圆形结束
square:线段末端以方形结束,但是增加了一个宽度和线段相同,高度是线段厚度一半的矩形区域。
lineJoin = type
同一个path内,设定线条与线条间接合处的样式。
共有3个值round, bevel 和 miter:
round:通过填充一个额外的,圆心在相连部分末端的扇形,绘制拐角的形状。 圆角的半径是线段的宽度。
bevel:在相连部分的末端填充一个额外的以三角形为底的区域, 每个部分都有各自独立的矩形拐角。
miter(默认):通过延伸相连部分的外边缘,使其相交于一点,形成一个额外的菱形区域。
图片处理
ctx.drawImage(img, x, y,width ,height)
width 和 height,这两个参数用来控制 当像canvas画入时应该缩放的大小。
要保证当图片完成加载后再实现绘制
let img = new Image() // 创建img
img.src = 'xxx.jpg' // img路径
img.onload = function(){
ctx.drawImage(img, x, y)
}
切片(slice)
drawImage(image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight)
前4个是定义图像源的切片位置和大小,后4个则是定义切片的目标显示位置和大小。
阴影
let canvas = document.getElementById('canvas')
let context = canvas.getContext('2d')
//设置阴影
context.shadowColor="rgba(0,0,0,0.5)"//设置阴影颜色
context.shadowOffsetX=5;//设置形状或路径x轴方向的阴影偏移量,默认值为0;
context.shadowOffsetY=5;//设置形状或路径y轴方向的阴影偏移量,默认值为0;
context.shadowBlur=4;//设置模糊的像素数,默认值为0,即不模糊。
//绘制红色矩形
context.fillStyle="red";
context.fillRect(10,10,50,50);
//绘制半透明的蓝色矩形
context.fillStyle="rgba(0,0,255,1)";
context.fillRect(30,30,50,50);
渐变
渐变由CanvasGradient实例表示。要创建一个新的先行渐变,可以调用createLinearGradient()方法。这个方法接收4个参数:起点的x坐标,起点的y坐标,终点的x坐标,终点的y坐标。创建渐变对象后,下一步就是使用addColorStop()方法来指定色标,这个方法接收两个参数:色标位置和css颜色值。色标位置是一个0(开始的颜色)到1(结束的颜色)之间的数据。
gradient=context.createLinearGradient(30,30,70,70); 相当于画了一条渐变直线
gradient.addColorStop(0,“red”); 渐变开始的颜色
gradient.addColorStop(1,“black”); 渐变结束的颜色
context.fillStyle=gradient; 图形填充颜色为渐变色
let canvas = document.getElementById('canvas')
let context = canvas.getContext('2d')
// 设置渐变
var gradient=context.createLinearGradient(30,30,70,70);
gradient.addColorStop(0,"red");
gradient.addColorStop(1,"black");
//绘制红色矩形
context.fillStyle="red";
context.fillRect(10,10,50,50);
//绘制渐变矩形
context.fillStyle=gradient;
context.fillRect(30,30,50,50);
径向渐变(或放射渐变)
可以使用createRadialGradient()方法,这个方法接收6个参数,对应着两个圆的圆心和半径
var gradient=context.createRadialGradient(55,55,10,55,55,30);
//设置渐变
var gradient=context.createRadialGradient(55,55,10,55,55,30);
gradient.addColorStop(0,"white");
gradient.addColorStop(1,"black");
//绘制红色矩形
context.fillStyle="red";
context.fillRect(10,10,50,50);
//绘制渐变矩形
context.fillStyle=gradient;
context.fillRect(30,30,50,50);
var image=document.images[0];
pattern=context.createPattern(image,"repeat");
context.fillStyle=pattern;
context.fillRect(10,10,300,300);
游戏小人的走动
let canvas = document.getElementById('canvas')
let ctx = canvas.getContext('2d')
let img = new Image()
img.src = '1.jpg'
img.onload = function() {
let frame = 0 // 列 控制行走动作
let d = 'down' // 行 控制行走朝向
let x = 100
let y = 100
let speed = 10
document.onkeydown = function(event) {
// switch(event.keyCode) {
// case 87 || 38:
// d = 'up'
// break;
// case 83 || 40:
// d = 'down'
// break;
// case 65 || 37:
// d = 'left'
// break;
// case 68 || 39:
// d = 'right'
// break;
// }
let keys = {87:'up', 83:'down', 65:'left', 68:'right'}
let keys2 = {38:'up', 40:'down', 37:'left', 39:'right'}
d = keys[event.keyCode] || d || keys2[event.keyCode]
}
setInterval(() => {
let rows = {'down': 0, 'left': 1, 'right': 2, 'up': 3}
ctx.clearRect(0, 0, canvas.width, canvas.height)
ctx.drawImage(img, 10 + 94*frame, 100 * rows[d], 70, 90, x, y, 70, 90)
if (x < 0) {
x = 0
frame = 2
}
console.log(canvas.width)
if (x > canvas.width-70) {
x = canvas.width-70
frame = 2
}
if (y < 0) {
y = canvas.height
} else if (y > canvas.height) {
y = 0
}
switch (d) {
case 'down':
y += speed
break;
case 'up':
y -= speed
break;
case 'left':
x -= speed
break;
case 'right':
x += speed
break
}
frame++
if (frame === 3) {
frame = 0
}
}, 100)
}
像素操作
每个像素占4位:r g b a
若一张图大小为800600 则 宽=800 => 800列
高=600 => 600行
r行c列的某个像素:r800+c
r行c列的某个像素占位:(r*800+c) *4
获得像素区
let imgData = ctx.getImageData(100, 100, img.width, img.height) // 获取像素区
处理像素(黑白:使r=g=b;
昏黄:b=0)
改变像素
ctx.putImageData(imgData, 100, 100)
window.onload = function () {
let canvas = document.getElementById('canvas')
let ctx = canvas.getContext('2d')
let btn = document.querySelector('input')
let img = new Image()
img.src = '22.jpg'
img.onload = function() {
ctx.drawImage(img, 100, 100, 400, 250)
let imgData = ctx.getImageData(100, 100, img.width, img.height) // 获取像素区
btn.onclick = function() {
for(let r = 0; r < img.height; r++) { // 行
for(let c = 0; c< img.width; c++) { // 列
let avg = (imgData.data[(r*img.width + c) * 4] + imgData.data[(r*img.width + c) * 4 + 1] + imgData.data[(r*img.width + c) * 4 + 2]) / 3
imgData.data[(r*img.width + c) * 4] = imgData.data[(r*img.width + c) * 4 + 1] =imgData.data[(r*img.width + c) * 4 + 2] = avg
}
}
ctx.putImageData(imgData, 100, 100)
}
}
}
变形
在变形之前,先保存ctx.save(),变形之后再恢复,ctx.restore()
function draw(){
var canvas = document.querySelector('canvas');
if (!canvas.getContext) return;
var ctx = canvas.getContext("2d");
ctx.save(); //保存坐原点平移之前的状态
ctx.translate(100, 100);
ctx.strokeRect(0, 0, 100, 100)
ctx.restore(); //恢复到最初状态
ctx.translate(220, 220);
ctx.fillRect(0, 0, 100, 100)
}
draw();
2.rotate
这个方法只接受一个参数:旋转的角度(angle),它是顺时针方向的,以弧度为单位的值。
旋转的中心是坐标原点。
3.scale
scale(x, y) 用它来增减图形在 canvas 中的像素数目,对形状,位图进行缩小或者放大。
x,y分别是横轴和纵轴的缩放因子,它们都必须是正值。值比 1.0 小表示缩 小,比 1.0 大则表示放大,值为 1.0 时什么效果都没有。
function draw(){
var canvas = document.querySelector('canvas');
var ctx = canvas.getContext("2d");
let rotate = 0
ctx.strokeRect(100, 100, 200, 200) //先基于原原点画一个矩形
setInterval(() => {
ctx.clearRect(0,0,canvas.width,canvas.height)
rotate++
ctx.save(); //保存坐原点平移之前的状态
ctx.translate(200, 200); // 移动原点
ctx.rotate(Math.PI/180 * rotate) // 基于后来的原点再进行旋转
ctx.fillRect(-100,-100, 200, 200) // 基于后来的原点画一个矩形 实现新矩形围绕矩形中心旋转
ctx.restore()
}, 24)
}
draw();
合成
globalCompositeOperation = type
blue: #0000ff
red: #ff0000
所以重叠部分的颜色:#000000
11. lighten 与lighter 相似
保证重叠部分最量的像素。(每个颜色位进行比较,得到最大的)
blue: #0000ff
red: #ff0000
所以重叠部分的颜色:#ff00ff
12. xor
重叠部分会变成透明
裁剪路径
clip()
把已经创建的路径转换成裁剪路径。
裁剪路径的作用是遮罩。只显示裁剪路径内的区域,裁剪路径外的区域会被隐藏。
注意:clip()只能遮罩在这个方法调用之后绘制的图像,如果是clip()方法调用之前绘制的图像,则无法实现遮罩。
clip()遮住方法之前绘制的图像,显示之后的绘制的区域
动画
动画的基本步骤
1 清空canvas
再绘制每一帧动画之前,需要清空所有。清空所有最简单的做法就是clearRect()方法
2 保存canvas状态
如果在绘制的过程中会更改canvas的状态(颜色、移动了坐标原点等),又在绘制每一帧时都是原始状态的话,则最好保存下canvas的状态
3 绘制动画图形
这一步才是真正的绘制动画帧
4恢复canvas状态
如果你前面保存了canvas状态,则应该在绘制完成一帧之后恢复canvas状态
时钟绘制
Document
声明:
本文借鉴引用 做人要厚道2013 的CSDN 博客 ,全文地址请点击:https://blog.csdn.net/u012468376/article/details/73350998?utm_source=copy