欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

使用canvas實(shí)現(xiàn)魔法攝像頭的示例代碼

 更新時(shí)間:2023年08月07日 08:48:33   作者:萬(wàn)少  
我們用手機(jī)的攝像頭自拍,很容易實(shí)現(xiàn)簡(jiǎn)單的自拍效果,如復(fù)古、黑白等等,其實(shí)我們使用web端的JavaScript也是可以實(shí)現(xiàn)的,接下來(lái)就帶領(lǐng)小伙伴實(shí)現(xiàn)一個(gè)魔法攝像頭,并且提供了截圖下載功能,需要的朋友可以參考下

背景

我們用手機(jī)的攝像頭自拍,很容易實(shí)現(xiàn)簡(jiǎn)單的自拍效果,如復(fù)古、黑白等等。其實(shí)我們使用web端的JavaScript也是可以實(shí)現(xiàn)的。接下來(lái)就帶領(lǐng)小伙伴實(shí)現(xiàn)一個(gè)魔法攝像頭。并且提供了截圖下載功能。

魔鬼風(fēng)格

image-20230806210316856

復(fù)古風(fēng)格

image-20230806211755401

關(guān)鍵技術(shù)

  1. canvas 它可以用于動(dòng)畫、游戲畫面、數(shù)據(jù)可視化、圖片編輯以及實(shí)時(shí)視頻處理等方面。
  2. video 用于在 HTML 或者 XHTML 文檔中嵌入媒體播放器
  3. navigator.mediaDevices.getUserMedia 用來(lái)將攝像頭視頻轉(zhuǎn)成文件流
  4. requestAnimationFrame 你希望執(zhí)行一個(gè)動(dòng)畫,并且要求瀏覽器在下次重繪之前調(diào)用指定的回調(diào)函數(shù)更新動(dòng)畫

主要業(yè)務(wù)流程

  1. 調(diào)用攝像頭加載畫面到video上
  2. 使用canvas將video視頻逐幀畫到canvas上
  3. 實(shí)現(xiàn)canvas濾鏡效果
  4. 點(diǎn)擊截圖

調(diào)用攝像頭加載畫面到video上

image-20230806213348829

image-20230806213703274

<!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) {
      // 請(qǐng)求訪問(wèn)攝像頭
      navigator.mediaDevices.getUserMedia({ video: true })
        .then(function (stream) {
          // 將視頻流綁定到視頻元素上
          video.srcObject = stream;
          // 開始繪制視頻畫面到畫布上
          requestAnimationFrame(drawFrame);
        })
        .catch(function (error) {
          console.error('無(wú)法訪問(wèn)攝像頭:', error);
        });
    } else {
      console.error('瀏覽器不支持 getUserMedia API');
    }
  </script>
</body>
</html>

將video視頻逐幀畫到canvas上

image-20230806220903756

image-20230806214258903

<!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');
    // 當(dāng)視頻元素加載完成后執(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) {
      // 請(qǐng)求訪問(wèn)攝像頭
      navigator.mediaDevices.getUserMedia({ video: true })
        .then(function (stream) {
          // 將視頻流綁定到視頻元素上
          video.srcObject = stream;
          // 開始繪制視頻畫面到畫布上
          requestAnimationFrame(drawFrame);
        })
        .catch(function (error) {
          console.error('無(wú)法訪問(wèn)攝像頭:', error);
        });
    } else {
      console.error('瀏覽器不支持 getUserMedia API');
    }
  </script>
</body>
</html>

實(shí)現(xiàn)canvas濾鏡效果

以下代碼沒(méi)有進(jìn)行過(guò)多封裝,后續(xù)會(huì)出一篇使用面向?qū)ο蠛驮O(shè)計(jì)模式的續(xù)集來(lái)優(yōu)化代碼

本次案例實(shí)現(xiàn)的濾鏡效果主要有 反轉(zhuǎn) 黑白 亮度 復(fù)古 紅色 綠色 藍(lán)色 透明 馬賽克 漸變

在canvas中,可以通過(guò) getImageData 獲取到當(dāng)前畫布上所有的像素點(diǎn),它以4個(gè)點(diǎn)為一組,表示畫布上當(dāng)前坐標(biāo)點(diǎn)的 R G B A (紅、綠、藍(lán)、透明度)。我們要實(shí)現(xiàn)的濾鏡效果,幾乎都是直接對(duì)該像素點(diǎn)進(jìn)行操作。如 黑白效果 將每個(gè)像素的RGB值轉(zhuǎn)換為灰度值(R、G、B三個(gè)分量取平均值)

image-20230806215151375

<!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 = ""
    // 當(dāng)視頻元素加載完成后執(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; //藍(lán)
      }
      ctx.putImageData(imageObj, 0, 0)
      requestAnimationFrame(drawFrame);
    }
    // 檢查瀏覽器是否支持 getUserMedia API
    if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
      // 請(qǐng)求訪問(wèn)攝像頭
      navigator.mediaDevices.getUserMedia({ video: true })
        .then(function (stream) {
          // 將視頻流綁定到視頻元素上
          video.srcObject = stream;
          // 開始繪制視頻畫面到畫布上
          requestAnimationFrame(drawFrame);
        })
        .catch(function (error) {
          console.error('無(wú)法訪問(wèn)攝像頭:', error);
        });
    } else {
      console.error('瀏覽器不支持 getUserMedia API');
    }
  </script>
</body>
</html>

所有濾鏡效果總結(jié)如下

  1. 反轉(zhuǎn)效果:
    • 原理:通過(guò)將每個(gè)像素的RGB值取反來(lái)實(shí)現(xiàn)反轉(zhuǎn)效果。
    • 實(shí)現(xiàn)方式:使用 getImageData 獲取圖像數(shù)據(jù),然后遍歷每個(gè)像素,將每個(gè)像素的RGB值取反,再使用 putImageData 將修改后的數(shù)據(jù)繪制回Canvas。
  2. 黑白效果:
    • 原理:將每個(gè)像素的RGB值轉(zhuǎn)換為灰度值,使圖像變?yōu)楹诎住?/li>
    • 實(shí)現(xiàn)方式:使用 getImageData 獲取圖像數(shù)據(jù),然后遍歷每個(gè)像素,將每個(gè)像素的RGB值轉(zhuǎn)換為灰度值(R、G、B三個(gè)分量取平均值),再使用 putImageData 將修改后的數(shù)據(jù)繪制回Canvas。
  3. 亮度效果:
    • 原理:調(diào)整每個(gè)像素的亮度值,使圖像變亮或變暗。
    • 實(shí)現(xiàn)方式:使用 getImageData 獲取圖像數(shù)據(jù),然后遍歷每個(gè)像素,調(diào)整每個(gè)像素的亮度值,再使用 putImageData 將修改后的數(shù)據(jù)繪制回Canvas。
  4. 復(fù)古效果:
    • 原理:通過(guò)調(diào)整每個(gè)像素的色調(diào)、飽和度和亮度,使圖像呈現(xiàn)復(fù)古效果。
    • 實(shí)現(xiàn)方式:使用 getImageData 獲取圖像數(shù)據(jù),然后遍歷每個(gè)像素,調(diào)整每個(gè)像素的色調(diào)、飽和度和亮度,再使用 putImageData 將修改后的數(shù)據(jù)繪制回Canvas。
  5. 紅色、綠色、藍(lán)色效果:
    • 原理:增加或減少每個(gè)像素的紅色、綠色、藍(lán)色分量的值,使圖像呈現(xiàn)相應(yīng)顏色的效果。
    • 實(shí)現(xiàn)方式:使用 getImageData 獲取圖像數(shù)據(jù),然后遍歷每個(gè)像素,增加或減少每個(gè)像素的紅色、綠色、藍(lán)色分量的值,再使用 putImageData 將修改后的數(shù)據(jù)繪制回Canvas。
  6. 透明效果:
    • 原理:調(diào)整每個(gè)像素的透明度值,使圖像呈現(xiàn)透明效果。
    • 實(shí)現(xiàn)方式:使用 getImageData 獲取圖像數(shù)據(jù),然后遍歷每個(gè)像素,調(diào)整每個(gè)像素的透明度值,再使用 putImageData 將修改后的數(shù)據(jù)繪制回Canvas。
  7. 馬賽克效果:
    • 原理:將圖像分割為小塊,每個(gè)小塊的像素值設(shè)置為該小塊內(nèi)像素的平均值,從而實(shí)現(xiàn)馬賽克效果。
    • 實(shí)現(xiàn)方式:使用 getImageData 獲取圖像數(shù)據(jù),然后將圖像分割為小塊,計(jì)算每個(gè)小塊內(nèi)像素的平均值,再將該小塊內(nèi)所有像素的值設(shè)置為該平均值,最后使用 putImageData 將修改后的數(shù)據(jù)繪制回Canvas。
  8. 馬賽克效果
    • 由于實(shí)際操作過(guò)程中,上述馬賽克效果處理性能比較底下,這里用來(lái)一個(gè)取巧的效果來(lái)實(shí)現(xiàn)。就是先用canvas將畫面畫小,然后再將畫面縮放來(lái)實(shí)現(xiàn)一個(gè)模糊效果,間接實(shí)現(xiàn)馬賽克效果
  9. 漸變?yōu)V鏡效果:
    • 原理:通過(guò)在圖像上應(yīng)用漸變效果,使圖像呈現(xiàn)漸變色的效果。
    • 實(shí)現(xiàn)方式:使用 createLinearGradient createRadialGradient 創(chuàng)建漸變對(duì)象,然后使用漸變對(duì)象作為填充樣式,繪制圖像到Canvas上。

2023-8-62202.gif

<!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">藍(lán)色</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 = ""
    // 當(dāng)視頻元素加載完成后執(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; //藍(lán)
        }
        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") {
        // 藍(lán)色
        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;
        // 遍歷每個(gè)像素
        for (let i = 0; i < data.length; i += 4) {
          const x = (i / 4) % canvas.width; // 當(dāng)前像素的 x 坐標(biāo)
          const y = Math.floor(i / (4 * canvas.width)); // 當(dāng)前像素的 y 坐標(biāo)
          // 計(jì)算當(dāng)前像素的顏色值
          const r = x / canvas.width * 255; // 紅色分量
          const g = y / canvas.height * 255; // 綠色分量
          const b = 128; // 藍(lán)色分量
          const a = 100; // 不透明度
          // 設(shè)置當(dāng)前像素的顏色值
          data[i] = r; // 紅色分量
          data[i + 1] = g; // 綠色分量
          data[i + 2] = b; // 藍(lán)色分量
          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);
        // 放大回原來(lái)的大小
        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) {
      // 請(qǐng)求訪問(wèn)攝像頭
      navigator.mediaDevices.getUserMedia({ video: true })
        .then(function (stream) {
          // 將視頻流綁定到視頻元素上
          video.srcObject = stream;
          // 開始繪制視頻畫面到畫布上
          requestAnimationFrame(drawFrame);
        })
        .catch(function (error) {
          console.error('無(wú)法訪問(wèn)攝像頭:', error);
        });
    } else {
      console.error('瀏覽器不支持 getUserMedia API');
    }
    buttons.forEach(button => {
      button.addEventListener("click", function (e) {
        drawType = e.target.dataset.type;
      })
    })
  </script>
</body>
</html>

點(diǎn)擊截圖

image-20230806221624692

流程

  1. 將Canvas 的 toDataURL 方法將內(nèi)容轉(zhuǎn)換為數(shù)據(jù) URL。
  2. 創(chuàng)建一個(gè) <a> 元素,并將數(shù)據(jù) URL 賦值給其 href 屬性。
  3. 設(shè)置 <a> 元素的 download 屬性為要保存的文件名。
  4. 使用 JavaScript 模擬點(diǎn)擊 <a> 元素來(lái)觸發(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)建一個(gè) <a> 元素并設(shè)置屬性
  const link = document.createElement('a');
  link.href = dataURL;
  link.download = 'screenshot.png'; // 設(shè)置要保存的文件名
  // 模擬點(diǎn)擊 <a> 元素來(lái)觸發(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">藍(lán)色</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 = ""
    // 當(dāng)視頻元素加載完成后執(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; //藍(lán)
        }
        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") {
        // 藍(lán)色
        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;
        // 遍歷每個(gè)像素
        for (let i = 0; i < data.length; i += 4) {
          const x = (i / 4) % canvas.width; // 當(dāng)前像素的 x 坐標(biāo)
          const y = Math.floor(i / (4 * canvas.width)); // 當(dāng)前像素的 y 坐標(biāo)
          // 計(jì)算當(dāng)前像素的顏色值
          const r = x / canvas.width * 255; // 紅色分量
          const g = y / canvas.height * 255; // 綠色分量
          const b = 128; // 藍(lán)色分量
          const a = 100; // 不透明度
          // 設(shè)置當(dāng)前像素的顏色值
          data[i] = r; // 紅色分量
          data[i + 1] = g; // 綠色分量
          data[i + 2] = b; // 藍(lán)色分量
          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);
        // 放大回原來(lái)的大小
        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) {
      // 請(qǐng)求訪問(wèn)攝像頭
      navigator.mediaDevices.getUserMedia({ video: true })
        .then(function (stream) {
          // 將視頻流綁定到視頻元素上
          video.srcObject = stream;
          // 開始繪制視頻畫面到畫布上
          requestAnimationFrame(drawFrame);
        })
        .catch(function (error) {
          console.error('無(wú)法訪問(wèn)攝像頭:', 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)建一個(gè) <a> 元素并設(shè)置屬性
      const link = document.createElement('a');
      link.href = dataURL;
      link.download = 'screenshot.png'; // 設(shè)置要保存的文件名
      // 模擬點(diǎn)擊 <a> 元素來(lái)觸發(fā)下載
      link.click();
    })
  </script>
</body>
</html>

以上就是使用canvas實(shí)現(xiàn)魔法攝像頭的示例代碼的詳細(xì)內(nèi)容,更多關(guān)于canvas 魔法攝像頭的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

最新評(píng)論