亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

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

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

背景

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

魔鬼風格

image-20230806210316856

復(fù)古風格

image-20230806211755401

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

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

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

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

調(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) {
      // 請求訪問攝像頭
      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上

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');
    // 當視頻元素加載完成后執(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三個分量取平均值)

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 = ""
    // 當視頻元素加載完成后執(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é)如下

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

點擊截圖

image-20230806221624692

流程

  1. 將Canvas 的 toDataURL 方法將內(nèi)容轉(zhuǎn)換為數(shù)據(jù) URL。
  2. 創(chuàng)建一個 <a> 元素,并將數(shù)據(jù) URL 賦值給其 href 屬性。
  3. 設(shè)置 <a> 元素的 download 屬性為要保存的文件名。
  4. 使用 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)文章

最新評論