使用canvas實現(xiàn)魔法攝像頭的示例代碼
背景
我們用手機的攝像頭自拍,很容易實現(xiàn)簡單的自拍效果,如復(fù)古、黑白等等。其實我們使用web端的JavaScript也是可以實現(xiàn)的。接下來就帶領(lǐng)小伙伴實現(xiàn)一個魔法攝像頭。并且提供了截圖下載功能。
魔鬼風格
復(fù)古風格
關(guān)鍵技術(shù)
- canvas 它可以用于動畫、游戲畫面、數(shù)據(jù)可視化、圖片編輯以及實時視頻處理等方面。
- video 用于在 HTML 或者 XHTML 文檔中嵌入媒體播放器
- navigator.mediaDevices.getUserMedia 用來將攝像頭視頻轉(zhuǎn)成文件流
- requestAnimationFrame 你希望執(zhí)行一個動畫,并且要求瀏覽器在下次重繪之前調(diào)用指定的回調(diào)函數(shù)更新動畫
主要業(yè)務(wù)流程
- 調(diào)用攝像頭加載畫面到video上
- 使用canvas將video視頻逐幀畫到canvas上
- 實現(xiàn)canvas濾鏡效果
- 點擊截圖
調(diào)用攝像頭加載畫面到video上
<!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上
<!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'); // 當視頻元素加載完成后執(zhí)行 video.addEventListener('loadedmetadata', function () { // 設(shè)置畫布大小與視頻尺寸相同 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>
實現(xiàn)canvas濾鏡效果
以下代碼沒有進行過多封裝,后續(xù)會出一篇使用面向?qū)ο蠛驮O(shè)計模式的續(xù)集來優(yōu)化代碼
本次案例實現(xiàn)的濾鏡效果主要有 反轉(zhuǎn) 黑白 亮度 復(fù)古 紅色 綠色 藍色 透明 馬賽克 漸變
在canvas中,可以通過 getImageData 獲取到當前畫布上所有的像素點,它以4個點為一組,表示畫布上當前坐標點的 R G B A (紅、綠、藍、透明度)。我們要實現(xiàn)的濾鏡效果,幾乎都是直接對該像素點進行操作。如 黑白效果 將每個像素的RGB值轉(zhuǎn)換為灰度值(R、G、B三個分量取平均值)
<!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 = "" // 當視頻元素加載完成后執(zhí)行 video.addEventListener('loadedmetadata', function () { // 設(shè)置畫布大小與視頻尺寸相同 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>
所有濾鏡效果總結(jié)如下
- 反轉(zhuǎn)效果:
- 原理:通過將每個像素的RGB值取反來實現(xiàn)反轉(zhuǎn)效果。
- 實現(xiàn)方式:使用
getImageData
獲取圖像數(shù)據(jù),然后遍歷每個像素,將每個像素的RGB值取反,再使用putImageData
將修改后的數(shù)據(jù)繪制回Canvas。
- 黑白效果:
- 原理:將每個像素的RGB值轉(zhuǎn)換為灰度值,使圖像變?yōu)楹诎住?/li>
- 實現(xiàn)方式:使用
getImageData
獲取圖像數(shù)據(jù),然后遍歷每個像素,將每個像素的RGB值轉(zhuǎn)換為灰度值(R、G、B三個分量取平均值),再使用putImageData
將修改后的數(shù)據(jù)繪制回Canvas。
- 亮度效果:
- 原理:調(diào)整每個像素的亮度值,使圖像變亮或變暗。
- 實現(xiàn)方式:使用
getImageData
獲取圖像數(shù)據(jù),然后遍歷每個像素,調(diào)整每個像素的亮度值,再使用putImageData
將修改后的數(shù)據(jù)繪制回Canvas。
- 復(fù)古效果:
- 原理:通過調(diào)整每個像素的色調(diào)、飽和度和亮度,使圖像呈現(xiàn)復(fù)古效果。
- 實現(xiàn)方式:使用
getImageData
獲取圖像數(shù)據(jù),然后遍歷每個像素,調(diào)整每個像素的色調(diào)、飽和度和亮度,再使用putImageData
將修改后的數(shù)據(jù)繪制回Canvas。
- 紅色、綠色、藍色效果:
- 原理:增加或減少每個像素的紅色、綠色、藍色分量的值,使圖像呈現(xiàn)相應(yīng)顏色的效果。
- 實現(xiàn)方式:使用
getImageData
獲取圖像數(shù)據(jù),然后遍歷每個像素,增加或減少每個像素的紅色、綠色、藍色分量的值,再使用putImageData
將修改后的數(shù)據(jù)繪制回Canvas。
- 透明效果:
- 原理:調(diào)整每個像素的透明度值,使圖像呈現(xiàn)透明效果。
- 實現(xiàn)方式:使用
getImageData
獲取圖像數(shù)據(jù),然后遍歷每個像素,調(diào)整每個像素的透明度值,再使用putImageData
將修改后的數(shù)據(jù)繪制回Canvas。
- 馬賽克效果:
- 原理:將圖像分割為小塊,每個小塊的像素值設(shè)置為該小塊內(nèi)像素的平均值,從而實現(xiàn)馬賽克效果。
- 實現(xiàn)方式:使用
getImageData
獲取圖像數(shù)據(jù),然后將圖像分割為小塊,計算每個小塊內(nèi)像素的平均值,再將該小塊內(nèi)所有像素的值設(shè)置為該平均值,最后使用putImageData
將修改后的數(shù)據(jù)繪制回Canvas。
- 馬賽克效果
- 由于實際操作過程中,上述馬賽克效果處理性能比較底下,這里用來一個取巧的效果來實現(xiàn)。就是先用canvas將畫面畫小,然后再將畫面縮放來實現(xiàn)一個模糊效果,間接實現(xiàn)馬賽克效果
- 漸變?yōu)V鏡效果:
- 原理:通過在圖像上應(yīng)用漸變效果,使圖像呈現(xiàn)漸變色的效果。
- 實現(xiàn)方式:使用
createLinearGradient
或createRadialGradient
創(chuàng)建漸變對象,然后使用漸變對象作為填充樣式,繪制圖像到Canvas上。
<!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">反轉(zhuǎn)</button> <button data-type="blackwhite">黑白</button> <button data-type="brightness">亮度</button> <button data-type="sepia">復(fù)古</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 = "" // 當視頻元素加載完成后執(zhí)行 video.addEventListener('loadedmetadata', function () { // 設(shè)置畫布大小與視頻尺寸相同 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") { // 反轉(zhuǎn) 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") { // 復(fù)古 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; // 不透明度 // 設(shè)置當前像素的顏色值 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
方法將內(nèi)容轉(zhuǎn)換為數(shù)據(jù) URL。 - 創(chuàng)建一個
<a>
元素,并將數(shù)據(jù) URL 賦值給其href
屬性。 - 設(shè)置
<a>
元素的download
屬性為要保存的文件名。 - 使用 JavaScript 模擬點擊
<a>
元素來觸發(fā)下載。
const takePhoto = document.querySelector("#takePhoto")// 截圖 按鈕 takePhoto.addEventListener('click', function (e) { // 繪制原始 Canvas 的內(nèi)容到新的 Canvas 上 ctx.drawImage(canvas, 0, 0, canvas.width, canvas.height); // 將內(nèi)容轉(zhuǎn)換為數(shù)據(jù) URL const dataURL = canvas.toDataURL(); // 創(chuàng)建一個 <a> 元素并設(shè)置屬性 const link = document.createElement('a'); link.href = dataURL; link.download = 'screenshot.png'; // 設(shè)置要保存的文件名 // 模擬點擊 <a> 元素來觸發(fā)下載 link.click(); })
完整代碼
<!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">反轉(zhuǎn)</button> <button data-type="blackwhite">黑白</button> <button data-type="brightness">亮度</button> <button data-type="sepia">復(fù)古</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 = "" // 當視頻元素加載完成后執(zhí)行 video.addEventListener('loadedmetadata', function () { // 設(shè)置畫布大小與視頻尺寸相同 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") { // 反轉(zhuǎn) 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") { // 復(fù)古 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; // 不透明度 // 設(shè)置當前像素的顏色值 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 的內(nèi)容到新的 Canvas 上 ctx.drawImage(canvas, 0, 0, canvas.width, canvas.height); // 將內(nèi)容轉(zhuǎn)換為數(shù)據(jù) URL const dataURL = canvas.toDataURL(); // 創(chuàng)建一個 <a> 元素并設(shè)置屬性 const link = document.createElement('a'); link.href = dataURL; link.download = 'screenshot.png'; // 設(shè)置要保存的文件名 // 模擬點擊 <a> 元素來觸發(fā)下載 link.click(); }) </script> </body> </html>
以上就是使用canvas實現(xiàn)魔法攝像頭的示例代碼的詳細內(nèi)容,更多關(guān)于canvas 魔法攝像頭的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
JavaScript計算值然后把值嵌入到html中的實現(xiàn)方法
下面小編就為大家?guī)硪黄狫avaScript計算值然后把值嵌入到html中的實現(xiàn)方法。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2016-10-10基于cornerstone.js的dicom醫(yī)學影像查看瀏覽功能
這篇文章主要介紹了基于cornerstone.js的dicom醫(yī)學影像查看瀏覽功能,本文通過實例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2022-07-07JavaScript使用ul中l(wèi)i標簽實現(xiàn)刪除效果
這篇文章主要為大家詳細介紹了JavaScript使用ul中l(wèi)i標簽實現(xiàn)刪除效果,具有一定的參考價值,感興趣的小伙伴們可以參考一下2019-04-04非jQuery實現(xiàn)照片散落桌子上,單擊放大的LightBox效果
本文給大家介紹一款js實現(xiàn)的照片散落桌子上點擊放大圖片的LightBox效果,非常的炫酷,而且是非jQuery實現(xiàn)的,有需要的小伙伴可以參考下2014-11-11Javascript延遲執(zhí)行實現(xiàn)方法(setTimeout)
延遲執(zhí)行,其實就是用到了setTimeout這個函數(shù)。善于利用這個函數(shù),可以減少很多ajax的請求,以及dom操作。2010-12-12