canvas
www.w3school.com.cn/tags/html_r…
canvas元素
<canvas>
是 HTML5
新增的,一个可以使用脚本(通常为 JavaScript
) 在其中绘制图像的 HTML
元素。它可以用来制作照片集或者制作简单(也不是那么简单)的动画,甚至可以进行实时视频处理和渲染。
<canvas>
看起来和 <img>
标签一样,只是 <canvas>
只有两个可选的属性 width、heigth
属性,而没有 src、alt
属性。
如果不给 <canvas>
设置 widht、height
属性时,则默认 width
为300、height
为 150,单位都是 px
。也可以使用 css
属性来设置宽高,但是如宽高属性和初始比例不一致,他会出现扭曲。所以,建议永远不要使用 css
属性来设置 <canvas>
的宽高。
渲染上下文(Thre Rending Context)
canvas元素创造了一个固定大小的画布,它公开了一个或多个渲染上下文,其可以用来绘制和处理要展示的内容。我们将会将注意力放在2D渲染上下文中。 元素有一个叫做 getContext() 的方法,这个方法是用来获得渲染上下文和它的绘画功能。
var canvas = document.getElementById('tutorial');
//获得 2d 上下文对象
var ctx = canvas.getContext('2d');
绘制矩形
不同于 SVG, 只支持两种形式的图形绘制:矩形和路径(由一系列点连成的线段)。所有其他类型的图形都是通过一条或者多条路径组合而成的。
canvas提供了三种方法绘制矩形:
- fillRect(x, y, width, height) 绘制一个填充的矩形,默认填充黑色
- strokeRect(x, y, width, height) 绘制一个矩形的边框
- clearRect(x, y, width, height) 清除指定矩形区域,让清除部分完全透明。
x与y指定了在canvas画布上所绘制的矩形的左上角(相对于原点)的坐标。width和height设置矩形的尺寸。
绘制路径
图形的基本元素是路径。路径是通过不同颜色和宽度的线段或曲线相连形成的不同形状的点的集合。一个路径,甚至一个子路径,都是闭合的。
- 首先,你需要创建路径起始点。
- 然后你使用画图命令去画出路径。
- 之后你把路径封闭。
- 一旦路径生成,你就能通过描边或填充路径区域来渲染图形。
-
beginPath()
·新建一条路径,生成之后,图形绘制命令被指向到路径上生成路径
-
closePath()
闭合路径之后图形绘制命令又重新指向到上下文中。
-
moveTo(x,y)
方法把路径移动到画布中的指定点,不创建线条。
-
stroke()
通过线条来绘制图形轮廓
-
lineTo(x,y)
绘制一条从当前位置到指定x以及y位置的直线。
-
fill()
通过填充路径的内容区域生成实心的图形。
注意:当你调用fill()函数时,所有没有闭合的形状都会自动闭合,所以你不需要调用closePath()函数。但是调用stroke()时不会自动闭合* *。
绘制一个三角形
例如,绘制三角形的代码如下:
function draw() {
var canvas = document.getElementById('canvas');
if (canvas.getContext) {
var ctx = canvas.getContext('2d');
ctx.beginPath();
ctx.moveTo(75, 50);
ctx.lineTo(100, 75);
ctx.lineTo(100, 25);
ctx.fill();
}
}
样式和颜色
颜色绘制
- fillStyle = color 设置图形的填充颜色。
- strokeStyle = color 设置图形轮廓的颜色。
一旦您设置了 strokeStyle
或者 fillStyle
的值,那么这个新值就会成为新绘制的图形的默认值。如果你要给每个图形上不同的颜色,你需要重新设置 fillStyle
或 strokeStyle
的值。
-
透明度 Transparency
除了可以绘制实色图形,我们还可以用 canvas 来绘制半透明的图形。通过设置
globalAlpha
属性或者使用一个半透明颜色作为轮廓或填充的样式。-
globalAlpha = transparencyValue
这个属性影响到 canvas 里所有图形的透明度,有效的值范围是 0.0 (完全透明)到 1.0(完全不透明),默认是 1.0。
-
线型 Line styles
- lineWidth = value 设置线条宽度。
- lineCap = type 设置线条末端样式。
- lineJoin = type 设定线条与线条间接合处的样式。
- miterLimit = value 限制当两条线相交时交接处最大长度;所谓交接处长度(斜接长度)是指线条交接处内角顶点到外角顶点的长度。
- getLineDash() 返回一个包含当前虚线样式,长度为非负偶数的数组。
- setLineDash(segments) 设置当前虚线样式。
- lineDashOffset = value 设置虚线样式的起始偏移量。
渐变 Gradients
就好像一般的绘图软件一样,我们可以用线性或者径向的渐变来填充或描边。我们用下面的方法新建一个 canvasGradient 对象,并且赋给图形的 fillStyle 或 strokeStyle 属性。
- createLinearGradient(x1, y1, x2, y2) createLinearGradient 方法接受 4 个参数,表示渐变的起点 (x1,y1) 与终点 (x2,y2)。
- createRadialGradient(x1, y1, r1, x2, y2, r2) createRadialGradient 方法接受 6 个参数,前三个定义一个以 (x1,y1) 为原点,半径为 r1 的圆,后三个参数则定义另一个以 (x2,y2) 为原点,半径为 r2 的圆。
var lineargradient = ctx.createLinearGradient(0,0,150,150);
var radialgradient = ctx.createRadialGradient(75,75,0,75,75,100);
Copy to Clipboard
创建出 canvasGradient
对象后,我们就可以用 addColorStop
方法给它上色了。
-
gradient.addColorStop(position, color)
addColorStop 方法接受 2 个参数,
position
参数必须是一个 0.0 与 1.0 之间的数值,表示渐变中颜色所在的相对位置。例如,0.5 表示颜色会出现在正中间。color
参数必须是一个有效的 CSS 颜色值(如 #FFF, rgba(0,0,0,1),等等)。
图案样式 Patterns
上一节的一个例子里面,我用了循环来实现图案的效果。其实,有一个更加简单的方法:createPattern。
createPattern(image, type) 该方法接受两个参数。Image 可以是一个 Image 对象的引用,或者另一个 canvas 对象。Type 必须是下面的字符串值之一:repeat,repeat-x,repeat-y 和 no-repeat。 注意: 用 canvas 对象作为 Image 参数在 Firefox 1.5 (Gecko 1.8) 中是无效的。 图案的应用跟渐变很类似的,创建出一个 pattern 之后,赋给 fillStyle 或 strokeStyle 属性即可。
var img = new Image(); img.src = 'someimage.png'; var ptrn = ctx.createPattern(img,'repeat');
阴影 Shadows
- shadowOffsetX = float shadowOffsetX 和 shadowOffsetY 用来设定阴影在 X 和 Y 轴的延伸距离,它们是不受变换矩阵所影响的。负值表示阴影会往上或左延伸,正值则表示会往下或右延伸,它们默认都为 0。
- shadowOffsetY = float shadowOffsetX 和 shadowOffsetY 用来设定阴影在 X 和 Y 轴的延伸距离,它们是不受变换矩阵所影响的。负值表示阴影会往上或左延伸,正值则表示会往下或右延伸,它们默认都为 0。
- shadowBlur = float shadowBlur 用于设定阴影的模糊程度,其数值并不跟像素数量挂钩,也不受变换矩阵的影响,默认为 0。
- shadowColor = color shadowColor 是标准的 CSS 颜色值,用于设定阴影颜色效果,默认是全透明的黑色
图像
上传图像
<input type="file"></input>
function getObjectURL(file) {
var url = null;
if (window.createObjcectURL != undefined) {
url = window.createOjcectURL(file);
} else if (window.URL != undefined) {
url = window.URL.createObjectURL(file);
} else if (window.webkitURL != undefined) {
url = window.webkitURL.createObjectURL(file);
}
return url;
}
var objURL = getObjectURL(imgFile);//imgFile文件对象
//objURL -->blob:http://127.0.0.1:5500/d17cdcc3-b698-4011-a6d2-f63360cdd22b
// 你可以像使用普通 URL 那样使用它,比如用在 img.src 上。
$("#img").attr("src", objURL);
通过window.URL.createObjectURL方法可以把一个blob转化为一个Blob URL,并且用做文件下载或者图片显示的链接。File对象是一种特殊的Blob对象
每次你调用window.URL.createObjectURL(),就会产生一个唯一的对象URL,即使是你对一个已创建了对象URL的文件再次创建一个对象URL。每个创建了的对象URL必须要释放。当文档关闭时,它们会自动被释放。如果你的网页要动态使用它们,你需要显式调用 window.URL.revokeObjectURL() 来释放它们
绘制图片
获取图片的方式
-
HTMLImageElement 图片由Image()函数构造出来,或者任何的
元素
-
用脚本创建一个新的 HTMLImageElement 对象,使用Image()构造函数
var img = new Image(); // 创建一个<img>元素 img.src = objURL; // 设置图片源地址
若调用
drawImage
时,图片没装载完,那什么都不会发生(在一些旧的浏览器中可能会抛出异常)。因此你应该用load事件来保证不会在加载完毕之前使用这个图片://已知图像路径 var img = new Image(); // 创建img元素 img.src = 'myImage.png'; // 设置图片源地址 img.onload = function(){ // 执行drawImage语句 //1.创建的img对象 ctx.drawImage(this,offsetX,0,imgW,imgH); //2.文中的img标签 ctx.drawImage(document.getElementById("img"), 0, 0, imgW, imgH); }
-
-
HTMLVideoElement 用一个HTML的 元素作为你的图片源,可以从视频中抓取当前帧作为一个图像
-
HTMLCanvasElement 可以使用另一个 元素作为你的图片源。
-
HTMLCanvasElement.toDataURL()
获取canvas对应的data-URL (一串 Base64 编码的字符串)。canvas.toDataURL(type, encoderOptions); type 可选的 默认格式类型为 image/png. encoderOptions 可选的 表示所述图像质量对于使用的图像格式即使用有损压缩,如 image/jpeg和image/webp。 如果此参数是其他任何参数,则使用图像质量的默认值。默认值为0.92
var canvas = document.getElementById("canvas"); var dataURL = canvas.toDataURL(); //dataURL,相当于就是src img.src = dataURL //src-> //'data:image/gif;base64,R0lGODlhCwALAIAAAAAA3pn/ZiH5BAEAAAEALAAAAAALAAsAAAIUhA+hkcuO4lmNVindo7qyrIXiGBYAOw==';
设置jpegs图片的质量
var fullQuality = canvas.toDataURL("image/jpeg", 1.0); // data:image/jpeg;base64,/9j/4AAQSkZJRgABAQ...9oADAMBAAIRAxEAPwD/AD/6AP/Z" var mediumQuality = canvas.toDataURL("image/jpeg", 0.5); var lowQuality = canvas.toDataURL("image/jpeg", 0.1);
-
-
ImageBitmap 这是一个高性能的位图,可以低延迟地绘制,它可以从上述的所有源以及其它几种源中生成。
剪切板
-
Clipboard.JS:Selection 与 execCommand API
运行
ClipboardJS.isSupported()
来检查是否支持clipboard.js
点击按钮两次才执行
-
图像写入剪切板
-
base64,file和Blob的转换
-
阮一峰:剪贴板操作 Clipboard API 教程
文字
<input id="foo" type="text" value="大家好,我是阿宝哥">
<button class="btn" data-clipboard-action="copy" data-clipboard-target="#foo">复制</button>
<script>
var clipboard = new ClipboardJS('.btn');
clipboard.on('success', function(e) {
console.log(e);
});
clipboard.on('error', function(e) {
console.log(e);
});
</script>
除了 input
元素之外,复制的目标还可以是 div
或 textarea
元素。在以上示例中,我们复制的目标是通过 data-* 属性 来指定。此外,我们也可以在实例化 clipboard 对象时,设置复制的目标:
// https://github.com/zenorocha/clipboard.js/blob/master/demo/function-target.html
let clipboard = new ClipboardJS('.btn', {
target: function() {
return document.querySelector('div');
}
});
复制代码
如果需要设置复制的文本,我们也可以在实例化 clipboard 对象时,设置复制的文本:
// https://github.com/zenorocha/clipboard.js/blob/master/demo/function-text.html
let clipboard = new ClipboardJS('.btn', {
text: function() {
return '大家好,我是阿宝哥';
}
});
图像
//将base64转换为blob对象
//https://juejin.cn/post/6844903862873112583
function dataURLtoFile(dataurl, filename) {
var arr = dataurl.split(",");
var mime = arr[0].match(/:(.*?);/)[1];
var bstr = atob(arr[1]);
var n = bstr.length;
var u8arr = new Uint8Array(n);
while (n--) {
u8arr[n] = bstr.charCodeAt(n);
}
//转换成file对象
//return new File([u8arr], filename, { type: mime });
//转换成成blob对象
return new Blob([u8arr], { type: mime });
}
var imageBlob = dataURLtoFile(src);
const item = new ClipboardItem({
[imageBlob.type]: imageBlob,
});
//检测浏览器是否支持clipboard-write
async function askWritePermission() {
try {
const { state } = await navigator.permissions.query({
name: "clipboard-write",
});
return state === "granted";
} catch (error) {
return false;
}
}
if (askWritePermission()) {
navigator.clipboard.write([item]);
alert("成功复制到剪切板");
} else {
alert("不支持复制");
}
Chrome 浏览器规定,navigator.clipboard只有 HTTPS 协议的页面才能使用这个 API。不过,开发环境(localhost
)允许使用非加密协议。
blob对象
developer.mozilla.org/zh-CN/docs/…
Blob
对象表示一个不可变、原始数据的类文件对象。它的数据可以按文本或二进制的格式进行读取
如你所见,myBlob 对象含有两个属性:size 和 type。其中 size 属性用于表示数据的大小(以字节为单位),type 是 MIME 类型的字符串。Blob 表示的不一定是 JavaScript 原生格式的数据。比如 File 接口基于 Blob,继承了 blob 的功能并将其扩展使其支持用户系统上的文件。
Blob
由一个可选的字符串 type
(通常是 MIME 类型)和 blobParts
组成:
Blob 构造函数的语法为:
var aBlob = new Blob(blobParts, options);
blobParts:它是一个由 ArrayBuffer,ArrayBufferView,Blob,DOMString 等对象构成的数组。DOMStrings 会被编码为 UTF-8。
options:一个可选的对象,包含以下两个属性:
- type —— 默认值为
""
,它代表了将会被放入到 blob 中的数组内容的 MIME 类型。 - endings —— 默认值为
"transparent"
,用于指定包含行结束符\n
的字符串如何被写入。 它是以下两个值中的一个:"native"
,代表行结束符会被更改为适合宿主操作系统文件系统的换行符,或者"transparent"
,代表会保持 blob 中保存的结束符不变。
file对象
developer.mozilla.org/zh-CN/docs/…
File 对象是来自用户在一个 元素上选择文件后返回的 FileList 对象,也可以是来自由拖放操作生成的 DataTransfer 对象
File 对象是特殊类型的 Blob,且可以用在任意的 Blob 类型的 context 中
ImageData 对象
ImageData对象中存储着canvas对象真实的像素数据
getImageData(left, top, width, height)
获得一个包含画布场景像素数据的ImageData对像
-
**
width
**图片宽度,单位是像素 -
height
图片高度,单位是像素 -
data
Uint8ClampedArray
类型的一维数组,包含着RGBA格式的整型数据,范围在0至255之间(包括255)。
putImageData() 对场景进行像素数据的写入
图像模糊
developer.mozilla.org/zh-CN/docs/…
设备像素比
设备像素比(window.devicePixelRatio)当前显示设备的物理像素分辨率与CSS像素分辨率之比,此值也可以解释为像素大小的比率:一个CSS像素的大小与一个物理像素的大小。 简单来说,它告诉浏览器应使用多少屏幕实际像素来绘制单个CSS像素。
在很早以前还没高分屏的时候,开发写的的1像素也就是实际的1像素(如果不考虑缩放的情况),你根本不需要做什么特殊的处理。如果css中设置100px那他就是100px。
后来出现了高分屏的手机,并且在window对象下面出现了devicePixelRatio 这个神秘的属性,并且还可以用devicePixelRatio在媒体查询中进行判断。这个属性的意思就是:渲染时,css中的像素(逻辑像素)候和实际像素(物理像素)的比值。比如说:iPhone 4S它的devicePixelRatio 属性的值是2,那就是100px逻辑像素等于200px的设备实际像素。原先需要一个像素绘制的点,现在会用两个像素来绘制。
canvas 的 css 宽高与画布宽高
<canvas id="canvas" width="200" height="200"></canvas>
canvas 标签中的 width
和 height
属性并不是 css 中的宽高,而是 canvas 画布宽高(绘图区域),当不设置 canvas 的 css 宽高时,canvas 会将 width
和 height
的值作为 css 宽高,而 css 宽高是元素在页面上的可见尺寸
但是 canvas 的上下文宽高略奇怪,它可不管像素比是 1 是 2 还是 3,它就是会将整个 canvas 绘图区域塞进 css 宽高中并且填满,绘图的时候会将绘制的图形的宽高按照塞进 css 时宽与高的缩放比率分别进行缩放(所以如果缩放比率不同,就会导致绘制的图形变形)
但是上面这些都不是导致模糊的真正原因,下面这个才是捣乱的元凶:
canvas 绘图时,会从两个物理像素的中间位置开始绘制并向两边扩散 0.5 个物理像素。当设备像素比为 1 时,一个 1px 的线条实际上占据了两个物理像素(每个像素实际上只占一半),由于不存在 0.5 个像素,所以这两个像素本来不应该被绘制的部分也被绘制了,于是 1 物理像素的线条变成了 2 物理像素,视觉上就造成了模糊
解决绘图模糊
创建的图片的时候根据devicePixelRatio 放大数倍(比原照片更大的新的一张图片)然后再用css再把它缩小到原来的样子。因此缩小后的图片不会超过自己原来的尺寸并且不会再模糊。
var getPixelRatio = function (context) {
var backingStore = context.backingStorePixelRatio ||
context.webkitBackingStorePixelRatio ||
context.mozBackingStorePixelRatio ||
context.msBackingStorePixelRatio ||
context.oBackingStorePixelRatio ||
context.backingStorePixelRatio || 1;
return (window.devicePixelRatio || 1) / backingStore;
};
var pixelRatio = getPixelRatio(canvas);
console.log(pixelRatio)
//设置宽高
function setCanvasArea(canvas, ctx) {
canvas.width = Math.floor(cw * pixelRatio);
canvas.height = Math.floor(ch * pixelRatio);
// 设置canvas的真实宽高
canvas.style.width = cw + 'px';
canvas.style.height = ch + 'px';
ctx.scale(pixelRatio, pixelRatio);
}
反锯齿
CanvasRenderingContext2D
.imageSmoothingEnabled
是 Canvas 2D API 用来设置图片是否平滑的属性,true表示图片平滑(默认值),false表示图片不平滑。
反锯齿默认是启用的,我们可能想要关闭它以看到清楚的像素。你可以通过切换勾选框来看到imageSmoothingEnabled属性的效果
zoomctx.imageSmoothingEnabled = false;
zoomctx.mozImageSmoothingEnabled = false;
zoomctx.webkitImageSmoothingEnabled = false;
zoomctx.msImageSmoothingEnabled = false;
第一个图像以其自然大小绘制,第二个图像缩放为3倍并启用了图像平滑,而第三个图像缩放为3倍但禁用了图像平滑。
imageSmoothingQuality
使用imageSmoothingQuality
属性来调整平滑质量
ctx.imageSmoothingQuality = value
value = ctx.imageSmoothingQuality
//value
//"low","medium","high"
image-rendering
developer.mozilla.org/zh-CN/docs/…
CSS 属性 image-rendering
用于设置图像缩放算法。
/* 专有属性值 */
image-rendering: auto;
image-rendering: crisp-edges;
image-rendering: pixelated;
/* 全局属性值 */
image-rendering: inherit;
image-rendering: initial;
image-rendering: unset;
保存图片
-
将DOM转化成canvas对象
-
将canvas生成照片
性能
- 避免浮点数的坐标点,用整数取而代之
- 在离屏canvas中缓存图片的不同尺寸,而不要用
drawImage()
去缩放它们。
变形
translate(x, y)
rotate(angle)
scale(x, y)
如果比1小,会缩小图形, 如果比1大会放大图形
如果参数为负实数, 相当于以x 或 y轴作为对称轴镜像反转(例如, 使用translate(0,canvas.height); scale(1,-1);
以y轴作为对称轴镜像反转
transform(a, b, c, d, e, f)
a (m11) 水平方向的缩放 b(m12) 竖直方向的倾斜偏移 c(m21) 水平方向的倾斜偏移 d(m22) 竖直方向的缩放 e(dx) 水平方向的移动 f(dy) 竖直方向的移动
组合
绘制顺序:globalCompositeOperation,这个属性设定了在画新图形时采用的遮盖策略
save&&restore
- save() 保存画布(canvas)的所有状态
- restore() save 和 restore 方法是用来保存和恢复 canvas 状态的,都没有参数。Canvas 的状态就是当前画面应用的所有样式和变形的一个快照。
Canvas状态存储在栈中,每当save()
方法被调用后,当前的状态就被推送到栈中保存。每一次调用 restore
方法,上一个保存的状态就从栈中弹出,所有设定都恢复。
Konva
工作原理
Konva 的对象是以一颗树的形式保存的,Konva.Stage
是树的根节点,Stage
子节点是用户创建的图层 (Konva.Layer
)。
每一个 layer 有两个 <canvas>
渲染器: 场景渲染器 和 图像命中检测渲染器。场景渲染器输出你所看见的内容,图像命中渲染器在隐藏的 canvas 里用于高性能的检测事件。
图层可以包含图形、嵌套图形的组、嵌套组的组。Stage
(舞台),layers
(图层),groups
(组),和 shapes
(图形) 都是虚拟节点,类似于 HTML 的 DOM 节点。
节点结构图:
Stage
|
+------+------+
| |
Layer Layer
| |
+-----+-----+ Shape
| |
Group Group
| |
+ +---+---+
| | |
Shape Group Shape
|
+
|
Shape
示例
-
引入 Konva.js 文件
<script src="konva.js"></script>
-
然后页面中放置一个容器作为 Konva 处理的对象. Konva 会在该容器中添加 canvas 标签. 值得说明的是, 需要为这个标签添加 id 属性.
<div id="dv"></div>
-
然后编写 js 代码,Konva 是一个完全面向对象的库.
-
创建舞台
var stage = new Konva.Stage({ container: 'dv', width: window.innerWidth, height: window.innerHeight });
- 首先, 在 Konva 中所有的图形都是在 Konva 中的一个构造函数. Konva 是全局的命名空间.
- 创建舞台使用的是 Stage 构造函数,该函数需要提供参数.
- Konva 中所有图形的参数都是使用 json 对象的方式进行提供.
- 舞台需要设置容器的 id, 即 container 属性. 以及宽( width ), 高( height ).
-
舞台中可以放置一个到多个层( Layer ), 所有的图形应该放在在层中
- 首先创建层对象. 层对象不需要传递参数.
var layer = new Konva.Layer();
-
将层添加到舞台中. Konva 中凡是添加, 都是使用 add 方法.
stage.add( layer );
-
在层中放置一个矩形, 就创建一个 矩形对象
var rect = new Konva.Rect({ x: 100, y: 50, width: 200, height: 100, fill: 'red' });
将矩形添加到 层中
layer.add( rect );
-
最后绘图使用 draw 方法
layer.draw();
stage
layer 的概念类似于 ps 中的图层,或者 DOM 中的 z-index,当我们向 stage 中添加一个 layer 时,DOM 中会再加入一个 canvas 元素来对应这个 layer。
当我们往 layer 中添加了多个 shape 时,调用 layer.draw 时,layer 会按照形状添加的先后顺序依次进行绘制。后面添加的在上面,最先添加的在最下面。