Canvas基础
  8CtDmU74qicX 2023年11月24日 19 0

一 什么是canvas ?
MDN 中这样定义:

是 HTML5 新增的元素,一个可以使用脚本(通常为 JavaScript) 在其中绘制图像的 HTML 元素。它可以用来制作照片集或者制作简单(也不是那么简单)的动画,甚至可以进行实时视频处理和渲染。

我们可以这样认为, 标签只是一个矩形的画布。JavaScript 就是画笔,负责在画布上画画。

Canvas 是为了解决 Web 页面中只能显示静态图片这个问题而提出的,一个可以使用 JavaScript 等脚本语言向其中绘制图像的 HTML 标签。
二 绘图基础

  1. 创建canvas 上下文,为画板设置宽高
const canvas = document.querySelector('canvas');

// 获取渲染上下文,通过上下文来进行绘图

const ctx = canvas.getContext('2d'); // webgl,webgl2

// 设置canvas的宽高

// 在html中设置,或者通过js设置 (用css设置,会把画板拉长 )

// canvas 的宽高只能通过js 进行设置,且不能为 canvas.style.width 这样设置,也不能有单位。正确的设置如下:

canvas.width = 400;

canvas.height = 400;

// 开始绘图

// stroke() :描边 strokeStyle填充的颜色

// fill() :填充 fillStyle 描边颜色

ctx.beginPath();

  1. canvas实际上有两套尺寸,一个是元素本身的大小,另一个是元素绘图表面(drawing surface)的大小,通过CSS只能改变元素本身的尺寸,而通过canvas标签属性能够改变元素本身和元素绘图表面的大小。
  2. 理解 栅格 (grid) 和坐标空间

[图片]

理解画布的坐标点,规定在第四象限内,x轴和y轴为正,这个和我们原来认知的第二象限均为正是不一致的。这一点需要特别注意。 后续涉及画板中涉及到坐标原点的平移、网格的旋转以及缩放至关重要。

3. canvas 的ctx 中提供的一些基础绘图方法

Canvas基础_数据


4. 理解beginPath 和 closePath

  1. beginPath 为起始一条新路径,或者重置当前路径。 也就是,在beginPath后面设置的 stroleStyle 和 fillStyle 对前面已经绘制的图形都不生效了。每次绘制一个新图形(与已经绘制的图形无关)的时候,都必须先使用改属性。
  2. closePath (路径闭合到起始点) ,从字面理解 就是每次绘制完成后,当前的图形就与后面的图形无关了,就如同上面的beginPath的功能一样, 但实际上并不是,closePath 只是一个路径合并的功能,也就是回到初始点的功能:就如同 lineTo(初始点x,初始点y)。
  3. 绘制矩形
  4. ctx.rect (x,y,width,height); width 和 height 可以为负数,负数表示反向绘制
  5. ctx.fillRect : 相当于在绘制完成后自动调用了一次,ctx.fill();
  6. ctx.strokeRect : 相当于在绘制完成后自动调用了一次,ctx.stroke();
  7. ctx.clearRect : 清空矩形内的所有内容,然后这块区域会变的完全透明
1. ctx.beginPath();

ctx.rect(x,y,width,height); // x,y 为矩形左上角坐标,width、height为矩形的宽高

ctx.strokeStyle = 'gray';

ctx.fillStyle = 'green';

ctx.stroke();

ctx.fill();
  1. 绘制路径 Line(path)- moveTo 和 lineTo
    图形的基本元素是路径。
    路径是通过不同颜色和宽度的线段或曲线相连形成的不同形状的点的集合。
    基本步骤为
  2. 创建路径起始点 ctx.moveTo(x,y)
  3. 调用绘制方法去绘制出路径 ctx.lineTo(x,y)
  4. 把路径封闭 (绘制图形时候需要封闭路径) ctx.closePath()
  5. 一旦路径生成,通过描边或填充路径区域来渲染图形 ctx.stroke()或者ctx.fill(); 注意看是否需要重新设置 strokeStyle 或者 lineWidth等,如果不重新设置 会复用前面绘图设置的属性或者使用默认属性。

其他方法

  1. lineWidth 用于设置 line 描边 的宽度
  2. lineCap 用于设置线段末端的样式
  3. butt:线段末端以方形结束
  4. round:线段末端以圆形结束
  5. square:线段末端以方形结束,但是增加了一个宽度和线段相同,高度是线段厚度一半的矩形区域。
    C. lineJoin = type
  6. round 通过填充一个额外的,圆心在相连部分末端的扇形,绘制拐角的形状。 圆角的半径是线段的宽度。
  7. bevel 在相连部分的末端填充一个额外的以三角形为底的区域, 每个部分都有各自独立的矩形拐角。
  8. miter(默认) 通过延伸相连部分的外边缘,使其相交于一点,形成一个额外的菱形区域。

ctx.lineJoin = "bevel";
ctx.lineJoin = "round";
ctx.lineJoin = "miter";
7. 绘制圆弧

  1. arc(x, y, r, startAngle, endAngle, anticlockwise) 绘制圆幅x、y 为圆心坐标,, r半径 ,startAngle,endAngle为 开始的幅度和结束的幅度,anticlockwise默认为false是顺时针,true为逆时针.
// 将度数转化为弧度

function toRadians(degrees) {

return (Math.PI / 180) * degrees;

}// 绘制 圆弧
ctx.beginPath();
ctx.moveTo(0,0); // 起点坐标
ctx.arc(0,0,0,toRadians(45),false);
ctx.closePath(); // 回到起始点
ctx.strokeStyle = 'red';
ctx.stroke();
2. 3. 
arcTo(x1, y1, x2, y2, radius)   根据起始点,控制点1(x1,y1),控制点2(x2,y2),radius 圆幅半径4. ctx.beginPath();
5. ctx.moveTo(50, 50);
6. //参数1、2:控制点1坐标   参数3、4:控制点2坐标  参数4:圆弧半径
7. ctx.arcTo(200, 50, 200, 200, 100);
8. ctx.lineTo(200, 200)
9. ctx.stroke();


  1. Canvas基础_缩放_02

  2. 绘制文本
  3. fillText(text, x, y [, maxWidth]) 在指定的 (x,y) 位置填充指定的文本,绘制的最大宽度是可选的。
  4. strokeText(text, x, y [, maxWidth]) 在指定的 (x,y) 位置绘制文本边框,绘制的最大宽度是可选的。
1. 
如何让文本换行? ctx.measureText(text); 根据font 信息自动 获取文本的宽度
// 绘制文本

ctx.beginPath();

ctx.font = '100px sans-serif';

ctx.textAlign = 'center'; // start,end,center,left,right,默认为start

ctx.fillText('天若有情', 10, 100);

ctx.strokeText('天若有情', 10, 200);
  1. 阴影
  2. ctx.shadowColor = "red"; // 阴影颜色
  3. ctx.shadowBlur = 10; // 阴影模糊度
  4. ctx.shadowOffsetX = 10 ;// 阴影x方向偏移量
  5. ctx.shadowOffsetY = 10 ;// 阴影y方向偏移量
  6. ctx.fillStyle = "#000";
  7. ctx.fillRect(10, 10, 100, 100);
  8. Canvas基础_Data_03

  9. 绘制椭圆
1. ctx.ellipse(x, y, radiusX, radiusY, rotation, startAngle, endAngle, anticlockwise);

x,y 为椭圆圆心坐标

radiusX 为长轴x半径, radiusY 为短轴y半径

rotation 为旋转弧度

startAngle, endAngle 开始和结束的弧度

anticlockwise 默认true为顺时针,false 为逆时针

// 如果长轴和短轴半径设置一样将会出现一个常规的⚪形

ctx.ellipse(200, 200, 200, 100, 0, 0, Math.PI*2);

ctx.stroke()
  1. getImageData 和 putImageData(获取矩形区域内 ImageData 数据)
    备注:通过getImageData 获取的数据 包含了基础的数据比如lineWidth,strokeStyle,fillStyle 等等
7. const imgData = ctx.getImageData(x, y, width, height) ;

// 对imageData做一系列的处理(比如黑白化)

ctx.putImageData(imgData, 50, 184);

在Canvas中绘制表面的保存与恢复(应用场景 - 先对当前canvas整体进行保存,然后canvas进行各种绘制操作,最后恢复原状 - 历史记录-回退功能)

// 在操作前 - 保存整个canvas上的数据

const imageData = ctx.getImageData(0,0,canvas.width,canvas.height);// 中间做各种绘图操作

ctx.save();

//绘图

ctx.restore();// 回退到历史版本

ctx.putImageData(imageData,0,0);


12. 检测一个点是否在fill区域或者stroke描边上
IsPointInPath()
ctx.rect(20, 10, 200, 200);
ctx.stroke();
console.log(ctx.isPointInPath(20, 10)); // true
IsPointInStroke()
ctx.rect(20, 10, 200, 200);
ctx.stroke();
console.log(ctx.isPointInStroke(20, 10)); // true
13. 旋转,缩放,平移
变换

  • translate(x,y):
  • 偏移:从起始点为基准点,移动到当前坐标位置
  • rotate(r):
  • 旋转:以画布起点为旋转中心,参数为弧度
  • scale(x,y):
  • 缩放:参数为缩放宽高比例

ctx.rotate(弧度)
将整个画布旋转(注意,为当前beginPath 后绘制的区域矩形进行旋转操作,其他不影响)
Scale(x,y)
缩放 canvas 的坐标系,只影响坐标,之后的绘制会受此方法影响,之前已经绘制好的效果不会有任何变化。
x:canvas 坐标系水平缩放的比例。支持小数,-1代表水平翻转。
y:canvas 坐标系垂直缩放的比例。支持小数,-1代表垂直翻转。
Translate(x,y) 对当前网格添加平移变换的方法。
X: x方向平移的距离
Y: y方向平移的距离

// 旋转

ctx.rotate(60*Math.PI / 180)

ctx.font = '24px Songti'

ctx.fillText('我是王大锤', 50, 0) // 重置当前的变换矩阵为初始态 ctx.setTransform(1, 0, 0, 1, 0, 0);// 缩放

ctx.fillRect(10, 10, 10, 10);

ctx.scale(10, 3);  // 缩放

ctx.fillRect(10, 10, 10, 10); // 再次绘制

ctx.setTransform(1, 0, 0, 1, 0, 0); // 恢复坐标系// ctx.setTransform(a,b,c,d,e,f)

/**

a (m11)水平缩放。

b (m12)垂直倾斜。

c (m21)水平倾斜。

d (m22)垂直缩放。

e (dx)水平移动。

f (dy)垂直移动。

*/// 平移

ctx.translate(50, 50);

ctx.fillRect(0,0,100,100);

ctx.setTransform(1, 0, 0, 1, 0, 0);


14. 绘制图片

  1. drawImage(image, x, y, width, height); // imgame 可以为创建的img元素,也可以为页面中的元素 - 使用元素获取器进行获取,或者图片的位图数据等。不加width和height 将展示原图
    CanvasImageSource 包括以下数据
  • HTMLImageElement
  • SVGImageElement
  • HTMLVideoElement
  • HTMLCanvasElement
  • ImageBitmap
  • OffscreenCanvas
  1. 切片 drawImage(image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight)
    image相同。前 4 个是定义图像源的切片位置和大小,后 4 个则是定义切片的目标显示位置和大小
  2. Save 与 restore 在绘制一些图形时,我们需要改变 一些基础属性,比如lineWidth, strokeStyle 等等。但是又不想改变全局的,那么比较好的办法就是 在改变这些属性前先进行save,绘制结束后再 调用 restore
1. 
// 绘制矩形

ctx.save();

// 这中间改变的状态将会只对该次绘制生效,用完调用restore 又恢复默认的状态

ctx.lineWidth = 4;

ctx.strokeStyle = 'blue';

ctx.strokeRect(60, 60, 100, 100);

ctx.restore();
  1. canvas的其他方法
    16.1 canvas转图片地址
    canvas.toDataUrl();将当前cavas绘制的内容转化为图片数据,可以提供给img使用,也可提供给background用作背景图,也可提供给其他canvas使用
    1. 通过a标签下载canvas的图片
2. function downLoad(src) {

const canvas = document.createElement('canvas');

const ctx = canvas.getContext('2d');

const img = new Image();

img.src = src;

// 解决图片跨域问题

img.crossOrigin = '';

img.onload = () => {

canvas.width = img.width;

canvas.height = img.height;

ctx.drawImage(img, 0, 0);

const url = canvas.toDataURL('image/png');

const aDom = document.createElement('a');

aDom.download = 'download';

// 新建点击事件

const event = new MouseEvent('click');

aDom.href = url;

aDom.dispatchEvent(event);

};

}

16.2  Canvas 转图片数据blob数据

canvas.toBlob(cb,type);  //type,默认为 image/png格式其他知识

URL.createObjectURL(file) 这个新的URL 对象表示指定的 File 对象或 Blob 对象. 得到本地内存容器的URL地址,方便预览,URL.createObjectURL(file)存在于当前doucment内,清除方式只有unload()事件或revokeObjectURL()手动清除 ,在多次使用需要注意手动释放内存的问题,性能优秀。

FileReader.readAsDataURL(file)胜在直接转为base64格式,可以直接用于业务,无需二次转换格式。

canvas.toBlob((blob) => {

// 获取该blob数据在本地内存容器中的url地址,可以提供给img使用

const url = URL.createObjectURL(blob);

const img = new Image();

img.onload = () => {

URL.revokeObjectURL(url);

document.body.appendChild(img);

};

img.src = url;

});17. canvas 事件分发系统方案
18. 离屏渲染

  1. 什么是离屏渲染?就是在屏幕之外预渲染一个Canvas,之后通过drawImage的形式将其绘制到屏幕要显示的Canvas上面,对形状相似或者重复的对象绘制性能提升非常高。
    三 绘图的基础数学知识
  2. 求解代数方程
  3. 向量运算
  4. Canvas基础_缩放_04

  5. 三角函数
  6. 1. 角度与弧度:在canvas中 都是以弧度进行表示的角的大小的,在js中Math.sin/cos/tan 都是用弧度进行计算的。 我们一般学习的时候都习惯使用角度。所以需要了解 弧度与角度之间的转化。
  7. Math.PI = 180度; Math.PI2 = 360度。
    function toRadian(x){
    return (Math.PI/180)
    x;
  8. }
  9. 正弦 sin(θ)=对边/斜边; sin 30 = 1/2; sin 45 = √2/2;sin60 = √3/2 ;
  10. 余弦cos(θ)=邻边/斜边; con30 = √3/2 ;con 45 = √2/2; con 60= 1/2;
  11. 正切 tan(θ)=对边/邻边; tan30=√3/3;tan45=1;tan60=√3;tan90无解;
  12. Canvas基础_缩放_05

四 Canvas 常见的优化方案

  1. 避免浮点数的坐标点
    绘制图形时,长宽与坐标应选取整数而不是浮点数,原因在于Canvas支持半个像素绘制,会根据小数位实现插值算法实现绘制图像的反锯齿效果,所以没有必要请不要选择浮点数值。
  2. 使用多层画布去画一个复杂的场景
    将经常运动的元素和不常变动的元素进行分层,避免不必要的重绘。
  3. 用csstransform特性缩放画布
    如果你使用 left、top 这些 CSS 属性来写动画的话,那么会触发整个像素渲染流程 —— paint、layout 和 composition。
    但是使用 transform 中的 translateX/Y 来切换动画,你将会发现,这并不会触发 paint 和 layout,仅仅会触发 composition 的阶段。
    这是因为 transform 调用的是 GPU 而不是 CPU。
  4. 离屏渲染
    四 canvas 实践
  5. 前端水印制作
  6. 通过canvas 绘制文本,旋转
  7. 利用 canvas.toDataUrl() 将当前canvas转化为图片地址 canvasUrl
  8. 设置一个外层div,position:fixed。z-index:设置为最大。background:url( canvasUrl)
  9. 给外层div 设置css属性,让所有事件都进行穿透 pointer-events:none;
  10. 将一张图片转化为黑白

整个流程中所用到的主要Canvas API有:
绘制图像: drawImage()

  1. drawImage(image,dx,dy)
  2. drawImage(image,dx,dy,dWidth,dHeight);
  3. drawImage(image,sx,sy,swidth,sHeight,dx,dy,dWidth,dHeight); // 图片裁切
1. image:CanvasImageSource =  HTMLImageElement | SVGImageElement | HTMLVideoElement | HTMLCanvasElement | ImageBitmap | OffscreenCanvas (离屏渲染的Canvas画布-在window 和 worker上下文都可用)

dx,dy: 图片在画布中的坐标(左上角)

dWidth,dHeight: 绘制的图片的宽高

获取图像数据: getImageData(sx, sy, sWidth, sHeight)

重写图像数据: putImageData(imageData,sx,sy)

const imageData = context.getImageData(sx, sy, sWidth, sHeight);

// sx,sy 需要返回图像数据区域的起始坐标

// sWidth,sHeight 需要返回图像区域的宽高// 解决canvas图片getImageData,toDataURL跨域问题

// 配置 img.crossOrigin = '';// 对imageData 进行处理灰度处理

var length = imageData.data.length;

for (var index = 0; index < length; index += 4) {

var r = imageData.data[index];

var g = imageData.data[index + 1];

var b = imageData.data[index + 2];

// 计算灰度

var gray = r * 0.299 + g * 0.587 + b * 0.114;

imageData.data[index] = gray;

imageData.data[index + 1] = gray;

imageData.data[index + 2] = gray;

}// 利用 putImageData 更新某个区域内图片绘制

context.putImageData(imageData, 75, 34);

导出图像: toDataURL(mimeType?,quality?)
Canvas本质上就是一个位图图像,因此,浏览器提供了若干API可以将Canvas图像转换成可以作为img呈现的数据,其中最老牌的方法就是HTMLCanvasElement.toDataURL(),此方法可以返回Canvas图像对应的data URI,也就是平常我们所说的base64地址。

const imgSrc = cavansELe.toDataURL(mimeType?,quality?);

// mimeType 可选:image/png- 默认,image/jpeg,image/webp ;可以利用这个特性来修改图片的格式

// png,jpeg,webp格式图片有何区别?// quality 可选,压缩质量 默认为0.92。 只有在mimeType 设置为 mage/jpeg,image/webp才生效;可以利用这个特性来压缩图片质量,但是同样的压缩比,要比ps效果要差一些
// 检测浏览器是否支持webp

var isSupportWebp = !![].map && document.createElement('canvas').toDataURL('image/webp').indexOf('data:image/webp') == 0;// 提供给图片使用

const img = new Image();

img.onload = function(){

// drawImage 等操作再这里执行

}

img.src = imgSrc;
  1. Canvas 实现图片前端压缩 参考:https://www.zhangxinxu.com/study/201707/js-compress-image-before-upload.html 具体实现步骤和原理
21. 
// html input type = 'file' 文件选择器 , multiple 代表可以选择多个文件


// js

const eleFile = document.querySelector('#file');eleFile.onchange = (e)=>{

// 读取选择的文件 e.target.files  {name:图片名称,size:大小,type:image/jpg}

};

参考文档

  1. 一步步教你利用Canvas对图片进行处理
  2. Canvas API中文文档首页地图
  3. CanvasStudy
  4. 学习 HTML5 Canvas 这一篇文章就够了
  5. 如何用canvas实现在线签名?
  6. 前端推荐!10分钟带你了解Konva运行原理
  7. canvas事件模拟
  8. canvas进阶——如何实现canvas的事件系统
  9. Web-worker + skia绘图
  10. 关于浏览器缓存、CacheStorage、Web Worker 与 Service Worker
【版权声明】本文内容来自摩杜云社区用户原创、第三方投稿、转载,内容版权归原作者所有。本网站的目的在于传递更多信息,不拥有版权,亦不承担相应法律责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@moduyun.com

上一篇: hdfs 下一篇: Linux架构及运维行业发展趋势
  1. 分享:
最后一次编辑于 2023年11月24日 0

暂无评论

推荐阅读
8CtDmU74qicX