功能: 在网页中实时生成图像,并操作图像内容

看起来和img标签一样,只是canvas只有两个可选的属性 width、heigth 属性,而没有 src、alt 属性

1.兼容


你的浏览器不支持 canvas,请升级你的浏览器。


canvas - 图1

<img> 元素不同,<canvas> 元素需要结束标签(</canvas>)。如果结束标签不存在,则文档的其余部分会被认为是替代内容,将不会显示出来 ### 2. 设置绘图坏境 会创建一个固定大小的画布,会公开一个或多个渲染上下文(画笔),使用渲染上下文来绘制和处理要展示的内容。
var canvas = document.getElementById(‘canvas’);
var cxt = canvas.getContext(‘2d’); ### 3.canvas设置宽高 如果不给canvas设置 widht、height 属性时,则默认 width为300、height 为 150,单位都是 px。也可以使用 css 属性来设置宽高,但是如宽高属性和初始比例不一致,他会出现扭曲。所以,建议永远不要使用 css 属性来设置canvas的宽高

简单的例子

**

1.绘制矩形

fillRect(x, y, width, height) 绘制一个填充的矩形
strokeRect(x, y, width, height) 绘制一个矩形的边框。
clearRect(x, y, widh, height) 清除指定的矩形区域,然后这块区域会变的完全透明
let ctx = couterRef.current.getContext(‘2d’)
let ctx1 = couterRef1.current.getContext(‘2d’)
ctx.fillStyle = “rgb(200,0,0)”
ctx.fillRect(20, 20, 55, 50)
ctx.strokeRect(100, 100, 100, 50)
ctx.clearRect(25, 25, 10, 10)
console.log(1)
ctx1.fillStyle = “rgb(200,0,0)”

2.绘制路径

  1. beginPath()
    新建一条路径,路径一旦创建成功,图形绘制命令被指向到路径上生成路径
  2. moveTo(x, y)
    把画笔移动到指定的坐标(x, y)。相当于设置路径的起始点坐标。
  3. closePath()
    闭合路径之后,图形绘制命令又重新指向到上下文中

    4.lineTo()
    方法添加一个新点,然后创建从该点到画布中最后指定点的线条
    5.stroke
    通过线条来绘制图形轮廓
    6.fill()
    通过填充路径的内容区域生成实心的图形
    let ctx = couterRef.current.getContext(‘2d’)
    ctx.beginPath();
    ctx.moveTo(50, 50);
    ctx.lineTo(200, 50);
    ctx.lineTo(200, 200);
    ctx.lineTo(50, 200)
    // ctx.lineTo(50, 50)
    // ctx.closePath();
    ctx.stroke();
    // ctx.fill();

3.绘制圆弧

1.arcTo(x1, y1, x2, y2, r)

  1. x1 y1 实际上就是这两条线相交后焦点的坐标.<br /> x2 y2 是线上任意坐标.<br /> r是圆的半径<br />![image.png](https://cdn.nlark.com/yuque/0/2020/png/2506381/1599575681024-12d16040-d357-4f54-a49a-c4f116c72fc9.png#align=left&display=inline&height=203&margin=%5Bobject%20Object%5D&name=image.png&originHeight=406&originWidth=1380&size=58135&status=done&style=none&width=690)<br /> ctx.beginPath();<br /> ctx.moveTo(20, 20); // 创建开始点<br /> ctx.lineTo(100, 20); // 创建水平线<br /> ctx.arcTo(150, 20, 150, 70, 50); // 创建弧<br /> ctx.lineTo(150, 120);<br /> ctx.stroke();

2.arc() 方法创建弧/曲线

context.arc(x,y,r,start,end,counterclockwise);
x: 圆的中心的 x 坐标
y:圆的中心的 y 坐标
r:圆的半径
start:起始角
end:结束角
counterclockwise:顺势针还是逆时针
arc(100,75,50,0Math.PI,1.5Math.PI)

image.png
ctx.beginPath();
ctx.arc(50, 50, 40, 0, Math.PI / 2, false);
// ctx.closePath();
ctx.fill();
ctx.stroke();

4.添加样式和颜色

fillStyle = color 设置图形的填充颜色

strokeStyle = color 设置图形轮廓的颜色
color 可以是表示 css 颜色值的字符串、渐变对象或者图案对象
默认情况下,线条和填充颜色都是黑色

1.fillStyle

for (var i = 0; i < 6; i++){
for (var j = 0; j < 6; j++){
ctx.fillStyle = ‘rgb(‘ + Math.floor(255 - 42.5 i) + ‘,’ + Math.floor(255 - 42.5 j) + ‘,0)’; ctx.fillRect(j 50, i 50, 50, 50);
}
}
image.png

2.strokeStyle

function draw(){
var canvas = document.getElementById(‘tutorial’);
if (!canvas.getContext) return;
var ctx = canvas.getContext(“2d”);
for (var i = 0; i < 6; i++){
for (var j = 0; j < 6; j++){
ctx.strokeStyle = rgb(${randomInt(0, 255)},${randomInt(0, 255)},${randomInt(0, 255)}); ctx.strokeRect(j 50, i 50, 40, 40);
}
}
}
draw();

function randomInt(from, to){ return parseInt(Math.random() * (to - from + 1) + from);}

image.png

5.line style

1.lineWidth

线宽。只能是正值。默认是 1.0。
起始点和终点的连线为中心,上下各占线宽的一半
ctx.beginPath();
ctx.moveTo(10, 10);
ctx.lineTo(100, 10);
ctx.lineWidth = 10;
ctx.stroke();
ctx.beginPath();
ctx.moveTo(110, 10);
ctx.lineTo(160, 10)ctx.lineWidth = 20;
ctx.stroke()

image.png

2.lineCap = type

  1. butt:线段末端以方形结束
  2. round:线段末端以圆形结束
  3. square:线段末端以方形结束,但是增加了一个宽度和线段相同,高度是线段厚度一半的矩形区域

var lineCaps = [“butt”, “round”, “square”];
for (var i = 0; i < 3; i++){
ctx.beginPath();
ctx.moveTo(20 + 30 i, 30);
ctx.lineTo(20 + 30
i, 100);
ctx.lineWidth = 20;
ctx.lineCap = lineCaps[i];
ctx.stroke();
}
ctx.beginPath();
ctx.moveTo(0, 30);
ctx.lineTo(300, 30);
ctx.moveTo(0, 100);
ctx.lineTo(300, 100)
ctx.strokeStyle = “red”;
ctx.lineWidth = 1;
ctx.stroke();

image.png

3.lineJoin = type

  1. round 通过填充一个额外的,圆心在相连部分末端的扇形,绘制拐角的形状。 圆角的半径是线段的宽度。
  2. bevel 在相连部分的末端填充一个额外的以三角形为底的区域, 每个部分都有各自独立的矩形拐角。
  3. miter(默认) 通过延伸相连部分的外边缘,使其相交于一点,形成一个额外的菱形区域

var lineJoin = [‘round’, ‘bevel’, ‘miter’];
ctx4.lineWidth = 20;
for (var i = 0; i < lineJoin.length; i++) {
ctx4.lineJoin = lineJoin[i];
ctx4.beginPath();
ctx4.moveTo(50, 50 + i 50);
ctx4.lineTo(100, 100 + i
50);
ctx4.lineTo(150, 50 + i 50);
ctx4.lineTo(200, 100 + i
50);
ctx4.lineTo(250, 50 + i * 50);
ctx4.stroke();
}

image.png

6. 虚线

setLineDash 方法和 lineDashOffset 属性来制定虚线样式。 setLineDash 方法接受一个数组,来指定线段与间隙的交替;lineDashOffset属性设置起始偏移量

  1. function draw(){

    var canvas = document.getElementById(‘tutorial’);
    if (!canvas.getContext) return;
    var ctx = canvas.getContext(“2d”);
    ctx.setLineDash([20, 5]); // [实线长度, 间隙长度]
    ctx.lineDashOffset = -0;
    ctx.strokeRect(50, 50, 210, 210);
    }
    draw();

7.绘制文本

  1. fillText(text, x, y [, maxWidth]) 在指定的 (x,y) 位置填充指定的文本,绘制的最大宽度是可选的。
  2. strokeText(text, x, y [, maxWidth]) 在指定的 (x,y) 位置绘制文本边框,绘制的最大宽度是可选的

font = value 当前我们用来绘制文本的样式。
textAlign = value 文本对齐选项。 可选的值包括:start, end, left, right or center
textBaseline = value 基线对齐选项,可选的值包括: top, hanging, middle, alphabetic, ideographic, bottom
direction = value 文本方向。可能的值包括:ltr, rtl, inherit

8.状态的保存和恢复

  1. save()restore()
  2. saverestore 方法是用来保存和恢复 canvas 状态的,都没有参数。
  3. Canvas 的状态就是当前画面应用的所有样式和变形的一个快照

关于 save() :Canvas状态存储在栈中,每当save()方法被调用后,当前的状态就被推送到栈中保存。
可以调用任意多次 save方法(类似数组的push())
4、关于restore():每一次调用 restore 方法,上一个保存的状态就从栈中弹出,所有设定都恢复(类似数组的 pop())。
当前应用的变形(即移动,旋转和缩放)

strokeStyle, fillStyle, globalAlpha, lineWidth, lineCap, lineJoin, miterLimit, shadowOffsetX, shadowOffsetY, shadowBlur, shadowColor, globalCompositeOperation 的值

当前的裁切路径(clipping path

9.变形

在做变形之前先保存状态是一个良好的习惯

1.translate(x, y)

  1. 用来移动 canvas原点到指定的位置

2.translate 方法接受两个参数。x 是左右偏移量,y 是上下偏移量

2.rotate(angle)

  1. 旋转坐标轴。
  2. 这个方法只接受一个参数:旋转的角度(angle),它是顺时针方向的,以弧度为单位的值。
  3. 旋转的中心是坐标原点

    3.scale(x, y)

    用它来增减图形在 canvas 中的像素数目,对形状,位图进行缩小或者放大。
    x,y,它们都必须是正值。值比 1.0 小表示缩 小,比 1.0 大则表示放大,值为 1.0 时什么效果都没有。

4.transform (变形矩阵)

image.png

10.合成

globalCompositeOperation = type
1.type这是默认设置,新图像会覆盖在原有图像。
image.png
2 source-in 仅仅会出现新图像与原来图像重叠的部分,其他区域都变成透明的。(包括其他的老图像区域也会透明)
image.png
3.source-out 仅仅显示新图像与老图像没有重叠的部分,其余部分全部透明。(老图像也不显示)
image.png
4.source-atop 新图像仅仅显示与老图像重叠区域。老图像仍然可以显示。
image.png
5.destination-over 新图像会在老图像的下面。
image.png
6.destination-in 仅仅新老图像重叠部分的老图像被显示,其他区域全部透明。
image.png
7. destination-out仅仅老图像与新图像没有重叠的部分。 注意显示的是老图像的部分区域。
image.png
8. destination-atop 老图像仅仅仅仅显示重叠部分,新图像会显示在老图像的下面。
image.png
9. lighter 新老图像都显示,但是重叠区域的颜色做加处理。
image.png
10.darken 保留重叠部分最黑的像素。(每个颜色位进行比较,得到最小的)
image.png
11. lighten 保证重叠部分最量的像素。(每个颜色位进行比较,得到最大的)
image.png
12. xor 重叠部分会变成透明。
image.png
13. copy 只有新图像会被保留,其余的全部被清除(边透明)。
image.png

11.动画

1.太阳

import React, { useEffect, useRef } from ‘react’

const SunRender = () => {
const couterRef = useRef()
useEffect(() => {
if (couterRef.current) {
getCanvas()
}
}, [couterRef])

  1. const getCanvas = () => {<br /> let ctx = couterRef.current.getContext("2d");<br /> draw(ctx)<br /> }
  2. const draw = (ctx) => {<br /> requestAnimationFrame(function step() {<br /> run(ctx)<br /> requestAnimationFrame(step);<br /> });<br /> }
  3. const run = (ctx) => {<br /> ctx.clearRect(0, 0, 300, 300); //清空所有的内容<br /> /*绘制 太阳*/<br /> ctx.beginPath();<br /> ctx.fillStyle = "rgb(200,0,0)";<br /> ctx.arc(150, 150, 40, 0, 2 * Math.PI, false);<br /> ctx.fill();<br /> ctx.save();<br /> ctx.translate(150, 150);
  4. //绘制earth轨道<br /> ctx.beginPath();<br /> ctx.strokeStyle = "rgba(255,255,0,0.5)";<br /> ctx.arc(0, 0, 100, 0, 2 * Math.PI)<br /> ctx.stroke()
  5. let time = new Date();<br /> //绘制地球<br /> ctx.rotate(2 * Math.PI / 60 * time.getSeconds() + 2 * Math.PI / 60000 * time.getMilliseconds())<br /> ctx.translate(100, 0);<br /> ctx.beginPath();<br /> ctx.fillStyle = "rgb(51,133,255)";<br /> ctx.arc(0, 0, 10, 0, 2 * Math.PI, false);<br /> ctx.fill();
  6. //绘制月球轨道<br /> ctx.beginPath();<br /> ctx.strokeStyle = "rgba(255,255,255,.3)";<br /> ctx.arc(0, 0, 40, 0, 2 * Math.PI);<br /> ctx.stroke();
  7. //绘制月球<br /> ctx.rotate(2 * Math.PI / 6 * time.getSeconds() + 2 * Math.PI / 6000 * time.getMilliseconds());<br /> ctx.translate(43, 0);<br /> ctx.beginPath();<br /> ctx.fillStyle = "rgb(243,247,9)";<br /> ctx.arc(-3.5, -3.5, 4, 0, 2 * Math.PI, false);<br /> ctx.fill();<br /> ctx.restore();<br /> }
  8. return <div><br /> <canvas style={{background:'black'}}id="solar" width="300" height="300" ref={couterRef}></canvas><br /> </div><br />}

export default SunRender

2.时钟

import React, { useEffect, useRef } from ‘react’

const TimeRender = () => {
const couterRef = useRef()
useEffect(() => {
if (couterRef.current) {
getCanvas()
}
}, [couterRef])

  1. const getCanvas = () => {<br /> let ctx = couterRef.current.getContext('2d')<br /> draw(ctx)<br /> }
  2. const draw = (ctx) => {<br /> requestAnimationFrame(function step() {<br /> drawDial(ctx); //绘制表盘<br /> drawAllHands(ctx); //绘制时分秒针<br /> requestAnimationFrame(step);<br /> })<br /> }
  3. const drawDial = (ctx) => {<br /> let pi = Math.PI;<br /> ctx.clearRect(0, 0, 300, 300); //清除所有内容<br /> ctx.save();
  4. ctx.translate(150, 150); //一定坐标原点到原来的中心<br /> ctx.beginPath();<br /> ctx.arc(0, 0, 148, 0, 2 * pi); //绘制圆周<br /> ctx.stroke();<br /> ctx.closePath();
  5. for (let i = 0; i < 60; i++) {//绘制刻度。<br /> ctx.save();<br /> ctx.rotate(-pi / 2 + i * pi / 30); //旋转坐标轴。坐标轴x的正方形从 向上开始算起<br /> ctx.beginPath();<br /> ctx.moveTo(110, 0);<br /> ctx.lineTo(140, 0);<br /> ctx.lineWidth = i % 5 ? 2 : 4;<br /> ctx.strokeStyle = i % 5 ? "blue" : "red";<br /> ctx.stroke();<br /> ctx.closePath();<br /> ctx.restore();<br /> }<br /> ctx.restore();<br /> }<br /> /*绘制时分秒针*/<br /> const drawAllHands = (ctx) => {<br /> let time = new Date();
  6. let s = time.getSeconds();<br /> let m = time.getMinutes();<br /> let h = time.getHours();
  7. let pi = Math.PI;<br /> let secondAngle = pi / 180 * 6 * s; //计算出来s针的弧度<br /> let minuteAngle = pi / 180 * 6 * m + secondAngle / 60; //计算出来分针的弧度<br /> let hourAngle = pi / 180 * 30 * h + minuteAngle / 12; //计算出来时针的弧度<br /> drawHand(hourAngle, 60, 6, "red", ctx); //绘制时针<br /> drawHand(minuteAngle, 106, 4, "green", ctx); //绘制分针<br /> drawHand(secondAngle, 129, 2, "blue", ctx); //绘制秒针<br /> }

/绘制时针、或分针、或秒针
参数1:要绘制的针的角度
参数2:要绘制的针的长度
参数3:要绘制的针的宽度
参数4:要绘制的针的颜色
参数4:ctx
/
function drawHand(angle, len, width, color, ctx) {
ctx.save();
ctx.translate(150, 150); //把坐标轴的远点平移到原来的中心
ctx.rotate(-Math.PI / 2 + angle); //旋转坐标轴。 x轴就是针的角度
ctx.beginPath();
ctx.moveTo(-4, 0);
ctx.lineTo(len, 0); // 沿着x轴绘制针
ctx.lineWidth = width;
ctx.strokeStyle = color;
ctx.lineCap = “round”;
ctx.stroke();
ctx.closePath();
ctx.restore();
}

  1. return <div><br /> <canvas width='300px' height='300px' ref={couterRef}></canvas><br /> </div><br />}

export default TimeRender