使用canvas实现魔法摄像头的示例代码
脚本之家 / 编程助手:解决程序员“几乎”所有问题!
脚本之家官方知识库 → 点击立即使用
背景
我们用手机的摄像头自拍,很容易实现简单的自拍效果,如复古、黑白等等。其实我们使用web端的JavaScript也是可以实现的。接下来就带领小伙伴实现一个魔法摄像头。并且提供了截图下载功能。
魔鬼风格
复古风格
关键技术
- canvas 它可以用于动画、游戏画面、数据可视化、图片编辑以及实时视频处理等方面。
- video 用于在 HTML 或者 XHTML 文档中嵌入媒体播放器
- navigator.mediaDevices.getUserMedia 用来将摄像头视频转成文件流
- requestAnimationFrame 你希望执行一个动画,并且要求浏览器在下次重绘之前调用指定的回调函数更新动画
主要业务流程
- 调用摄像头加载画面到video上
- 使用canvas将video视频逐帧画到canvas上
- 实现canvas滤镜效果
- 点击截图
调用摄像头加载画面到video上
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | <!DOCTYPE html> < html > < head > < title >Canvas Demo</ title > </ head > < body > < video id = "videoElement" autoplay></ video > < canvas id = "canvasElement" ></ canvas > < script > // 获取视频元素和画布元素 const video = document.getElementById('videoElement'); // 检查浏览器是否支持 getUserMedia API if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) { // 请求访问摄像头 navigator.mediaDevices.getUserMedia({ video: true }) .then(function (stream) { // 将视频流绑定到视频元素上 video.srcObject = stream; // 开始绘制视频画面到画布上 requestAnimationFrame(drawFrame); }) .catch(function (error) { console.error('无法访问摄像头:', error); }); } else { console.error('浏览器不支持 getUserMedia API'); } </ script > </ body > </ html > |
将video视频逐帧画到canvas上
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | <!DOCTYPE html> < html > < head > < title >Canvas Demo</ title > </ head > < body > < video id = "videoElement" autoplay></ video > < canvas id = "canvasElement" ></ canvas > < script > // 获取视频元素和画布元素 const video = document.getElementById('videoElement'); const canvas = document.getElementById('canvasElement'); const ctx = canvas.getContext('2d'); // 当视频元素加载完成后执行 video.addEventListener('loadedmetadata', function () { // 设置画布大小与视频尺寸相同 canvas.width = video.videoWidth; canvas.height = video.videoHeight; }); // 在每一帧绘制视频画面到画布上 一秒描绘60次 function drawFrame() { ctx.drawImage(video, 0, 0, canvas.width, canvas.height);// 将视频画在画布上 requestAnimationFrame(drawFrame); } // 检查浏览器是否支持 getUserMedia API if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) { // 请求访问摄像头 navigator.mediaDevices.getUserMedia({ video: true }) .then(function (stream) { // 将视频流绑定到视频元素上 video.srcObject = stream; // 开始绘制视频画面到画布上 requestAnimationFrame(drawFrame); }) .catch(function (error) { console.error('无法访问摄像头:', error); }); } else { console.error('浏览器不支持 getUserMedia API'); } </ script > </ body > </ html > |
实现canvas滤镜效果
以下代码没有进行过多封装,后续会出一篇使用面向对象和设计模式的续集来优化代码
本次案例实现的滤镜效果主要有 反转 黑白 亮度 复古 红色 绿色 蓝色 透明 马赛克 渐变
在canvas中,可以通过 getImageData 获取到当前画布上所有的像素点,它以4个点为一组,表示画布上当前坐标点的 R G B A (红、绿、蓝、透明度)。我们要实现的滤镜效果,几乎都是直接对该像素点进行操作。如 黑白效果 将每个像素的RGB值转换为灰度值(R、G、B三个分量取平均值)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 | <!DOCTYPE html> <html> <head> <title>Canvas Demo</title> </head> <body> <video id= "videoElement" autoplay></video> <canvas id= "canvasElement" ></canvas> <script> // 获取视频元素和画布元素 const video = document.getElementById( 'videoElement' ); const canvas = document.getElementById( 'canvasElement' ); const ctx = canvas.getContext( '2d' ); const buttons = document.querySelectorAll( "button[data-type]" ); const takePhoto = document.querySelector( "#takePhoto" ) let drawType = "" // 当视频元素加载完成后执行 video.addEventListener( 'loadedmetadata' , function () { // 设置画布大小与视频尺寸相同 canvas.width = video.videoWidth; canvas.height = video.videoHeight; }); // 在每一帧绘制视频画面到画布上 function drawFrame() { ctx.drawImage(video, 0, 0, canvas.width, canvas.height); let imageObj = ctx.getImageData(0, 0, canvas.width, canvas.height); // 黑白效果 for (let i = 0; i < imageObj.data.length; i += 4) { const average = (imageObj.data[i + 0] + imageObj.data[i + 1] + imageObj.data[i + 2] + imageObj.data[i + 3]) / 3; imageObj.data[i + 0] = average; //红 imageObj.data[i + 1] = average; //绿 imageObj.data[i + 2] = average; //蓝 } ctx.putImageData(imageObj, 0, 0) requestAnimationFrame(drawFrame); } // 检查浏览器是否支持 getUserMedia API if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) { // 请求访问摄像头 navigator.mediaDevices.getUserMedia({ video: true }) .then( function (stream) { // 将视频流绑定到视频元素上 video.srcObject = stream; // 开始绘制视频画面到画布上 requestAnimationFrame(drawFrame); }) . catch ( function (error) { console.error( '无法访问摄像头:' , error); }); } else { console.error( '浏览器不支持 getUserMedia API' ); } </script> </body> </html> |
所有滤镜效果总结如下
- 反转效果:
- 原理:通过将每个像素的RGB值取反来实现反转效果。
- 实现方式:使用
getImageData
获取图像数据,然后遍历每个像素,将每个像素的RGB值取反,再使用putImageData
将修改后的数据绘制回Canvas。
- 黑白效果:
- 原理:将每个像素的RGB值转换为灰度值,使图像变为黑白。
- 实现方式:使用
getImageData
获取图像数据,然后遍历每个像素,将每个像素的RGB值转换为灰度值(R、G、B三个分量取平均值),再使用putImageData
将修改后的数据绘制回Canvas。
- 亮度效果:
- 原理:调整每个像素的亮度值,使图像变亮或变暗。
- 实现方式:使用
getImageData
获取图像数据,然后遍历每个像素,调整每个像素的亮度值,再使用putImageData
将修改后的数据绘制回Canvas。
- 复古效果:
- 原理:通过调整每个像素的色调、饱和度和亮度,使图像呈现复古效果。
- 实现方式:使用
getImageData
获取图像数据,然后遍历每个像素,调整每个像素的色调、饱和度和亮度,再使用putImageData
将修改后的数据绘制回Canvas。
- 红色、绿色、蓝色效果:
- 原理:增加或减少每个像素的红色、绿色、蓝色分量的值,使图像呈现相应颜色的效果。
- 实现方式:使用
getImageData
获取图像数据,然后遍历每个像素,增加或减少每个像素的红色、绿色、蓝色分量的值,再使用putImageData
将修改后的数据绘制回Canvas。
- 透明效果:
- 原理:调整每个像素的透明度值,使图像呈现透明效果。
- 实现方式:使用
getImageData
获取图像数据,然后遍历每个像素,调整每个像素的透明度值,再使用putImageData
将修改后的数据绘制回Canvas。
- 马赛克效果:
- 原理:将图像分割为小块,每个小块的像素值设置为该小块内像素的平均值,从而实现马赛克效果。
- 实现方式:使用
getImageData
获取图像数据,然后将图像分割为小块,计算每个小块内像素的平均值,再将该小块内所有像素的值设置为该平均值,最后使用putImageData
将修改后的数据绘制回Canvas。
- 马赛克效果
- 由于实际操作过程中,上述马赛克效果处理性能比较底下,这里用来一个取巧的效果来实现。就是先用canvas将画面画小,然后再将画面缩放来实现一个模糊效果,间接实现马赛克效果
- 渐变滤镜效果:
- 原理:通过在图像上应用渐变效果,使图像呈现渐变色的效果。
- 实现方式:使用
createLinearGradient
或createRadialGradient
创建渐变对象,然后使用渐变对象作为填充样式,绘制图像到Canvas上。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 | <!DOCTYPE html> < html > < head > < title >Canvas Demo</ title > < style > button { border-radius: 10px; display: inline-flex; align-items: center; justify-content: center; cursor: pointer; overflow: hidden; user-select: none; outline: none; border: none; padding: 16px; background-color: #1d93ab; color: #fff; } button:focus { background-color: #e88f21 } </ style > </ head > < body > < div > < button data-type = "gray" >反转</ button > < button data-type = "blackwhite" >黑白</ button > < button data-type = "brightness" >亮度</ button > < button data-type = "sepia" >复古</ button > < button data-type = "redMask" >红色</ button > < button data-type = "greenMask" >绿色</ button > < button data-type = "blueMask" >蓝色</ button > < button data-type = "opacity" >透明</ button > < button data-type = "mosaic" >马赛克</ button > < button data-type = "linearGradient" >渐变</ button > </ div > < video id = "videoElement" autoplay></ video > < canvas id = "canvasElement" ></ canvas > < script > // 获取视频元素和画布元素 const video = document.getElementById('videoElement'); const canvas = document.getElementById('canvasElement'); const ctx = canvas.getContext('2d'); const buttons = document.querySelectorAll("button[data-type]"); let drawType = "" // 当视频元素加载完成后执行 video.addEventListener('loadedmetadata', function () { // 设置画布大小与视频尺寸相同 canvas.width = video.videoWidth; canvas.height = video.videoHeight; }); // 在每一帧绘制视频画面到画布上 function drawFrame() { ctx.drawImage(video, 0, 0, canvas.width, canvas.height); let imageObj = ctx.getImageData(0, 0, canvas.width, canvas.height); if (drawType === "gray") { // 反转 for (let i = 0; i < imageObj.data.length ; i += 4) { imageObj.data[i + 0] = 255 - imageObj.data[i + 0]; imageObj.data[i + 1] = 255 - imageObj.data[i + 1]; imageObj.data[i + 2] = 255 - imageObj.data[i + 2]; } ctx.putImageData(imageObj, 0, 0) } if (drawType === "blackwhite") { // 黑白 for (let i = 0 ; i < imageObj.data.length; i += 4) { const average = (imageObj.data[i + 0] + imageObj.data[i + 1] + imageObj.data[i + 2] + imageObj.data[i + 3]) / 3; imageObj.data[i + 0] = average;//红 imageObj.data[i + 1] = average; //绿 imageObj.data[i + 2] = average; //蓝 } ctx.putImageData(imageObj, 0, 0) } if (drawType === "brightness") { // 亮度 for (let i = 0 ; i < imageObj.data.length; i += 4) { const a = 50 ; imageObj.data[i + 0] += a; imageObj.data[i + 1] += a; imageObj.data[i + 2] += a; } ctx.putImageData(imageObj, 0, 0) } if (drawType === "sepia") { // 复古 for (let i = 0 ; i < imageObj.data.length; i += 4) { const r = imageObj .data[i + 0]; const g = imageObj .data[i + 1]; const b = imageObj .data[i + 2]; imageObj.data[i + 0] = r * 0.39 + g * 0.76 + b * 0.18; imageObj.data[i + 1] = r * 0.35 + g * 0.68 + b * 0.16; imageObj.data[i + 2] = r * 0.27 + g * 0.53 + b * 0.13; } ctx.putImageData(imageObj, 0, 0) } if (drawType === "redMask") { // 红色 for (let i = 0 ; i < imageObj.data.length; i += 4) { const r = imageObj .data[i + 0] const g = imageObj .data[i + 1] const b = imageObj .data[i + 2] const average = (r + g + b) / 3 imageObj.data[i + 0] = average imageObj.data[i + 1] = 0 imageObj.data[i + 2] = 0 } ctx.putImageData(imageObj, 0, 0) } if (drawType === "greenMask") { // 绿色 for (let i = 0 ; i < imageObj.data.length; i += 4) { const r = imageObj .data[i + 0] const g = imageObj .data[i + 1] const b = imageObj .data[i + 2] const average = (r + g + b) / 3 imageObj.data[i + 0] = 0 imageObj.data[i + 1] = average imageObj.data[i + 2] = 0 } ctx.putImageData(imageObj, 0, 0) } if (drawType === "blueMask") { // 蓝色 for (let i = 0 ; i < imageObj.data.length; i += 4) { const r = imageObj .data[i + 0] const g = imageObj .data[i + 1] const b = imageObj .data[i + 2] const average = (r + g + b) / 3 imageObj.data[i + 0] = 0 imageObj.data[i + 1] = 0 imageObj.data[i + 2] = average } ctx.putImageData(imageObj, 0, 0) } if (drawType === "opacity") { // 透明 for (let i = 0 ; i < imageObj.data.length; i += 4) { imageObj.data[i + 3] = imageObj.data[i + 3] * 0.3; } ctx.putImageData(imageObj, 0, 0) } if (drawType === "linearGradient") { // 渐变 const data = imageObj .data; // 遍历每个像素 for (let i = 0 ; i < data.length; i += 4) { const x = (i / 4) % canvas.width; // 当前像素的 x 坐标 const y = Math .floor(i / (4 * canvas.width)); // 当前像素的 y 坐标 // 计算当前像素的颜色值 const r = x / canvas.width * 255; // 红色分量 const g = y / canvas.height * 255; // 绿色分量 const b = 128 ; // 蓝色分量 const a = 100 ; // 不透明度 // 设置当前像素的颜色值 data[i] = r; // 红色分量 data[i + 1] = g; // 绿色分量 data[i + 2] = b; // 蓝色分量 data[i + 3] = a; // 不透明度 } ctx.putImageData(imageObj, 0, 0) } if (drawType === "mosaic") { // 马赛克 ctx.imageSmoothingEnabled = false ; // 禁用图像平滑处理 const tileSize = 10 ; // 马赛克块的大小 // 缩小马赛克块 ctx.drawImage(canvas, 0, 0, canvas.width, canvas.height, 0, 0, canvas.width / tileSize, canvas.height / tileSize); // 放大回原来的大小 ctx.drawImage(canvas, 0, 0, canvas.width / tileSize, canvas.height / tileSize, 0, 0, canvas.width, canvas.height); } requestAnimationFrame(drawFrame); // setTimeout(drawFrame, 1000); } // 检查浏览器是否支持 getUserMedia API if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) { // 请求访问摄像头 navigator.mediaDevices.getUserMedia({ video: true }) .then(function (stream) { // 将视频流绑定到视频元素上 video.srcObject = stream ; // 开始绘制视频画面到画布上 requestAnimationFrame(drawFrame); }) .catch(function (error) { console.error('无法访问摄像头:', error); }); } else { console.error('浏览器不支持 getUserMedia API'); } buttons.forEach(button => { button.addEventListener("click", function (e) { drawType = e.target.dataset.type; }) }) </ script > </ body > </ html > |
点击截图
流程
- 将Canvas 的
toDataURL
方法将内容转换为数据 URL。 - 创建一个
<a>
元素,并将数据 URL 赋值给其href
属性。 - 设置
<a>
元素的download
属性为要保存的文件名。 - 使用 JavaScript 模拟点击
<a>
元素来触发下载。
1 2 3 4 5 6 7 8 9 10 11 12 13 | const takePhoto = document.querySelector( "#takePhoto" ) // 截图 按钮 takePhoto.addEventListener( 'click' , function (e) { // 绘制原始 Canvas 的内容到新的 Canvas 上 ctx.drawImage(canvas, 0, 0, canvas.width, canvas.height); // 将内容转换为数据 URL const dataURL = canvas.toDataURL(); // 创建一个 <a> 元素并设置属性 const link = document.createElement( 'a' ); link.href = dataURL; link.download = 'screenshot.png' ; // 设置要保存的文件名 // 模拟点击 <a> 元素来触发下载 link.click(); }) |
完整代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 | <!DOCTYPE html> < html > < head > < title >Canvas Demo</ title > < style > button { border-radius: 10px; display: inline-flex; align-items: center; justify-content: center; cursor: pointer; overflow: hidden; user-select: none; outline: none; border: none; padding: 16px; background-color: #1d93ab; color: #fff; } button:focus { background-color: #e88f21 } </ style > </ head > < body > < div > < button data-type = "gray" >反转</ button > < button data-type = "blackwhite" >黑白</ button > < button data-type = "brightness" >亮度</ button > < button data-type = "sepia" >复古</ button > < button data-type = "redMask" >红色</ button > < button data-type = "greenMask" >绿色</ button > < button data-type = "blueMask" >蓝色</ button > < button data-type = "opacity" >透明</ button > < button data-type = "mosaic" >马赛克</ button > < button data-type = "linearGradient" >渐变</ button > < button id = "takePhoto" >拍摄</ button > </ div > < video id = "videoElement" autoplay></ video > < canvas id = "canvasElement" ></ canvas > < script > // 获取视频元素和画布元素 const video = document.getElementById('videoElement'); const canvas = document.getElementById('canvasElement'); const ctx = canvas.getContext('2d'); const buttons = document.querySelectorAll("button[data-type]"); const takePhoto = document.querySelector("#takePhoto")// 截图 按钮 let drawType = "" // 当视频元素加载完成后执行 video.addEventListener('loadedmetadata', function () { // 设置画布大小与视频尺寸相同 canvas.width = video.videoWidth; canvas.height = video.videoHeight; }); // 在每一帧绘制视频画面到画布上 function drawFrame() { ctx.drawImage(video, 0, 0, canvas.width, canvas.height); let imageObj = ctx.getImageData(0, 0, canvas.width, canvas.height); if (drawType === "gray") { // 反转 for (let i = 0; i < imageObj.data.length ; i += 4) { imageObj.data[i + 0] = 255 - imageObj.data[i + 0]; imageObj.data[i + 1] = 255 - imageObj.data[i + 1]; imageObj.data[i + 2] = 255 - imageObj.data[i + 2]; } ctx.putImageData(imageObj, 0, 0) } if (drawType === "blackwhite") { // 黑白 for (let i = 0 ; i < imageObj.data.length; i += 4) { const average = (imageObj.data[i + 0] + imageObj.data[i + 1] + imageObj.data[i + 2] + imageObj.data[i + 3]) / 3; imageObj.data[i + 0] = average;//红 imageObj.data[i + 1] = average; //绿 imageObj.data[i + 2] = average; //蓝 } ctx.putImageData(imageObj, 0, 0) } if (drawType === "brightness") { // 亮度 for (let i = 0 ; i < imageObj.data.length; i += 4) { const a = 50 ; imageObj.data[i + 0] += a; imageObj.data[i + 1] += a; imageObj.data[i + 2] += a; } ctx.putImageData(imageObj, 0, 0) } if (drawType === "sepia") { // 复古 for (let i = 0 ; i < imageObj.data.length; i += 4) { const r = imageObj .data[i + 0]; const g = imageObj .data[i + 1]; const b = imageObj .data[i + 2]; imageObj.data[i + 0] = r * 0.39 + g * 0.76 + b * 0.18; imageObj.data[i + 1] = r * 0.35 + g * 0.68 + b * 0.16; imageObj.data[i + 2] = r * 0.27 + g * 0.53 + b * 0.13; } ctx.putImageData(imageObj, 0, 0) } if (drawType === "redMask") { // 红色 for (let i = 0 ; i < imageObj.data.length; i += 4) { const r = imageObj .data[i + 0] const g = imageObj .data[i + 1] const b = imageObj .data[i + 2] const average = (r + g + b) / 3 imageObj.data[i + 0] = average imageObj.data[i + 1] = 0 imageObj.data[i + 2] = 0 } ctx.putImageData(imageObj, 0, 0) } if (drawType === "greenMask") { // 绿色 for (let i = 0 ; i < imageObj.data.length; i += 4) { const r = imageObj .data[i + 0] const g = imageObj .data[i + 1] const b = imageObj .data[i + 2] const average = (r + g + b) / 3 imageObj.data[i + 0] = 0 imageObj.data[i + 1] = average imageObj.data[i + 2] = 0 } ctx.putImageData(imageObj, 0, 0) } if (drawType === "blueMask") { // 蓝色 for (let i = 0 ; i < imageObj.data.length; i += 4) { const r = imageObj .data[i + 0] const g = imageObj .data[i + 1] const b = imageObj .data[i + 2] const average = (r + g + b) / 3 imageObj.data[i + 0] = 0 imageObj.data[i + 1] = 0 imageObj.data[i + 2] = average } ctx.putImageData(imageObj, 0, 0) } if (drawType === "opacity") { // 透明 for (let i = 0 ; i < imageObj.data.length; i += 4) { imageObj.data[i + 3] = imageObj.data[i + 3] * 0.3; } ctx.putImageData(imageObj, 0, 0) } if (drawType === "linearGradient") { // 渐变 const data = imageObj .data; // 遍历每个像素 for (let i = 0 ; i < data.length; i += 4) { const x = (i / 4) % canvas.width; // 当前像素的 x 坐标 const y = Math .floor(i / (4 * canvas.width)); // 当前像素的 y 坐标 // 计算当前像素的颜色值 const r = x / canvas.width * 255; // 红色分量 const g = y / canvas.height * 255; // 绿色分量 const b = 128 ; // 蓝色分量 const a = 100 ; // 不透明度 // 设置当前像素的颜色值 data[i] = r; // 红色分量 data[i + 1] = g; // 绿色分量 data[i + 2] = b; // 蓝色分量 data[i + 3] = a; // 不透明度 } ctx.putImageData(imageObj, 0, 0) } if (drawType === "mosaic") { // 马赛克 ctx.imageSmoothingEnabled = false ; // 禁用图像平滑处理 const tileSize = 10 ; // 马赛克块的大小 // 缩小马赛克块 ctx.drawImage(canvas, 0, 0, canvas.width, canvas.height, 0, 0, canvas.width / tileSize, canvas.height / tileSize); // 放大回原来的大小 ctx.drawImage(canvas, 0, 0, canvas.width / tileSize, canvas.height / tileSize, 0, 0, canvas.width, canvas.height); } requestAnimationFrame(drawFrame); // setTimeout(drawFrame, 1000); } // 检查浏览器是否支持 getUserMedia API if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) { // 请求访问摄像头 navigator.mediaDevices.getUserMedia({ video: true }) .then(function (stream) { // 将视频流绑定到视频元素上 video.srcObject = stream ; // 开始绘制视频画面到画布上 requestAnimationFrame(drawFrame); }) .catch(function (error) { console.error('无法访问摄像头:', error); }); } else { console.error('浏览器不支持 getUserMedia API'); } buttons.forEach(button => { button.addEventListener("click", function (e) { drawType = e.target.dataset.type; }) }) takePhoto.addEventListener('click', function (e) { // 绘制原始 Canvas 的内容到新的 Canvas 上 ctx.drawImage(canvas, 0, 0, canvas.width, canvas.height); // 将内容转换为数据 URL const dataURL = canvas.toDataURL(); // 创建一个 < a > 元素并设置属性 const link = document.createElement('a'); link.href = dataURL; link.download = 'screenshot.png'; // 设置要保存的文件名 // 模拟点击 < a > 元素来触发下载 link.click(); }) </ script > </ body > </ html > |
以上就是使用canvas实现魔法摄像头的示例代码的详细内容,更多关于canvas 魔法摄像头的资料请关注脚本之家其它相关文章!
微信公众号搜索 “ 脚本之家 ” ,选择关注
程序猿的那些事、送书等活动等着你
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若内容造成侵权/违法违规/事实不符,请将相关资料发送至 reterry123@163.com 进行投诉反馈,一经查实,立即处理!
相关文章
浅谈js中的attributes和Attribute的用法与区别
这篇文章主要介绍了浅谈js中的attributes和Attribute的用法与区别,attributes可以获取一个对象中的一个属性,attributes 属性返回指定节点属性的集合,文中通过示例代码介绍的非常详细,需要的朋友们下面随着小编来一起学习学习吧2020-07-07JavaScript中的document.querySelector()方法使用详解
HTML的DOM querySelector()方法可以不需要额外的jQuery等支持,也可以方便的获取DOM元素,语法跟jQuery类似,这篇文章主要给大家介绍了关于JavaScript中document.querySelector()方法使用的相关资料,需要的朋友可以参考下2024-06-06详解BootStrap中Affix控件的使用及保持布局的美观的方法
Affix是BootStrap中的一个很有用的控件,他能够监视浏览器的滚动条的位置并让你的导航始终都在页面的可视区域。本文重点给大家介绍BootStrap中Affix控件的使用及保持布局的美观的方法,感兴趣的朋友一起看看吧2016-07-07
最新评论