前端黑科技:使用 JavaScript 實現網頁掃碼功能

geekcjj發表於2024-08-10

在數字化時代,二維碼已經滲透到我們生活的方方面面。從移動支付到產品溯源,二維碼憑藉其便捷性和高效性,成為了資訊傳遞的重要載體。而隨著前端技術的不斷髮展,我們甚至可以使用 JavaScript 在網頁端實現二維碼掃描功能,為使用者提供更加便捷的操作體驗。

本文將帶您深入瞭解如何使用 JavaScript 呼叫攝像頭,結合 jsQR 庫,以及如何控制閃光燈,最終實現一個功能完善的網頁掃碼應用。

一、 專案概述

我們將建立一個簡單的網頁應用,該應用能夠:

  1. 呼叫裝置攝像頭,獲取實時影片流。
  2. 在網頁上建立一個掃描區域,使用者可以將二維碼放置在該區域內進行掃描。
  3. 使用 jsQR 庫解碼掃描區域內的二維碼影像資料,獲取二維碼內容。
  4. 提供手動輸入二維碼內容的功能。
  5. 如果裝置支援,還可以控制閃光燈的開關,以便在光線不足的情況下進行掃描。

二、 實現步驟

1. HTML 結構

首先,我們需要構建基本的 HTML 結構,包括:

  • <video> 標籤:用於展示攝像頭捕獲的實時影片流。
  • <canvas> 標籤:用於繪製影片幀和掃描區域,並從中獲取影像資料。
  • <div> 標籤:用於建立掃描區域、按鈕組等 UI 元素。
<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>掃一掃</title>
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <video id="video" autoplay></video>
  <canvas id="overlay" hidden></canvas>

  <div class="scan-area"></div>

  <div class="btn-group">
    <button id="manualInputBtn">手動輸入</button>
    <button id="flashBtn">閃光燈</button>
  </div>

  <script src="https://cdn.jsdelivr.net/npm/jsqr@1.4.0/dist/jsQR.min.js"></script>
  <script src="script.js"></script>
</body>
</html>

2. CSS 樣式

為了提升使用者體驗,我們需要為頁面元素新增一些樣式:

/* style.css */
body {
  margin: 0;
  overflow: hidden;
}

#video {
  width: 100%;
  height: auto;
  display: block;
}

#overlay {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
}

.scan-area {
  border: 3px solid yellow;
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: 80%;
  height: 30%;
}

/* ...其他樣式 */

3. JavaScript 互動

JavaScript 程式碼是實現掃碼功能的核心部分,主要包括以下幾個步驟:

  1. 獲取攝像頭許可權: 使用 navigator.mediaDevices.getUserMedia() 方法請求訪問使用者的攝像頭。

  2. 播放影片流: 將獲取到的影片流賦值給 <video> 標籤的 srcObject 屬性,並呼叫 video.play() 方法開始播放。

  3. 建立掃描迴圈: 使用 requestAnimationFrame() 方法建立一個迴圈,不斷地從影片流中獲取幀影像,並進行二維碼解碼。

  4. 繪製影片幀: 在每一幀中,使用 canvas.drawImage() 方法將影片幀繪製到 <canvas> 元素上。

  5. 獲取掃描區域影像資料: 使用 canvas.getImageData() 方法獲取掃描區域的影像資料。

  6. 解碼二維碼: 使用 jsQR 庫的 jsQR() 方法解碼影像資料,如果解碼成功,則獲取二維碼內容。

  7. 處理掃描結果: 對解碼後的二維碼內容進行處理,例如跳轉到連結、顯示資訊等。

  8. 實現其他功能: 實現手動輸入二維碼內容和控制閃光燈等功能。

// script.js
const video = document.getElementById('video');
const overlay = document.getElementById('overlay');
const manualInputBtn = document.getElementById('manualInputBtn');
const flashBtn = document.getElementById('flashBtn');
const scanArea = document.querySelector('.scan-area');

let stream;
let scanning = false;
let flashEnabled = false;

// 獲取攝像頭許可權並開始播放影片流
navigator.mediaDevices.getUserMedia({ video: { facingMode: "environment" } })
  .then(s => {
    stream = s;
    video.srcObject = stream;
    video.play();

    // 開始掃描
    scanning = true;
    requestAnimationFrame(scan);
  })
  .catch(err => {
    console.error("無法訪問攝像頭:", err);
  });

// 掃描二維碼
function scan() {
  if (scanning) {
    const canvas = overlay.getContext('2d');
    const videoWidth = video.videoWidth;
    const videoHeight = video.videoHeight;

    // 設定畫布大小
    overlay.width = videoWidth;
    overlay.height = videoHeight;

    // 將影片幀繪製到畫布上
    canvas.drawImage(video, 0, 0, videoWidth, videoHeight);

    // ...獲取掃描區域影像資料

    // 使用 jsQR 庫解碼二維碼
    const code = jsQR(imageData.data, imageData.width, imageData.height);

    // 如果成功解碼,則停止掃描並處理結果
    if (code) {
      scanning = false;
      handleScanResult(code.data);
    } else {
      requestAnimationFrame(scan);
    }
  }
}

// 處理掃描結果
function handleScanResult(data) {
  alert("掃描結果:" + data);

  // 這裡可以根據掃描結果進行相應的操作,例如跳轉到連結或顯示資訊
}

// 手動輸入按鈕點選事件
manualInputBtn.addEventListener('click', function() {
  // ...
});

// 閃光燈按鈕點選事件
flashBtn.addEventListener('click', function() {
  // ...
});

三、完整程式碼

<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>掃一掃</title>
  <style>
    body {
      margin: 0;
      overflow: hidden;
    }

    #video {
      width: 100%;
      height: auto;
      display: block;
    }

    #overlay {
      position: absolute;
      top: 0;
      left: 0;
      width: 100%;
      height: 100%;
      pointer-events: none;
    }

    .scan-area {
      border: 3px solid yellow;
      position: absolute;
      top: 50%;
      left: 50%;
      transform: translate(-50%, -50%);
      width: 80%;
      height: 30%;
    }

    .btn-group {
      position: absolute;
      bottom: 20px;
      left: 50%;
      transform: translateX(-50%);
      display: flex;
    }

    button {
      background-color: rgba(0, 0, 0, 0.5);
      color: white;
      border: none;
      padding: 10px 20px;
      margin: 0 10px;
      border-radius: 5px;
      font-size: 16px;
      cursor: pointer;
    }
  </style>
</head>
<body>

<video id="video" autoplay></video>
<canvas id="overlay" hidden></canvas>

<div class="scan-area"></div>

<div class="btn-group">
  <button id="manualInputBtn">手動輸入</button>
  <button id="flashBtn">閃光燈</button>
</div>

<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/jsqr@1.4.0/dist/jsQR.min.js"></script>
<script>
  const video = document.getElementById('video');
  const overlay = document.getElementById('overlay');
  const manualInputBtn = document.getElementById('manualInputBtn');
  const flashBtn = document.getElementById('flashBtn');
  const scanArea = document.querySelector('.scan-area');

  let stream;
  let scanning = false;
  let flashEnabled = false;

  // 獲取攝像頭許可權並開始播放影片流
  navigator.mediaDevices.getUserMedia({ video: { facingMode: "environment" } })
    .then(function(s) {
      stream = s;
      video.srcObject = stream;
      video.play();

      // 開始掃描
      requestAnimationFrame(scan);
    })
    .catch(function(err) {
      console.error("無法訪問攝像頭:", err);
    });

  // 掃描二維碼
  function scan() {
    if (scanning) {
      const canvas = overlay.getContext('2d');
      const videoWidth = video.videoWidth;
      const videoHeight = video.videoHeight;

      // 設定畫布大小
      overlay.width = videoWidth;
      overlay.height = videoHeight;

      // 將影片幀繪製到畫布上
      canvas.drawImage(video, 0, 0, videoWidth, videoHeight);

      // 獲取掃描區域的座標和尺寸
      const scanAreaRect = scanArea.getBoundingClientRect();
      const scanAreaX = scanAreaRect.left;
      const scanAreaY = scanAreaRect.top;
      const scanAreaWidth = scanAreaRect.width;
      const scanAreaHeight = scanAreaRect.height;

      // 獲取掃描區域的影像資料
      const imageData = canvas.getImageData(scanAreaX, scanAreaY, scanAreaWidth, scanAreaHeight);

      // 使用 jsQR 庫解碼二維碼
      const code = jsQR(imageData.data, imageData.width, imageData.height);

      // 如果成功解碼,則停止掃描並處理結果
      if (code) {
        scanning = false;
        handleScanResult(code.data);
      } else {
        requestAnimationFrame(scan);
      }
    }
  }

  // 處理掃描結果
  function handleScanResult(data) {
    alert("掃描結果:" + data);

    // 這裡可以根據掃描結果進行相應的操作,例如跳轉到連結或顯示資訊
  }

  // 手動輸入按鈕點選事件
  manualInputBtn.addEventListener('click', function() {
    const input = prompt("請輸入二維碼內容:");
    if (input) {
      handleScanResult(input);
    }
  });

  // 閃光燈按鈕點選事件
  flashBtn.addEventListener('click', function() {
    if ('torch' in navigator.mediaDevices.getUserMedia({ video: true })) {
      flashEnabled = !flashEnabled;
      stream.getVideoTracks()[0].applyConstraints({
        advanced: [{ torch: flashEnabled }]
      });

      // 更新按鈕文字
      flashBtn.textContent = flashEnabled ? '關閉閃光燈' : '閃光燈';
    } else {
      alert('您的裝置不支援閃光燈功能。');
    }
  });

  // 開始掃描
  scanning = true;
</script>

</body>
</html> 

四、 總結

透過以上步驟,我們成功地使用 JavaScript 在網頁端實現了二維碼掃描功能。該功能可以廣泛應用於各種場景,例如:

  • 移動支付: 使用者可以使用手機掃描網頁上的二維碼完成支付。
  • 產品溯源: 使用者可以掃描產品上的二維碼,檢視產品資訊、生產日期、物流資訊等。
  • 活動簽到: 使用者可以使用手機掃描二維碼完成活動簽到。

隨著 Web 技術的不斷髮展,相信未來會有更多創新的應用場景出現。

希望本文能夠幫助您瞭解網頁掃碼功能的實現原理,並激發您探索更多前端黑科技的興趣。

相關文章