简言
学习canvas如何绘制图片或视频。
绘制图像
给定一个图像,一般使用drawImage()方法绘制。
drawImage 绘制图像
Canvas 2D API 中的 CanvasRenderingContext2D.drawImage() 方法提供了多种在画布(Canvas)上绘制图像的方式。
语法:
指定绘制位置快速绘制:
drawImage(image, dx, dy);
指定绘制位置宽高快速绘制:
drawImage(image, dx, dy, dWidth, dHeight);
指定图像被绘制区域和绘制区域绘制:
drawImage(image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight);
使用方法有三种
参数:
-
image
绘制到上下文的元素。允许任何的画布图像源,例如:HTMLImageElement、SVGImageElement (en-US)、HTMLVideoElement、HTMLCanvasElement、ImageBitmap、OffscreenCanvas 或 VideoFrame (en-US)。 -
sx 可选
需要绘制到目标上下文中的,image 的矩形(裁剪)选择框的左上角 X 轴坐标。可以使用 3 参数或 5 参数语法来省略这个参数。 -
sy 可选
需要绘制到目标上下文中的,image 的矩形(裁剪)选择框的左上角 Y 轴坐标。可以使用 3 参数或 5 参数语法来省略这个参数。 -
sWidth 可选
需要绘制到目标上下文中的,image 的矩形(裁剪)选择框的宽度。如果不说明,整个矩形(裁剪)从坐标的 sx 和 sy 开始,到 image 的右下角结束。可以使用 3 参数或 5 参数语法来省略这个参数。使用负值将翻转这个图像。 -
sHeight 可选
需要绘制到目标上下文中的,image的矩形(裁剪)选择框的高度。使用负值将翻转这个图像。 -
dx
image 的左上角在目标画布上 X 轴坐标。 -
dy
image 的左上角在目标画布上 Y 轴坐标。 -
dWidth
image 在目标画布上绘制的宽度。允许对绘制的 image 进行缩放。如果不说明,在绘制时 image 宽度不会缩放。注意,这个参数不包含在 3 参数语法中。 -
dHeight
image 在目标画布上绘制的高度。允许对绘制的 image 进行缩放。如果不说明,在绘制时 image 高度不会缩放。注意,这个参数不包含在 3 参数语法中。
注意
- 当 drawImage() 需要在 HTMLVideoElement 工作时,仅当 HTMLMediaElement.readyState 大于 1 时 drawImage() 才能正常工作。
- 在绘制,裁剪和/或缩放时,drawImage() 将始终使用源元素的固有尺寸(以 CSS 像素为单位)。
- 在某些旧版本浏览器中,drawImage() 将忽略图像中的所有 EXIF 元数据,包括方向。此行为在 iOS 设备上尤其麻烦。你应该自己检测方向并使用 rotate() 使其正确。
示例
const canvas = document.getElementById("canvas");
const ctx = canvas.getContext("2d");
const image = document.getElementById("source");image.addEventListener("load", (e) => {ctx.drawImage(image, 33, 71, 104, 124, 21, 20, 87, 104);
});
ImageData 绘制图像
获得一个ImageData 对象后,使用putImageData()方法绘制。
可以通过CanvasRenderingContext2D.getImageData()获得一个ImageData 对象,用来描述 canvas 区域隐含的像素数据。
语法:
ImageData ctx.getImageData(sx, sy, sw, sh);
参数:
-
sx
将要被提取的图像数据矩形区域的左上角 x 坐标。 -
sy
将要被提取的图像数据矩形区域的左上角 y 坐标。 -
sw
将要被提取的图像数据矩形区域的宽度。 -
sh
将要被提取的图像数据矩形区域的高度。
getImageData()
CanvasRenderingContext2D.getImageData() 返回一个ImageData对象,用来描述 canvas 区域隐含的像素数据,这个区域通过矩形表示,起始点为*(sx, sy)、宽为sw、高为sh。*
putImageData()
CanvasRenderingContext2D.putImageData() 是 Canvas 2D API 将数据从已有的 ImageData 对象绘制到位图的方法。如果提供了一个绘制过的矩形,则只绘制该矩形的像素。此方法不受画布转换矩阵的影响。
语法:
void ctx.putImageData(imagedata, dx, dy);
void ctx.putImageData(imagedata, dx, dy, dirtyX, dirtyY, dirtyWidth, dirtyHeight);
参数:
-
imageData
ImageData,包含像素值的数组对象。 -
dx
源图像数据在目标画布中的位置偏移量(x 轴方向的偏移量)。 -
dy
源图像数据在目标画布中的位置偏移量(y 轴方向的偏移量)。 -
dirtyX 可选
在源图像数据中,矩形区域左上角的位置。默认是整个图像数据的左上角(x 坐标)。 -
dirtyY 可选
在源图像数据中,矩形区域左上角的位置。默认是整个图像数据的左上角(y 坐标)。 -
dirtyWidth 可选
在源图像数据中,矩形区域的宽度。默认是图像数据的宽度。 -
dirtyHeight 可选
在源图像数据中,矩形区域的高度。默认是图像数据的高度。
示例
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>图像剪辑</title><style>html,body {width: 100vw;height: 100%;margin: 0;padding: 0;}.box {box-sizing: border-box;width: 100%;height: 100%;padding: 24px;}.box-content {margin-top: 16px;box-sizing: border-box;width: 100%;/* height: 500px; *//* display: flex; */align-items: center;text-align: center;border: 1px solid #000;}#origin {width: 500px;height: 500px;border: 1px solid #000;}</style>
</head><body><div class="box"><div><h1>图像查看</h1><input type="file" name="file" id="file"></div><div class="box-content"><canvas id="origin" width="500" height="500"></canvas></div><div></div></div><script>const file = document.getElementById('file');const origin = document.getElementById('origin');const ctx = origin.getContext('2d');const img = document.createElement('img');// 辅助线let fx = 0let fy = 0let showf = false// 图片缩放let scale = 1 // 图片缩放比例let dx = 0 // 图片偏移 xlet dy = 0// 图片偏移 ylet dw = origin.widthlet dh = origin.heightlet modify = falseorigin.addEventListener("mousedown", (e) => {modify = trueshowf = truelastX = e.offsetXlastY = e.offsetY});origin.addEventListener("mousemove", (e) => {if (!ctx) returnlet x = e.offsetXlet y = e.offsetYif (modify) {fx = xfy = ydx += (x - lastX)dy += (y - lastY)lastX = xlastY = ydrawOperatingLine()}})origin.addEventListener('mouseup', (e) => {if (modify) {modify = falseshowf = falsedrawOperatingLine()}})origin.addEventListener("contextmenu", (e) => {e.preventDefault()if (img.width > img.height) {scale = origin.width / img.width} else {scale = origin.height / img.height}dx = (origin.width - scale * img.width) / (origin.width / e.offsetX)dy = (origin.height - scale * img.height) / (origin.height / e.offsetY)drawOperatingLine()})origin.addEventListener('wheel', (e) => {const { deltaY } = eif (deltaY > 0) { // 下滑放大scale += 0.1} else if (scale > 0.1) { // 上滑缩小scale -= 0.1}dx = (origin.width - scale * img.width) / (origin.width / e.offsetX)dy = (origin.height - scale * img.height) / (origin.height / e.offsetY)drawOperatingLine()})file.addEventListener('change', (e) => {const file = e.target.files[0];const reader = new FileReader();reader.readAsDataURL(file);reader.onload = (e) => {img.src = e.target.result;img.onload = () => {// 更新图片缩放比例if (img.width > img.height) {scale = origin.width / img.width} else {scale = origin.height / img.height}drawOperatingLine(true)}}})// 绘制裁剪控制图像function drawOperatingLine(init = false) {ctx.clearRect(0, 0, origin.width, origin.height)// 绘制图像dx = init ? (origin.width - scale * img.width) / 2 : dx // 图片偏移 xdy = init ? (origin.height - scale * img.height) / 2 : dy // 图片偏移 ydw = scale * img.widthdh = scale * img.heightctx.drawImage(img, 0, 0, img.width, img.height, dx, dy, dw, dh);// 辅助线if (showf) {ctx.save()ctx.beginPath();ctx.strokeStyle = 'red'ctx.setLineDash([3, 3])ctx.moveTo(0, fy);ctx.lineTo(origin.width, fy);ctx.moveTo(fx, 0);ctx.lineTo(fx, origin.height);ctx.stroke();ctx.restore()}}</script>
</body></html>
结语
结束了。