画布(Canvas)实现技术
重要通知
canvas技术,是基于HTML5与JavaScript综合的高级技术,通过对外提供二维图像API接口,可以用于图像绘制处理、实时视频处理和渲染。
基本概况
- HTML5 canvas:https://www.runoob.com/html/html5-canvas.html
- HTML5 canvas参考手册:https://www.runoob.com/tags/ref-canvas.html
- 入门教程:https://www.runoob.com/w3cnote/html5-canvas-intro.html
- canvas教程:https://developer.mozilla.org/zh-CN/docs/Web/API/canvas_API
- 代码例子源码:http://www.corehtml5canvas.com/
- 实时视频捕获:https://developer.mozilla.org/zh-CN/docs/Web/API/HTMLCanvasElement/captureStream
- 案例:https://snayan.github.io/canvas-demo/
基础简介
- 兼容性:不支持IE8及以下版本
- 兼容解决方案:第一、使用explorecanvas,支持IE8及IE8以下;第二、使用Google Chrome Frame,将IE引擎替换为Google Chrome引擎;
- 默认canvas元素大小是300*150个屏幕像素,设置canvas属性时, 不能使用"px"单位, 因为已经默认
坐标系统
- 原点:canvas画布左上角;
- X坐标:向右方增长;
- Y坐标:向下方延伸。
/**
* 将鼠标坐标转换为canvas坐标
* @param {*} canvas:canvas对象
* @param {*} x:document文档 X 坐标
* @param {*} y:document文档 Y 坐标
*/
export function windowToCanvas(canvas, x, y) {
const clientRect = canvas.getBoundingClientRect();
return {
x: x - clientRect.left * (canvas.width / clientRect.width),
y: y - clientRect.top * (canvas.height / clientRect.height)
}
}
基础示例
const canvas = document.createElement( 'canvas' );
const ctx = canvas.getContext( '2d' );
ctx.imageSmoothingEnabled = false; // 设置图像平滑度的属性。
ctx.webkitImageSmoothingEnabled = false;
ctx.mozImageSmoothingEnabled = false;
function updateCanavsResize() {
const width = document.body.clientWidth;
const height = document.body.clientHeight;
canvas.setAttribute('width', width);
canvas.setAttribute('height', height);
}
updateCanavsResize();
document.body.append(canvas);
window.onresize = function () {
updateCanavsResize();
}
性能优化
- 使用空间划分算法(如四叉树)优化碰撞检测
- 实现脏矩形渲染,避免全量重绘
canvas 2d绘图环境
canvasRenderingContext2D对象
canvas对象属性
- 当设置canvas元素的width与height属性值时, 实际上是同时修改了canvas元素自身的大小与canvas元素绘图表面的大小
- 通过CSS属性修改canvas元素的大小, 仅仅修改了canvas元素自身的大小, canvas绘图表面大小依然为默认的300*150个屏幕像素
-------------------------------------------------------------------------------------------------------
属性 描述 类型 取值范围 默认值
-------------------------------------------------------------------------------------------------------
width canvas元素绘图的宽度 非负整数 在有效范围内的任意非负整数 300
height canvas元素绘图的高度 非负整数 在有效范围内的任意非负整数 150
-------------------------------------------------------------------------------------------------------
getContext("2d") 属性
--------------------------------------------------------------------------------------------------------------------------
属性 简介
--------------------------------------------------------------------------------------------------------------------------
canvas 获取canvas对象的宽度与高度, 例如:ctx.canvas.width与ctx.canvas.height
# 文本
font 设置或返回文本内容的当前字体属性
textAlign 设置或返回文本内容的当前对齐方式
textBaseline 设置或返回在绘制文本时使用的当前文本基线
# 颜色、样式和阴影
fillStyle 设置或返回用于填充绘画的颜色、渐变或模式
strokeStyle 设置或返回用于笔触的颜色、渐变或模式
shadowColor 设置或返回用于阴影的颜色
shadowBlur 设置或返回用于阴影的模糊级别,该值为非负、非无穷的double值,默认值为0。
shadowOffsetX 设置或返回阴影距形状的水平距离
shadowOffsetY 设置或返回阴影距形状的垂直距离
# 线条样式
lineCap 设置或返回线条末端线帽的样式;取值类型为butt(平直,默认值)、round(圆形)、square(方形)。
lineWidth 定义线段像素宽度,该值为非负、非无穷的double值,默认值为0,Number类型。
lineJoin 定义两条线段相交时焦点类型, 取值为'bevel'、'round'(圆角)、'miter'(方角,默认值),String类型。
miterLimit 设置或返回最大斜接长度,斜接长度指在两条线交汇处内角和外角之间的距离,当lineJoin = miter有效。
globalAlpha 定义全局透明度, 取值0(完全透明)~1.0(完全不透明);浏览器会将每个像素的alpha值与该值相乘。
# 合成
globalCompsiteOperation 设置或返回如何将一个源(新的)图像绘制到目标(已有的)的图像上。
imageSmoothingEnabled 图像平滑的方式;如果禁用,缩放时,图像不会被平滑处理。
--------------------------------------------------------------------------------------------------------------------------
getContext("2d") 方法
--------------------------------------------------------------------------------------------------------------------------
方法 简介
--------------------------------------------------------------------------------------------------------------------------
# 文本
fillText() 在画布上绘制“被填充的”文本
strokeText() 在画布上绘制文本(无填充)
measureText() 返回包含指定文本宽度的对象, 可以精准计算出文本的宽度
# 路径
fill() 填充当前绘图(路径)
stroke() 绘制已定义的路径
beginPath() 起始一条路径,或重置当前路径
moveTo() 把路径移动到画布中的指定点,不创建线条
closePath() 创建从当前点回到起始点的路径
lineTo() 添加一个新点,然后在画布中创建从该点到最后指定点的线条
clip() 从原始画布剪切任意形状和尺寸的区域
quadraticCurveTo() 创建二次贝塞尔曲线
bezierCurveTo() 创建三次方贝塞尔曲线
arc() 创建弧/曲线(用于创建圆形或部分圆)
arcTo() 创建两切线之间的弧/曲线
isPointInPath() 如果指定的点位于当前路径中,则返回 true,否则返回 false
# 颜色、样式和阴影
createLinearGradient() 创建线性渐变(用在画布内容上)
createPattern() 在指定的方向上重复指定的元素
createRadialGradient() 创建放射状/环形的渐变(用在画布内容上)
addColorStop() 规定渐变对象中的颜色和停止位置
# 矩形
rect() 创建矩形
fillRect() 绘制“被填充”的矩形
strokeRect() 绘制矩形(无填充)
clearRect() 在给定的矩形内清除指定的像素
# 转换
scale() 缩放当前绘图至更大或更小,例如:ctx.scale(2,2);
rotate() 旋转当前绘图,例如:ctx.rotate(1*Math.PI);
translate() 重新映射画布上的 (0,0) 位置
transform() 替换绘图的当前转换矩阵,rotate()、scale()、translate() 或 transform() 完成的其他变换
setTransform() 将当前转换重置为单位矩阵。然后运行 transform()
# 状态的保存和恢复
save() 保存当前环境的状态。
restore() 返回之前保存过的路径状态和属性。
# 其他
createEvent()
getContext()
toDataURL()
--------------------------------------------------------------------------------------------------------------------------
toBlob(callback, type, args)
创建一个用于表示此canvas元素图像文件的Blob
canvas.toBlob(
callback, // 回调函数,浏览器会以一个指向blob的引用为参数,去调用该回调函数
type, // 图像类型,例如:'image/jpeg' | 'image/png'
args // 图像质量,例如:0 ~ 1
);
globalCompositeOperation 属性
合成,设置或返回如何将一个源(新的)图像绘制到目标(已有的)的图像上。 源图像:您打算放置到画布上的绘图。 目标图像:您已经放置在画布上的绘图。
------------------------------------------------------------------------------------------------------------
值 描述
------------------------------------------------------------------------------------------------------------
source-over 默认。在目标图像上显示源图像。
source-atop 在目标图像顶部显示源图像。源图像位于目标图像之外的部分是不可见的。
source-in 在目标图像中显示源图像。只有目标图像之内的源图像部分会显示,目标图像是透明的。
source-out 在目标图像之外显示源图像。只有目标图像之外的源图像部分会显示,目标图像是透明的。
destination-over 在源图像上显示目标图像。
destination-atop 在源图像顶部显示目标图像。目标图像位于源图像之外的部分是不可见的。
destination-in 在源图像中显示目标图像。只有源图像之内的目标图像部分会被显示,源图像是透明的。
destination-out 在源图像之外显示目标图像。只有源图像之外的目标图像部分会被显示,源图像是透明的。
lighter 显示源图像 + 目标图像。
copy 显示源图像。忽略目标图像。
xor 使用异或操作对源图像与目标图像进行组合。
------------------------------------------------------------------------------------------------------------
ctx.globalCompositeOperation = "source-over";
状态的保存和恢复
ctx.fillRect(0, 0, 150, 150);
ctx.save();
ctx.fillStyle = 'red';
ctx.fillRect(15, 15, 120, 120);
ctx.save();
ctx.fillStyle = '#FFF';
ctx.fillRect(30, 30, 90, 90);
ctx.restore();
ctx.fillRect(45, 45, 60, 60);
ctx.restore();
ctx.fillRect(60, 60, 30, 30);
颜色、样式和阴影
属性列表
应用于线条、文本、图形、图像等对象。
------------------------------------------------------------------------------------------------------------
属性 描述
------------------------------------------------------------------------------------------------------------
fillStyle 设置或返回用于填充绘画的颜色、渐变或模式。
strokeStyle 设置或返回用于笔触的颜色、渐变或模式。
shadowColor 设置或返回用于阴影的颜色。
shadowBlur 设置或返回用于阴影的模糊级别,数值越大,模糊扩展范围越大(该值与像素无关)。
shadowOffsetX 设置或返回阴影与形状的水平距离。
shadowOffsetY 设置或返回阴影与形状的垂直距离。
------------------------------------------------------------------------------------------------------------
方法列表
------------------------------------------------------------------------------------------------------------
方法 描述
------------------------------------------------------------------------------------------------------------
createLinearGradient() 创建线性渐变(用在画布内容上)。
createPattern() 在指定的方向上重复指定的元素。
createRadialGradient() 创建放射状/环形的渐变(用在画布内容上)。
addColorStop() 规定渐变对象中的颜色和停止位置。
------------------------------------------------------------------------------------------------------------
渐变
addColorStop() 方法
规定渐变对象中的颜色和位置,与 createLinearGradient() 或 createRadialGradient() 一起使用。
线性渐变
渐变方向:左~右,上~下 填充范围:使用最后一种颜色来填充渐变线以外的区域
var radient = ctx.createLinearGradient(
startX, // 线性渐变起始位置X坐标
startY, // 线性渐变起始位置y坐标
endX, // 线性渐变终止位置x坐标
endY // 线性渐变终止位置y坐标
);
放射渐变
(该方法掌握难度太大,请静下心来仔细研究总结) 放射渐变有两种情况:第一种为起始圆与终止圆的圆心坐标一致,此时渐变规律比较观察;第二种为起始圆与终止圆圆心不同,此时太复杂。 渐变方向:当startRadius < endRadius时,由内向外;当startRadius > endRadius时,由外向内 填充范围:仅局限于由传递给createRadialGradient()方法的两个圆参数定义的区域之内
var grd = ctx.createRadialGradient(
startX, // 内圆圆心坐标X
startY, // 内圆圆心坐标Y
startRadius, // 内圆半径(该半径内颜色不渐变,半径外部开始渐变)
endX, // 外圆圆心坐标X
endY, // 外圆圆心坐标Y
endRadius // 外圆半径(放射区域:endRadius - startRadius)
);
实例
(startRadius < endRadius时,颜色渐变由内向外)
var grd = ctx.createRadialGradient(50, 50, 0, 50, 50, 100);
grd.addColorStop(0, '#f00');
grd.addColorStop(1, "#00f");
ctx.fillStyle = grd;
ctx.fillRect(50, 50, 100, 100);
实例
(startRadius endRadius时,颜色渐变由外向内)
var grd = ctx.createRadialGradient(50, 50, 100, 50, 50, 0);
grd.addColorStop(0, '#f00');
grd.addColorStop(1, "#00f");
ctx.fillStyle = grd;
ctx.fillRect(50, 50, 100, 100);
实例
(起始圆、终止圆与正方形中心相同时,图形很有规律)
var grd = ctx.createRadialGradient(150, 150, 0, 150, 150, 100);
grd.addColorStop(0, '#f00');
grd.addColorStop(1, "#00f");
ctx.fillStyle = grd;
ctx.fillRect(50, 50, 200, 200);
几何图形
线条(Line)
- 属性列表
------------------------------------------------------------------------------------------------------------
属性 描述
------------------------------------------------------------------------------------------------------------
lineCap 设置或返回线条的结束端点样式。
lineJoin 设置或返回两条线相交时,所创建的拐角类型。
lineWidth 设置或返回当前的线条宽度。
miterLimit 设置或返回最大斜接长度。
------------------------------------------------------------------------------------------------------------
// 线段 • 折线:起点 | 终点
ctx.moveTo(20, 50);
ctx.lineTo(500, 110);
ctx.closePath();
ctx.stroke();
ctx.lineWidth = 20; // 设置或返回当前的线条宽度。默认值是 1.0,并且这个属性必须大于 0.0。
ctx.lineJoin = "round"; // 设置或返回两条线相交时,所创建的拐角类型。合法的值是 "round"、"bevel" 和 "miter",默认值是 "miter"。
ctx.lineCap = 'butt'; // 设置或返回线条的结束端点样式,其值分别为:"butt"、"round" 和 "square",默认值是 "butt"。
ctx.miterLimit = 10; // 设置或返回最大斜接长度,表示斜面长度和线条长度的比值。当ctx.lineJoin = "miter"时,通过该属性调整斜面
虚线
function dashedLineTo(ctx, x1, y1, x2, y2, dashLength) {
dashLength = dashLength === undefined ? 5 : dashLength;
var deltaX = x2 - x1;
var deltaY = y2 - y1;
var nameDashes = Math.floor(Math.sqrt(deltaX * deltaX + deltaY * deltaY) / dashLength);
for (var i = 0; i < nameDashes; i++)
{
ctx[i % 2 === 0 ? 'moveTo' : 'lineTo'](x1 + (deltaX / nameDashes) * i, y1 + (deltaY / nameDashes) * i);
}
ctx.stroke();
}
ctx.lineWidth = 3;
ctx.strokeStyle = 'blue';
dashedLineTo(ctx, 20, 20, ctx.canvas.width - 20, 20, 5);
dashedLineTo(ctx, ctx.canvas.width - 20, 20, ctx.canvas.width - 20, ctx.canvas.height - 20, 10);
dashedLineTo(ctx, ctx.canvas.width - 20, ctx.canvas.height - 20, 20, ctx.canvas.height - 20, 15);
dashedLineTo(ctx, 20, ctx.canvas.height - 20, 20, 20, 2);
矩形(Rect)
- 方法列表
------------------------------------------------------------------------------------------------------------
方法 描述
------------------------------------------------------------------------------------------------------------
rect() 创建矩形。
fillRect() 绘制"被填充"的矩形。
strokeRect() 绘制矩形(无填充)。
clearRect() 在给定的矩形内清除指定的像素。
------------------------------------------------------------------------------------------------------------
ctx.strokeRect( // 描边
double x,
double y,
double width,
double height
);
ctx.fillRect( // 填充
double x,
double y,
double width,
double height
);
clearRect( // 清除
double x,
double y,
double width,
double height
);
// 笔触的默认颜色是黑色
ctx.fillStyle = "#f00"; // 背景色
ctx.rect(10, 10, 150, 100); // 长宽
ctx.fill(); // 填充
ctx.stroke(); // 描边-边框
ctx.fillStyle = "#0f0"; // 可以设置透明度:'rgba(0, 0, 0, 0.5)'
ctx.fillRect(200, 10, 150, 100);
ctx.strokeStyle = "#000";
ctx.strokeRect(400, 50, 150, 100);
// 实例(清除)
ctx.canvas.onclick = function() {
ctx.clearRect(10, 10, ctx.canvas.width, ctx.canvas.height);
}
圆形-圆弧
圆形(圆形处于半封闭状态时,便可以成为圆弧,这里比较特殊,也很复杂,请仔细研究)
特别注意,特别声明,绘制圆形的填充颜色区域或描边颜色区域是在圆环上的起始位置、在圆环上的终止位置与该两点之间的圆环封闭区域
ctx.arc(
Number x, // 圆心X坐标
Number y, // 圆心y坐标
Number radius, // 圆半径
Number startRdian, // 圆在圆环上的起始位置,以弧度为单位,例如:0
Number stopRdian, // 圆在圆环上的终止位置,以弧度为单位,例如:2 * Math.PI
Boolean direction // 取值为true(逆时针方向)、false(默认值,顺时针方向)
);
代码示例
// 实例(描边,当第五个参数不等于2 * Math.PI或不等于2 * Math.PI的整倍数时,绘制为圆弧,且弧线不关闭,起始点与终止点没有实线连接)
ctx.strokeStyle = '#f00';
ctx.beginPath();
ctx.arc(150, 150, 100, 0, 1 * Math.PI, true);
ctx.stroke();
// 实例(填充圆)
ctx.fillStyle = '#f00';
ctx.beginPath();
ctx.arc(150, 150, 100, 0, 0.1 * Math.PI, true);
ctx.closePath(); //声明关闭路径
ctx.fill();
圆弧:创建介于两个切线(先设置一个点,由前四个参数构成两个点,三个点两个切线)之间的弧/曲线
通常情况下,由该方法ctx.moveTo(20, 20)定义第一条切点的起始点坐标 startX与stopX最好设置为相同y坐标,如此效果比较明显,容易总结规律
ctx.arcTo(
Number startX, // 两切线交点的横坐标
Number startY, // 两切线交点的纵坐标
Number stopX, // 第二条切线上一点的x坐标
Number stopY, // 第二条切线上一点的y坐标
Number radius // 圆弧半径
);
代码示例
// 实例(描边,使用ctx.closePath()时,起始点与终止点之间存在实线连接,即处于封闭状态
ctx.beginPath();
ctx.arc(150, 150, 100, 0, 1 * Math.PI, true);
ctx.strokeStyle = '#f00';
ctx.closePath();
ctx.stroke();
// 实例(arcTo()方法,该方法掌握难度太大,请静下心来仔细研究总结)
ctx.beginPath();
ctx.moveTo(20, 20); //圆弧的第一条切线起始点坐标
ctx.arcTo(150, 20, 150, 200, 100); // 创建圆弧
ctx.stroke();
路径(Path)
方法列表
------------------------------------------------------------------------------------------------------------
方法 描述
------------------------------------------------------------------------------------------------------------
fill() 填充当前绘图(路径)。
stroke() 绘制已定义的路径。
beginPath() 起始一条路径,或重置当前路径。
moveTo() 把路径移动到画布中的指定点,不创建线条。
closePath() 创建从当前点回到起始点的路径。
lineTo() 添加一个新点,然后在画布中创建从该点到最后指定点的线条。
clip() 从原始画布剪切任意形状和尺寸的区域,裁剪路径的作用是遮罩。只显示裁剪路径内的区域,裁剪路径外的区域会被隐藏。
quadraticCurveTo() 创建二次贝塞尔曲线。
bezierCurveTo() 创建三次贝塞尔曲线。
arc() 创建弧/曲线(用于创建圆形或部分圆)。
arcTo() 创建两切线之间的弧/曲线。
isPointInPath() 如果指定的点位于当前路径中,则返回 true,否则返回 false。
------------------------------------------------------------------------------------------------------------
平方贝塞尔曲线
二次曲线,由三个点定义,两个描点,一个控制点。
quadraticCurveTo(
double controlX, // 控制点x坐标
double controlY, // 控制点y坐标
double stopX, // 第二个描点的x坐标
double stopY // 第二个描点的y坐标
)
- 代码示例
ctx.strokeStyle = "#f00";
ctx.beginPath();
ctx.moveTo(20, 20);
ctx.quadraticCurveTo(50, 30, 70, 100);
ctx.stroke();
立方贝塞尔曲线
三次曲线,由四个点定义,两个描点,两个控制点。
ctx.bezierCurveTo(
double control1X, // 第一个控制点x坐标
double control1Y, // 第一个控制点y坐标
double control2X, // 第二个控制点x坐标
double control2Y, // 第二个控制点y坐标
double stopX, // 第二个描点的x坐标
double stopY // 第二个描点的y坐标
);
- 代码示例
ctx.strokeStyle = "#f00";
ctx.beginPath();
ctx.moveTo(20, 20);
ctx.bezierCurveTo(200, 80, 20, 150, 200, 200);
ctx.stroke();
基本操作
- 方法列表
------------------------------------------------------------------------------------------------------------
方法 描述
------------------------------------------------------------------------------------------------------------
scale() 缩放当前绘图至更大或更小。
rotate() 旋转当前绘图。
translate() 重新映射画布上的 (0,0) 位置。
transform() 替换绘图的当前转换矩阵。
setTransform() 将当前转换重置为单位矩阵。然后运行 transform()。
------------------------------------------------------------------------------------------------------------
平移 translate
translate(x, y): 重新映射画布上的 (0,0) 位置 x:添加到水平坐标(x)上的值。 y:添加到垂直坐标(y)上的值。
旋转 rotate
rotate(angle):旋转当前绘图 angle:旋转角度,以弧度计。
缩放 scale
scale(scalewidth, scaleheight):缩放当前绘图至更大或更小 scalewidth:缩放当前绘图的宽度(1=100%,0.5=50%,2=200%,依次类推)。 scaleheight:缩放当前绘图的高度(1=100%,0.5=50%,2=200%,依次类推)
变换 transform
transform(a, b, c, d, e, f):替换绘图的当前转换矩阵 该变换只会影响 transform() 方法调用之后的绘图。 transform() 方法的行为相对于由 rotate()、scale()、translate() 或 transform() 完成的其他变换。
ctx.transform(
a, // 水平缩放绘图。
b, // 水平倾斜绘图。
c, // 垂直倾斜绘图。
d, // 垂直缩放绘图。
e, // 水平移动绘图。
f // 垂直移动绘图。
);
setTransform()
画布上的每个对象都拥有一个当前的变换矩阵。 把当前的变换矩阵重置为单位矩阵,然后以相同的参数运行 transform()。 setTransform() 允许您缩放、旋转、移动并倾斜当前的环境。
ctx.setTransform(
a, // 水平缩放绘图。
b, // 水平倾斜绘图。
c, // 垂直倾斜绘图。
d, // 垂直缩放绘图。
e, // 水平移动绘图。
f // 垂直移动绘图。
);
裁剪(clip)
ctx.clip();
从原始画布中剪切任意形状和尺寸。 一旦剪切了某个区域,则所有之后的绘图都会被限制在被剪切的区域内(不能访问画布上的其他区域)。 您也可以在使用 clip() 方法前通过使用 save() 方法对当前画布区域进行保存,并在以后的任意时间对其进行恢复(通过 restore() 方法)。 所以可以通过清除制定范围来实现擦除功能。
实现功能 | 橡皮檫 | 放大镜 | 查看器
橡皮檫
import { windowToCanvas } from '/public/library/canvas.js';
ctx.fillStyle = "#f00"; // 背景色
ctx.rect(0, 0, canvas.width, canvas.height); // 长宽
ctx.fill(); // 填充
let action = { start: false, startX: null, startY: null, };
const rubberSize = 25;
document.querySelector('canvas').addEventListener('mousedown', function() {
action.start = true;
}, false);
document.querySelector('canvas').addEventListener('mousemove', function(e) {
if (!action.start) return;
const coordinate = windowToCanvas(canvas, e.clientX, e.clientY);
ctx.save();
ctx.beginPath();
// 绘制圆形
ctx.arc(coordinate.x, coordinate.y, rubberSize/2, 0, Math.PI * 2);
// 声明裁剪区域为圆形
ctx.clip();
// 清除所有区域,但是由于声明了裁剪机制,则仅仅清除了与圆形相交的公共区域。
ctx.clearRect(coordinate.x - rubberSize/2, coordinate.y - rubberSize/2, rubberSize, rubberSize);
ctx.restore();
}, false);
document.querySelector('canvas').addEventListener('mouseup', function() {
action.start = false;
}, false);
Text文本
属性列表
------------------------------------------------------------------------------------------------------------
属性 描述
------------------------------------------------------------------------------------------------------------
font 设置或返回文本内容的当前字体属性。
textAlign 设置或返回文本内容的当前对齐方式。
textBaseline 设置或返回在绘制文本时使用的当前文本基线。
------------------------------------------------------------------------------------------------------------
方法列表
------------------------------------------------------------------------------------------------------------
方法 描述
------------------------------------------------------------------------------------------------------------
fillText() 在画布上绘制"被填充的"文本。
strokeText() 在画布上绘制文本(无填充)。
measureText() 返回包含指定文本宽度的对象。
------------------------------------------------------------------------------------------------------------
strokeText
文本(描边) | 文本轮廓
ctx.strokeText(
String content, // 需要填充的文本
Number x, // 该文本位于canvas元素中的x坐标
Number y // 该文本位于canvas元素中的y坐标
);
ctx.font = "48px Arial";
ctx.strokeStyle = "#f00";
ctx.strokeText("你好, 欢迎使用canvas", 50, 50);
fillText
文本(填充) | 填充文本
ctx.fillText(
String content, // 需要填充的文本
Number x, // 该文本位于canvas元素中的x坐标
Number y // 该文本位于canvas元素中的y坐标
);
ctx.font = "48px Arial";
ctx.fillStyle = "#f00";
ctx.fillText("文本", 190, 100);
measureText
计算字符串宽度
function calculateTextSize(text = '文本', fontSize = 16, fontFamily = '宋体') {
const canvas = document.createElement('canvas');
const ctx = canvas.getContext("2d");
ctx.font = `${fontSize} ${fontFamily}`;
ctx.fillStyle = '#000000';
const { width } = ctx.measureText(text); // 文案尺寸总长度
return width;
}
function getStyle(nodeName, keyName) {
// nodeName:"#id"、".class"、"div"、document.querySelector(target)
if (!nodeName || !((nodeName).nodeType === 1 || typeof nodeName === 'string')) throw new Error('缺失节点对象');
if (typeof nodeName === 'string' && !document.querySelector(nodeName)) throw new Error('缺失节点对象');
var nodeTarget = (nodeName).nodeType === 1 ? nodeName : document.querySelector(nodeName);
// 判断浏览器是否为IE-IE8及IE8以下:!!window.ActiveXObject || "ActiveXObject" in window
return (!!window.ActiveXObject || "ActiveXObject" in window) ? nodeName.currentStyle[keyName] : document.defaultView.getComputedStyle(nodeName, null)[keyName];
}
let target = document.querySelector('#text');
let content = document.all ? target.innerText : target.textContent;
var fontFamily = getStyle(target, 'fontFamily');
var fontSize = getStyle(target, 'fontSize');
// console.log(calculateTextSize(content, fontSize, fontFamily));
事件系统 Event
createEvent()
isPointInPath(x, y)
判断当前焦点是否为指定区域,如果指定的点位于当前路径中,返回 true,否则返回 false。
语法结构
isPointInPath(
x, // 检测点的 X 坐标
y // 检测点的 Y 坐标
)
isPointInPath(
x, // 检测点的 X 坐标
y, // 检测点的 Y 坐标
fillRule // 用来决定点在路径内还是在路径外的算法,其值为nonzero(非零环绕规则,默认的规则) | evenodd(奇偶环绕原则)。
)
isPointInPath(
path, // Path2D应用的路径。
x, // 检测点的 X 坐标
y // 检测点的 Y 坐标
)
isPointInPath(
path, // Path2D应用的路径。
x, // 检测点的 X 坐标
y, // 检测点的 Y 坐标
fillRule // 用来决定点在路径内还是在路径外的算法,其值为nonzero(非零环绕规则,默认的规则) | evenodd(奇偶环绕原则)。
)
代码示例
canvas.addEventListener("click", (e)=> {
console.log(ctx.isPointInPath(e.clientX, e.clientY));
}, false);
ctx.isPointInPath(x, y);
ctx.isPointInPath(x, y, fillRule);
具体思路与方法
像素检测法 角度法 射线法
代码示例
<script type='text/javascript'>
var canvas = document.createElement('canvas');
var ctx = canvas.getContext("2d");
let WIDTH = document.body.clientWidth;
let HEIGHT = document.body.clientHeight;
canvas.setAttribute('width', WIDTH);
canvas.setAttribute('height', HEIGHT);
document.body.append(canvas);
</script>
<script type='module'>
import { randomHexColor } from '/public/module/color.js';
import { SetObjectCoordinate } from '/Visual/Canvas/common/index.js';
const GRAPHIC_POINTS = {};
const caseRect = { l: 10, t: 10, w: 150, h: 100, };
ctx.fillStyle = "#f00";
ctx.rect(caseRect.l, caseRect.t, caseRect.w, caseRect.h);
ctx.fill();
GRAPHIC_POINTS['caseRect'] = { ...caseRect, ...SetObjectCoordinate(ctx) };
const caseCircle = { l: 85, t: 200, radius: 75 };
ctx.beginPath();
ctx.arc(caseCircle.l, caseCircle.t, caseCircle.radius, 0, 2 * Math.PI);
ctx.fillStyle = '#0f0';
ctx.fill();
ctx.closePath();
GRAPHIC_POINTS['caseCircle'] = { ...caseCircle, ...SetObjectCoordinate(ctx) };
console.log(GRAPHIC_POINTS, 'GRAPHIC_POINTS');
// 单击
canvas.onclick = (e) => {
Object.keys(GRAPHIC_POINTS).forEach((key) => {
if (GRAPHIC_POINTS[key].x[e.clientX] && GRAPHIC_POINTS[key].y[e.clientY]) {
canvasModel(key);
}
});
};
// 移动
canvas.onmousemove = (e) => {
Object.keys(GRAPHIC_POINTS).forEach((key) => {
if (GRAPHIC_POINTS[key].x[e.clientX] && GRAPHIC_POINTS[key].y[e.clientY]) {
canvasModel(key);
}
});
};
function canvasModel(type) {
switch(type) {
case 'caseRect':
{
const { l, t, w, h } = GRAPHIC_POINTS[type];
ctx.clearRect(l, t, w, h);
ctx.fillStyle = randomHexColor();
ctx.fillRect(caseRect.l, caseRect.t, caseRect.w, caseRect.h);
}
break;
case 'caseCircle':
{
const { l, t, radius } = GRAPHIC_POINTS[type];
ctx.clearRect(l - radius, t - radius, radius * 2, radius * 2);
ctx.beginPath();
ctx.arc(l, t, radius, 0, 2 * Math.PI);
ctx.fillStyle = randomHexColor();
ctx.fill();
ctx.closePath();
}
break;
default:
//
}
}
</script>
动画(Animate)
动画循环:持续的更新与重绘
window.setInterval():Window系统下表现为15毫秒;Forefix浏览器表现为10毫秒;Chrome浏览器表现为1毫秒
window.setTimeout():Window系统下表现为15毫秒;Forefix浏览器表现为10毫秒;Chrome浏览器表现为1毫秒
window.requestAnimationFrame(callback)
window.requestAnimationFrame = window.requestAnimationFrame || window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame || window.msRequestAnimationFrame;
var val = 0;
function test() {
val += 1;
console.log(val);
window.requestAnimationFrame(test);
}
window.requestAnimationFrame(test);
双缓冲技术
作用:防止闪烁
图像操作技术
- 属性列表
------------------------------------------------------------------------------------------------------------
属性 描述
------------------------------------------------------------------------------------------------------------
width 返回 ImageData 对象的宽度。
height 返回 ImageData 对象的高度。
data 返回一个对象,其包含指定的 ImageData 对象的图像数据。
------------------------------------------------------------------------------------------------------------
- 代码示例
const imgData = ctx.createImageData(100, 100);
console.info(imgData.width);
console.info(imgData.height);
console.info(imgData.data.length);
- 方法列表
------------------------------------------------------------------------------------------------------------
方法 描述
------------------------------------------------------------------------------------------------------------
createImageData() 创建新的、空白的 ImageData 对象。
getImageData() 返回 ImageData 对象,该对象为画布上指定的矩形复制像素数据。
putImageData() 把图像数据(从指定的 ImageData 对象)放回画布上。
------------------------------------------------------------------------------------------------------------
drawImage()方法
向画布上绘制图像、画布或视频,使用drawImage之前必须提前加载图像,否则不成功。
imageSmoothingEnabled属性
过度缩放图像可能会导致图像模糊或像素化,通过使用绘图环境的imageSmoothingEnabled属性来控制是否在缩放图像时使用平滑算法。默认值为true,即启用平滑缩放。
- 在画布上定位图像
ctx.drawImage(
img, // <必选> 规定要使用的图像、画布或视频数据,且已经加载成功。
x, // <必选> 距离文档左侧的X坐标
y // <必选> 距离文档左侧的Y坐标
);
- 在画布上定位图像,并规定图像的宽度和高度
ctx.drawImage(
img, // <必选> 规定要使用的图像、画布或视频数据,且已经加载成功。
x, // <必选> 距离文档左侧的X坐标
y, // <必选> 距离文档左侧的Y坐标
width, // [可选] 图像在画布中显示的宽度(伸展或缩小图像),默认为图像的原始宽度。
height // [可选] 图像在画布中显示的高度(伸展或缩小图像),默认为图像的原始高度。
);
- 剪切图像,并在画布上定位被剪切的部分,可以运用在图像缩放与移动的场景
ctx.drawImage(
img, // <必选> 规定要使用的图像、画布或视频数据,且已经加载成功。
sX, // <必选> 在图像中的开始裁剪 X 位置。
sY, // <必选> 在图像中的开始裁剪 Y 位置。
swidth, // <必选> 裁剪图像的宽度(swidth < width,则图像看起来被拉伸,反之则被压缩),注意:宽度是基于img数据源的实际宽度
sheight, // <必选> 裁剪图像的高度(sheight < height,则图像看起来被拉伸,反之则被压缩),注意:高度是基于img数据源的实际高度
x, // <必选> 距离文档左侧的X坐标
y, // <必选> 距离文档左侧的Y坐标
width, // [可选] 图像在画布中显示的宽度(伸展或缩小图像),默认为图像的原始宽度。
height // [可选] 图像在画布中显示的高度(伸展或缩小图像),默认为图像的原始高度。
);
createPattern()方法
在指定的方向内重复指定的元素(图片、视频,或者其他
<canvas>
)。
// 填充背景
ctx.fillStyle = ctx.createPattern(
image, // 规定要使用的模式的图片、画布或视频元素。
type // repeat 默认。该模式在水平和垂直方向重复。repeat-x 该模式只在水平方向重复。repeat-y 该模式只在垂直方向重复。no-repeat 该模式只显示一次(不重复)。
);
createImageData()
创建新的、空白的 ImageData 对象。
- 以指定的尺寸(以像素计)创建新的 ImageData 对象
const imgData = ctx.createImageData(
width, // ImageData 对象的宽度,以像素计。
height // ImageData 对象的高度,以像素计。
);
- 创建与指定的另一个 ImageData 对象尺寸相同的新 ImageData 对象(不会复制图像数据)
const imgData = ctx.createImageData(
imageData // 另一个 ImageData 对象。
);
getImageData()
返回 ImageData 对象,该对象为画布上指定的矩形复制像素数据。
ctx.getImageData(
x, // 开始复制的左上角位置的 x 坐标(以像素计)。
y, // 开始复制的左上角位置的 y 坐标(以像素计)。
w, // 要复制的矩形区域的宽度。
h // 要复制的矩形区域的高度。
);
- 案例
ctx.fillStyle="red";
ctx.fillRect(10, 10, 50, 50);
var imgData = ctx.getImageData(10, 10, 50, 50);
ctx.putImageData(imgData, 10, 70);
putImageData()
把图像数据(从指定的 ImageData 对象)放回画布上,重写图像数据,ImageData 对象中的每个像素,都存在着四方面的信息,即 RGBA 值,所以数组的大小是 ImageData 对象的四倍:widthheight4。
ctx.putImageData(
imgData, // 规定要放回画布的 ImageData 对象。
x, // ImageData 对象左上角的 x 坐标,以像素计。ImageData 对象距离画布顶部的 X 位置。
y, // ImageData 对象左上角的 y 坐标,以像素计。ImageData 对象距离画布左侧的 Y 位置。
dirtyX, // 可选。水平值(x),以像素计,在画布上放置图像的位置(图像基于自己,隐藏图像部分尺寸)。
dirtyY, // 可选。垂直值(y),以像素计,在画布上放置图像的位置(图像基于自己,隐藏图像部分尺寸)。
dirtyWidth, // 可选。在画布上绘制图像所使用的宽度。
dirtyHeight, // 可选。在画布上绘制图像所使用的高度。
);
- 代码示例
const imgData = ctx.createImageData(100, 100);
// RGBA 值 imgData.data.length == 40000
for (var i = 0; i < imgData.data.length; i += 4) {
imgData.data[i+0] = 255;
imgData.data[i+1] = 0;
imgData.data[i+2] = 0;
imgData.data[i+3] = 255;
}
ctx.putImageData(imgData, 0, 0);
toDataURL([type, encoderOptions])
导出画布canvas对象的图像数据源链接,返回一个数据地址(data URL),可以用于设定img元素的src属性值。
- ctx.toDataURL([type, encoderOptions]):返回一个包含图片展示的 data URI 。
- type:[可选],图片格式,默认为 image/png。
- encoderOptions:[可选],指定图片格式为 image/jpeg 或 image/webp的情况下,可以从 0 到 1 的区间内选择图片的质量。如果超出取值范围,将会使用默认值 0.92。其他参数会被忽略。
// 创建画布对象
const canvas = document.createElement( 'canvas' );
// 导出画布数据源链接
image.src = canvas.toDataURL(
type, // 图像类型,[可选] 例如:'image/jpeg' | 'image/png'
encoderOptions // 图像质量,[可选] double,例如:0 ~ 1
);
Audio音频
Video视频
File文件
Canvas
Canvas API 提供了一个通过JavaScript 和 HTML的
<canvas>
元素来绘制图形的方式。它可以用于动画、游戏画面、数据可视化、图片编辑以及实时视频处理等方面。
代码示例
var canvas = document.createElement('canvas');
var ctx = canvas.getContext("2d"); // 返回canvas元素的绘图环境对象
let width = document.body.clientWidth;
let height = document.body.clientHeight;
canvas.setAttribute('width', width);
canvas.setAttribute('height', height);
document.body.append(canvas);
状态管理
- 保存save():保存当前canvas绘图环境的所有属性
- 恢复restore():恢复当前canvas绘图环境的所有属性
ctx.fillStyle = '#999';
ctx.fillRect(0, 0, 50, 50);
//保存当前canvas绘图环境的属性
ctx.save();
ctx.fillStyle = '#f00';
ctx.fillRect(60, 0, 50, 50); // 背景色为:#f00
// //恢复经ctx.save()当前canvas绘图环境的属性
ctx.restore();
ctx.fillRect( 150, 0, 50, 50); // 背景色为:#999
离屏canvas
离屏,即不将canvas画布作为实际展示元素,而是仅仅作为处理图像的工具,然后将canvas数据导出并利用到其他元素在文档中呈现出来。
<body class='test'>
<canvas class='canvasCtn' id='canvasCtn' width='800' height='400' style='display: none;'></canvas>
<img src='' id='imgcanvas'/>
</body>
<script type="text/javascript">
var ctxCtn = document.getElementById("canvasCtn");
var imgcanvas = document.getElementById('imgcanvas');
imgcanvas.src = ctxCtn.toDataURL();
</script>
将鼠标坐标转换为canvas坐标
/**
* 将鼠标坐标转换为canvas坐标
* @param {*} canvas:canvas对象
* @param {*} x:document文档 X 坐标
* @param {*} y:document文档 Y 坐标
*/
export function windowToCanvas(canvas, x, y) {
const clientRect = canvas.getBoundingClientRect();
return {
x: x - clientRect.left * (canvas.width / clientRect.width),
y: y - clientRect.top * (canvas.height / clientRect.height)
}
}
实现橡皮檫功能
import { windowToCanvas } from '/Visual/Canvas/common/canvas.js';
ctx.fillStyle = "#f00"; // 背景色
ctx.rect(0, 0, canvas.width, canvas.height); // 长宽
ctx.fill(); // 填充
let action = { start: false, startX: null, startY: null, };
const rubberSize = 25;
document.querySelector('canvas').addEventListener('mousedown', function() {
action.start = true;
}, false);
document.querySelector('canvas').addEventListener('mousemove', function(e) {
if (!action.start) return;
const coordinate = windowToCanvas(canvas, e.clientX, e.clientY);
ctx.save();
ctx.beginPath();
// 绘制圆形
ctx.arc(coordinate.x, coordinate.y, rubberSize/2, 0, Math.PI * 2);
// 声明裁剪区域为圆形
ctx.clip();
// 清除所有区域,但是由于声明了裁剪机制,则仅仅清除了与圆形相交的公共区域。
ctx.clearRect(coordinate.x - rubberSize/2, coordinate.y - rubberSize/2, rubberSize, rubberSize);
ctx.restore();
}, false);
document.querySelector('canvas').addEventListener('mouseup', function() {
action.start = false;
}, false);
captureStream()
该 HTMLCanvasElement.captureStream() 方法返回的 CanvasCaptureMediaStream 是一个实时视频捕获的画布。
插件体系
canvg
https://github.com/ctxg/ctxg svg转ctxa
html2canvas
官网:http://html2canvas.hertzen.com/ GitHub:https://github.com/niklasvh/html2canvas/ 文档:http://html2canvas.hertzen.com/documentation 配置:http://html2canvas.hertzen.com/configuration 它不会制作实际屏幕截图,而是根据页面上的可用信息构建屏幕截图。
yarn add html2canvas
html2canvas(document.querySelector("#capture")).then(canvas => {
document.body.appendChild(canvas)
});
- 缺陷
不支持iframe 不支持跨域图片 不能在浏览器插件中使用 部分浏览器上不支持SVG图片 不支持Flash 不支持古代浏览器和IE