Интерфейс реализует очень впечатляющую функцию сканирования на стороне браузера 🌟

внешний интерфейс JavaScript
Интерфейс реализует очень впечатляющую функцию сканирования на стороне браузера 🌟

Мало знаний, большой вызов! Эта статья участвует в "Необходимые знания для программистов«Творческая деятельность.

Эта статья участвовала в "Проект «Звезда раскопок»”, чтобы выиграть творческий подарочный пакет и бросить вызов творческим поощрительным деньгам.

задний план

Не так давно я выполнил функцию получения камеры браузера и сканирования кода для распознавания.В этой статье разобраны точки знаний и конкретные реализации кода, связанные с этим, и организованы в содержание этой статьи.

Эта статья в основном знакомит с использованиемvueПередняя технология разработки стека технологий, активация камеры на стороне браузера📷, и выполните функцию распознавания скан-кода, чтобы выполнить переход или другие операции с распознанным QR-кодом. Содержание этого документа разделено на вводную часть, эффект реализации, техническое введение, реализацию кода, резюме и другие части.

добиться эффекта

В этом примере есть две основные страницы, домашняя страница и страница с кодом сканирования Конкретный эффект реализации показан на следующем рисунке.

  • Главная: нажмитеSCAN QRCODEкнопку для ввода кодовой страницы сканирования.
  • Сканировать кодовую страницу: при вводе в первый раз или всплывающее окно获取摄像头访问权限的系统提示框, нажмите Разрешить доступ, страница начнет загружать данные камеры и начнет захватывать и подбирать QR-код.Если QR-код захвачен, начинается синтаксический анализ QR-кода.После успешного синтаксического анализа появляется сообщение об успешном выполнении загрузки и идентификации отображается окно вверх.

📸онлайн опыт:дракон IR.GitHub.IO/также 5-трехразовое питание-но так…

📌Подсказка: вертикальный экран в браузере для доступа к камере устройства. Экран телефона в любом случае я обнаруживаю мало знаний, могу перейти к другой статье«Внешние знания в пятицветной мини-игре»чтобы понять.

Техническое введение

WebRTC API

WebRTC (Web Real-Time Communications) — это технология связи в реальном времени., что позволяет веб-приложению или сайтубез посредниковМежду корпусом браузера учреждения点对点(Peer-to-Peer)соединение для реализации передачи видеопотока и/или аудиопотока или других произвольных данных.WebRTCЭти стандарты включены, чтобы позволить пользователям создавать点对点(Peer-to-Peer)возможен обмен данными и конференц-связь.

три основных интерфейса:

  • MediaStream: Возможность получения синхронного потока видео и аудио через камеру и микрофон устройства.
  • RTCPeerConnection:ДаWebRTCКомпоненты для создания стабильной и эффективной потоковой передачи между узлами.
  • RTCDataChannel: обеспечивает высокую пропускную способность канала с малой задержкой между браузерами для передачи произвольных данных.

🔗идти кMDNУглубленное изучение:WebRTC_API

WebRTC adapter

несмотря на то чтоWebRTCСпецификация относительно надежна и стабильна, но не во всех браузерах реализованы все ее функции, а некоторым браузерам требуются некоторые или все функции.WebRTC APIПри добавлении префикса к нормальному использованию.

WebRTCорганизоватьgithubпредоставилWebRTC适配器(WebRTC adapter)решить реализацию в разных браузерахWebRTCпроблемы совместимости. Этот адаптер являетсяJavaScript垫片, что позволяет вамWebRTCкод, как описано в спецификации, во всех поддерживаемыхWebRTCНет необходимости писать префиксы или другие обходные пути совместимости в браузерах.

🔗идти кMDNУглубленное изучение:WebRTC adapter

Основной APInavigator.mediaDevices.getUserMedia

Веб-страница вызывает камеру, которую нужно вызватьgetUserMedia API,MediaDevices.getUserMedia()Пользователю будет предложено дать разрешение на использование медиа-ввода, что сгенерируетMediaStream, который содержит треки запрошенного типа мультимедиа. Этот поток может содержать видеодорожку (из аппаратных или виртуальных источников видео, таких как камеры, устройства видеозахвата, сервисы совместного использования экрана и т. д.), аудиодорожку (также из аппаратных или виртуальных источников звука, таких как микрофоны,A/D转换器д.), это может быть дорожка другого типа.

Он возвращает одинPromiseобъект, после успешногоresolveперезвони одинMediaStream对象; если пользователь отказывает в доступе или нужный медиа-источник недоступен,promiseМогуrejectперезвони одинPermissionDeniedError или NotFoundError. (вернутьpromise对象вероятно, ниresolveни будетreject, потому что пользователю не нужно разрешать или запрещать. )

Обычно вы можете использоватьnavigator.mediaDevicesполучитьMediaDevices, например:

navigator.mediaDevices.getUserMedia(constraints)
  .then(function(stream) {
    // 使用这个stream
  })
  .catch(function(err) {
    // 处理error
  })

🔗идти кMDNУглубленное изучение:navigator.mediaDevices.getUserMedia

Библиотека разбора QR-кодаJSQR

jsQRчистыйJavaScriptБиблиотека разбора QR-кода, которая считывает необработанные изображения или камеры и находит, извлекает и анализирует любыеQR码.

Если вы хотите использоватьjsQRДля сканирования потока с веб-камеры вам необходимоImageDataИзвлеченный из видеопотока, может быть передан вjsQR.

jsQRЭкспорт метода, который принимает4параметры соответственно расшифровываются图像数据,,так же как可选的对象Далее настройте поведение сканирования.

imageData: формат[r0, g0, b0, a0, r1, g1, b1, a1, ...]изUint8ClampedArray( 8位无符号整型固定数组)изrgbaПиксельные значения.

const code = jsQR(imageData, width, height, options);
if (code) {
  console.log('找到二维码!', code);
}

🔗идти кgithubпонять глубже:jsQR

Код

обработать

Весь процесс сканирования кода показан на рисунке ниже: инициализация страницы, сначала проверьте, поддерживает ли ее браузерmediaDevicesСвязанныйAPI, браузер передаст камеру, в случае неудачи звонка будет выполнен обратный вызов с ошибкой, в случае успешного звонка будет захвачен видеопоток, а затем будет отсканирован скан-код.

Следующее содержимое разделяет процесс и соответственно реализует соответствующие функции.

Компонент кода сканированияScaner

структура страницы

Давайте сначала посмотрим на структуру страницы, которая в основном состоит из4Частичный состав:

  • поле подсказки.
  • Окно кода сканирования.
  • video: Показать видеопоток, захваченный камерой.
  • canvas: рисовать видеокадры для распознавания QR-кода.
<template>
  <div class="scaner" ref="scaner">
    <!-- 提示框:用于在不兼容的浏览器中显示提示语 -->
    <div class="banner" v-if="showBanner">
      <i class="close_icon" @click="() => showBanner = false"></i>
      <p class="text">若当前浏览器无法扫码,请切换其他浏览器尝试</p>
    </div>
    <!-- 扫码框:显示扫码动画 -->
    <div class="cover">
      <p class="line"></p>
      <span class="square top left"></span>
      <span class="square top right"></span>
      <span class="square bottom right"></span>
      <span class="square bottom left"></span>
      <p class="tips">将二维码放入框内,即可自动扫描</p>
    </div>
    <!-- 视频流显示 -->
    <video
      v-show="showPlay"
      class="source"
      ref="video"
      :width="videoWH.width"
      :height="videoWH.height"
      controls
    ></video>
    <canvas v-show="!showPlay" ref="canvas" />
    <button v-show="showPlay" @click="run">开始</button>
  </div>
</template>

метод: рисовать

  • рисовать линии.
  • Рамка изображения (используется для рисования прямоугольной графики после успешного сканирования кода).

// 画线
drawLine (begin, end) {
  this.canvas.beginPath();
  this.canvas.moveTo(begin.x, begin.y);
  this.canvas.lineTo(end.x, end.y);
  this.canvas.lineWidth = this.lineWidth;
  this.canvas.strokeStyle = this.lineColor;
  this.canvas.stroke();
},
// 画框
drawBox (location) {
  if (this.drawOnfound) {
    this.drawLine(location.topLeftCorner, location.topRightCorner);
    this.drawLine(location.topRightCorner, location.bottomRightCorner);
    this.drawLine(location.bottomRightCorner, location.bottomLeftCorner);
    this.drawLine(location.bottomLeftCorner, location.topLeftCorner);
  }
},

Метод: Инициализировать

  • Проверьте поддержку.
  • Включите камеру.
  • Обработка успеха и неудачи.

// 初始化
setup () {
  // 判断了浏览器是否支持挂载在MediaDevices.getUserMedia()的方法
  if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
    this.previousCode = null;
    this.parity = 0;
    this.active = true;
    this.canvas = this.$refs.canvas.getContext("2d");
    // 获取摄像头模式,默认设置是后置摄像头
    const facingMode = this.useBackCamera ? { exact: 'environment' } : 'user';
    // 摄像头视频处理
    const handleSuccess = stream => {
       if (this.$refs.video.srcObject !== undefined) {
        this.$refs.video.srcObject = stream;
      } else if (window.videoEl.mozSrcObject !== undefined) {
        this.$refs.video.mozSrcObject = stream;
      } else if (window.URL.createObjectURL) {
        this.$refs.video.src = window.URL.createObjectURL(stream);
      } else if (window.webkitURL) {
        this.$refs.video.src = window.webkitURL.createObjectURL(stream);
      } else {
        this.$refs.video.src = stream;
      }
      // 不希望用户来拖动进度条的话,可以直接使用playsinline属性,webkit-playsinline属性
      this.$refs.video.playsInline = true;
      const playPromise = this.$refs.video.play();
      playPromise.catch(() => (this.showPlay = true));
      // 视频开始播放时进行周期性扫码识别
      playPromise.then(this.run);
    };
    // 捕获视频流
    navigator.mediaDevices
      .getUserMedia({ video: { facingMode } })
      .then(handleSuccess)
      .catch(() => {
        navigator.mediaDevices
          .getUserMedia({ video: true })
          .then(handleSuccess)
          .catch(error => {
            this.$emit("error-captured", error);
          });
      });
  }
},

Метод: периодическое сканирование

run () {
  if (this.active) {
    // 浏览器在下次重绘前循环调用扫码方法
    requestAnimationFrame(this.tick);
  }
},

Метод: Обратный вызов в случае успеха

// 二维码识别成功事件处理
found (code) {
  if (this.previousCode !== code) {
    this.previousCode = code;
  } else if (this.previousCode === code) {
    this.parity += 1;
  }
  if (this.parity > 2) {
    this.active = this.stopOnScanned ? false : true;
    this.parity = 0;
    this.$emit("code-scanned", code);
  }
},

Метод: стоп


// 完全停止
fullStop () {
  if (this.$refs.video && this.$refs.video.srcObject) {
    // 停止视频流序列轨道
    this.$refs.video.srcObject.getTracks().forEach(t => t.stop());
  }
}

Метод: сканирование

  • Нарисуйте кадры видео.
  • Идентификация кода сканирования.

// 周期性扫码识别
tick () {
  // 视频处于准备阶段,并且已经加载足够的数据
  if (this.$refs.video && this.$refs.video.readyState === this.$refs.video.HAVE_ENOUGH_DATA) {
    // 开始在画布上绘制视频
    this.$refs.canvas.height = this.videoWH.height;
    this.$refs.canvas.width = this.videoWH.width;
    this.canvas.drawImage(this.$refs.video, 0, 0, this.$refs.canvas.width, this.$refs.canvas.height);
    // getImageData() 复制画布上制定矩形的像素数据
    const imageData = this.canvas.getImageData(0, 0, this.$refs.canvas.width, this.$refs.canvas.height);
    let code = false;
    try {
      // 识别二维码
      code = jsQR(imageData.data, imageData.width, imageData.height);
    } catch (e) {
      console.error(e);
    }
    // 如果识别出二维码,绘制矩形框
    if (code) {
      this.drawBox(code.location);
      // 识别成功事件处理
      this.found(code.data);
    }
  }
  this.run();
},

родительский компонент

ScanerРодительский компонент в основном загружает страницу и отображаетScanerОбратный вызов результата кода сканирования.

структура страницы

<template>
  <div class="scan">
    <!-- 页面导航栏 -->
    <div class="nav">
      <a class="close" @click="() => $router.go(-1)"></a>
      <p class="title">Scan QRcode</p>
    </div>
    <div class="scroll-container">
      <!-- 扫码子组件 -->
      <Scaner
        v-on:code-scanned="codeScanned"
        v-on:error-captured="errorCaptured"
        :stop-on-scanned="true"
        :draw-on-found="true"
        :responsive="false"
      />
    </div>
  </div>
</template>

Метод родительского компонента

import Scaner from '../components/Scaner';

export default {
  name: 'Scan',
  components: {
    Scaner
  },
  data () {
    return {
      errorMessage: "",
      scanned: ""
    }
  },
  methods: {
    codeScanned(code) {
      this.scanned = code;
      setTimeout(() => {
        alert(`扫码解析成功: ${code}`);
      }, 200)
    },
    errorCaptured(error) {
      switch (error.name) {
        case "NotAllowedError":
          this.errorMessage = "Camera permission denied.";
          break;
        case "NotFoundError":
          this.errorMessage = "There is no connected camera.";
          break;
        case "NotSupportedError":
          this.errorMessage =
            "Seems like this page is served in non-secure context.";
          break;
        case "NotReadableError":
          this.errorMessage =
            "Couldn't access your camera. Is it already in use?";
          break;
        case "OverconstrainedError":
          this.errorMessage = "Constraints don't match any installed camera.";
          break;
        default:
          this.errorMessage = "UNKNOWN ERROR: " + error.message;
      }
      console.error(this.errorMessage);
     alert('相机调用失败');
    }
  },
  mounted () {
    var str = navigator.userAgent.toLowerCase();
    var ver = str.match(/cpu iphone os (.*?) like mac os/);
    // 经测试 iOS 10.3.3以下系统无法成功调用相机摄像头
    if (ver && ver[1].replace(/_/g,".") < '10.3.3') {
     alert('相机调用失败');
    }
  }

полный код

🔗 github: GitHub.com/dragon IR/ также 5…

Суммировать

Расширение приложения

Думаю следующих функций можно добиться вызовом камеры через браузер и сканированием и идентификацией.Какие еще функции думаете很哇塞🌟Функциональное приложение может быть реализовано путем сканирования кода на стороне браузера.😂?

  • 🌏Прыжок по ссылке.
  • 🛒проверить цену.
  • 🔒Аутентификация входа.
  • 📂загрузка документа.

совместимость

  • даже использоватьadapter,getUserMedia APIЕсть также неподдерживаемые в некоторых браузерах.
  • Старые браузеры (например,iOS 10.3последующий),AndroidНишевые браузеры (например,IQOOвстроенный браузер) не совместим.
  • QQ,微信Встроенный браузер не вызывается.

использованная литература