background

The other day the front end interviewer asked a question: “Do you know anything about Canvas?”

“This I know, I have done DEMO, this is not difficult, look at its API interface can achieve!”

Seeing him so confident, I decided to get to know him better!

“E-commerce big turntable, nine squares, scratch-off, how to use canvas to achieve, tell me your idea?

“How to achieve the generation of two-dimensional code and scan code recognition?”

“What about the particle explosion effect of the picture?

“……”


Therefore, I plan to write a series of articles about canvas to explore and improve myself and share them with you.

Generation of TWO-DIMENSIONAL code

The generation of two-dimensional code needs to use the third-party library, use its algorithm to convert the text into two-dimensional code, and draw it with canvas. Use canva. toDataURL(‘image/ PNG ‘) to get the qr code to base64 value and assign it to the SRC attribute of the IMG tag

Here I use a library, QrcodeJS.

Click “Demo” to see the effect

The usage method is as follows:


<! -- index.html -->
<! DOCTYPEhtml>
<html>
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <title>Suporka Vue App</title>
    <style>
      .container {
        padding: 60px;
        margin: 0 auto;
        line-height: 50px;
      }
      input {
        display: inline-block;
        width: 200px;
        height: 32px;
        line-height: 1.5;
        padding: 4px 7px;
        font-size: 12px;
        border: 1px solid #dcdee2;
        border-radius: 4px;
        color: #515a6e;
        background-color: #fff;
        background-image: none;
        position: relative;
        cursor: text;
        transition: border 0.2 s ease-in-out, background 0.2 s ease-in-out,
          box-shadow 0.2 s ease-in-out;
      }
      button {
        color: #fff;
        background-color: #19be6b;
        border-color: #19be6b;
        outline: 0;
        vertical-align: middle;
        line-height: 1.5;
        display: inline-block;
        font-weight: 400;
        text-align: center;
        -ms-touch-action: manipulation;
        touch-action: manipulation;
        cursor: pointer;
        background-image: none;
        border: 1px solid transparent;
        white-space: nowrap;
        -webkit-user-select: none;
        -moz-user-select: none;
        -ms-user-select: none;
        user-select: none;
        padding: 5px 15px 6px;
        font-size: 12px;
        border-radius: 4px;
        transition: color 0.2 s linear, background-color 0.2 s linear,
          border 0.2 s linear, box-shadow 0.2 s linear;
      }
      #qrcode {
        margin-top: 20px;
      }
    </style>
  </head>
  <body>
    <div class="container">
      <input
        type="text"
        placeholder="Please enter the string you want to convert to qr code."
        id="input"
      />
      <button onclick="creatQRcode();">A key generation</button>
      <div id="qrcode"></div>
    </div>
    <script src="https://zxpsuper.github.io/Demo/qrcode/qrcode-dev.js"></script>
    <script type="text/javascript">
      var qrcode = null;
      function creatQRcode() {
        document.getElementById("qrcode").innerHTML = "";
        qrcode = new QRCode(document.getElementById("qrcode"), {
          text: document.getElementById("input").value,
          width: 200.height: 200.colorDark: "# 000000".colorLight: "#ffffff".correctLevel: QRCode.CorrectLevel.H
        });
      }
    </script>
  </body>
</html>
Copy the code

options

  • The first parameter is generate storeimgThe parent of the tag
  • The second parameter isObjectThe type ofoptions
attribute type instructions
text String Target text
width Number Image width
height Number Picture height
colorDark String Two-dimensional code color
colorLight The default QRCode. CorrectLevel. L L, qrcode.correctLevel. M, qrcode.correctlevel. Q, qrcode.correctlevel.h

Qr code scanning identification

Here, a library llqrcode.js is used to decode the canvas with ID QR-canvas using qrcode.decode().

Demo and project source code first

What we need to do is to call the camera of the device (rear camera is preferred), and the obtained picture is displayed in real time with the video tag. Then, the picture is periodically taken to generate canvas and decrypted by calling qrcode.decode().

// variable.js
var gCtx = null; //canvas.ctx
var gCanvas = null; // qr-canvas
// var c = 0;
var stype = 0; // Identify flow 0 not started, 1 in progress, 2 completed
var gUM = false;
var webkit = false;
var moz = false;
var v = null; // Store the video variable
var scanCodeStart = false; // Start scanning
var mediaStreamTrack = null; Mediastreamtrack.stop ()
var imghtml =
  '<div id="qrfile"><canvas id="out-canvas" width="320" height="240"></canvas>' +
  '<div id="imghelp">drag and drop a QRCode here' +
  "<br>or select a file" +
  '<input type="file" onchange="handleFiles(this.files)" id="upload-img"/>' +
  "</div>" +
  "</div>";

var vidhtml = '<video id="v" autoplay muted></video>';
Copy the code
// methods.js

function qrcodeScanLoad(width, height) {
  if (isCanvasSupported() && window.File && window.FileReader) {
    initCanvas(width, height);
    qrcode.callback = scanCodeCallback;
    document.getElementById("mainbody").style.display = "inline";
    setwebcam();
  } else {
    document.getElementById("mainbody").style.display = "inline";
    document.getElementById("mainbody").innerHTML =
      '<p id="mp1">QR code scanner for HTML5 capable browsers</p><br>' +
      '<br><p id="mp2">sorry your browser is not supported</p><br><br>'; }}// Draw the mask layer canvas
function setMask() {
  var canvas = document.querySelector("#scancode-mask");
  canvas.width =
    document.body.clientWidth > 1024 ? 1024 : document.body.clientWidth;
  canvas.height =
    document.body.clientWidth > 1024 ? 1136 : document.body.clientHeight;
  var ctx = canvas.getContext("2d");

  ctx.fillRect(0.0, canvas.width, canvas.height);

  ctx.globalCompositeOperation = "destination-out";
  ctx.beginPath();
  let x1,
    y1,
    width = canvas.width * 0.6;
  x1 = (canvas.width - width) / 2;
  y1 = (canvas.height - width) / 2;
  ctx.fillRect(x1, y1, width, width);
  ctx.fill();
  ctx.save();

  ctx.globalCompositeOperation = "source-over";

  // Second quadrant point
  ctx.moveTo(x1, y1 + 2);
  ctx.lineTo(x1 + 20, y1 + 2);
  ctx.moveTo(x1 + 2, y1);
  ctx.lineTo(x1 + 2, y1 + 20);

  // First quadrant point
  ctx.moveTo(x1 + width - 20, y1 + 2);
  ctx.lineTo(x1 + width, y1 + 2);
  ctx.moveTo(x1 + width - 2, y1 + 1);
  ctx.lineTo(x1 + width - 2, y1 + 20);

  // The fourth quadrant
  ctx.moveTo(x1 + width - 20, y1 + width - 2);
  ctx.lineTo(x1 + width, y1 + width - 2);
  ctx.moveTo(x1 + width - 2, y1 + width - 1);
  ctx.lineTo(x1 + width - 2, y1 + width - 20);

  // The third quadrant
  ctx.moveTo(x1 + 20, y1 + width - 2);
  ctx.lineTo(x1, y1 + width - 2);
  ctx.moveTo(x1 + 2, y1 + width - 2);
  ctx.lineTo(x1 + 2, y1 + width - 20);
  ctx.lineWidth = 4;
  ctx.strokeStyle = "green";
  ctx.stroke();
}
function setwebcam() {
  var options = true;
  if (navigator.mediaDevices && navigator.mediaDevices.enumerateDevices) {
    try {
      navigator.mediaDevices.enumerateDevices().then(function(devices) {
        let video = [];
        devices.forEach(function(device) {
          if (device.kind === "videoinput") { video.push(device); }});// Call the camera of the device. Video [1] is the rear camera, or label containing "back" is the rear camera
        if (video.length >= 2) {
          options = {
            deviceId: { exact: video[1].deviceId },
            facingMode: { exact: "environment"}}; } video.forEach(item= > {
          if (item.label.toLowerCase().search("back") > -1)
            options = {
              deviceId: { exact: device.deviceId },
              facingMode: { exact: "environment"}}; }); scanCodeStart =true;
        setwebcam2(options);
      });
    } catch (e) {
      console.error(e); }}else {
    console.log("no navigator.mediaDevices.enumerateDevices"); setwebcam2(options); }}function setwebcam2(options) {
  if (stype == 1) {
    setTimeout(captureToCanvas, 500);
    return;
  }

  var n = navigator;
  document.getElementById("outdiv").innerHTML = vidhtml;
  v = document.getElementById("v");
  try {
    if (n.mediaDevices && n.mediaDevices.getUserMedia) {
      n.mediaDevices
        .getUserMedia({ video: options, audio: false })
        .then(function(stream) {
          success(stream);
        })
        .catch(function(error) {
          error(error);
        });
    } else if (n.getUserMedia) {
      webkit = true;
      n.getUserMedia({ video: options, audio: false }, success, error);
    } else if (n.webkitGetUserMedia) {
      webkit = true;
      n.webkitGetUserMedia({ video: options, audio: false}, success, error); }}catch (err) {
    console.log(err);
  }
  stype = 1;
  if (getSystem() === "ios") {
    alert("Your device does not support real-time scan code, please upload pictures to identify!");
    return;
  }
  if (
    (n.mediaDevices && n.mediaDevices.getUserMedia) ||
    n.getUserMedia ||
    n.webkitGetUserMedia
  )
    setTimeout(captureToCanvas, 500);
  else {
    alert("Your device does not support real-time scan code, please upload pictures to identify!"); }}// Get the operating system
function getSystem() {
  var u = navigator.userAgent;
  var isAndroid = u.indexOf("Android") > -1 || u.indexOf("Linux") > -1; //g
  varisIOS = !! u.match(/\(i[^;] +; ( U;) ? CPU.+Mac OS X/); / / ios terminal
  if (isAndroid) {
    // This is android operating system
    return "android";
  } else if (isIOS) {
    // This is the ios operating system
    return "ios";
  } else {
    return "other"; }}// Select the image to upload
function setimg($event) {
  qrcode.callback = scanCodeCallback;
  $event && $event.preventDefault();
  stype = 2;
  let file = document.getElementById("upload-img");
  file.click();
}

// Successful upload callback
function scanCodeCallback(a) {
  var html = htmlEntities(a);
  stype = 0;
  alert(html);
}

// Handle upload file identification
function handleFiles(f) {
  var o = [];

  for (var i = 0; i < f.length; i++) {
    var reader = new FileReader();
    reader.onload = (function(theFile) {
      return function(e) {
        gCtx.clearRect(0.0, gCanvas.width, gCanvas.height); qrcode.decode(e.target.result); }; })(f[i]); reader.readAsDataURL(f[i]); }}function initCanvas(w, h) {
  gCanvas = document.getElementById("qr-canvas");
  gCanvas.style.width = w + "px";
  gCanvas.style.height = h + "px";
  gCanvas.width = w;
  gCanvas.height = h;
  gCtx = gCanvas.getContext("2d");
  gCtx.clearRect(0.0, w, h);
}

// Canvas to canvas
function captureToCanvas() {
  if(stype ! =1) return;
  if (gUM && scanCodeStart) {
    try {
      gCtx.drawImage(v, 0.0);
      try {
        qrcode.decode(); // The default id=qr-canvas canvas is converted to base64 for the image
      } catch (e) {
        console.log(e);
        setTimeout(captureToCanvas, 500); }}catch (e) {
      console.log(e);
      setTimeout(captureToCanvas, 500); }}}// Handle special symbols
function htmlEntities(str) {
  return String(str)
    .replace(/&/g."&amp;")
    .replace(/</g."&lt;")
    .replace(/>/g."&gt;")
    .replace(/"/g."&quot;");
}

// Determine whether canvas is supported
function isCanvasSupported() {
  var elem = document.createElement("canvas");
  return!!!!! (elem.getContext && elem.getContext("2d"));
}

function success(stream) {
  // mediaStreamTrack to close the camera function
  if (stream)
    mediaStreamTrack =
      typeof stream.stop === "function" ? stream : stream.getTracks()[0];

  v.srcObject = stream;
  if (scanCodeStart) {
    v.play();
    gUM = true;
    setTimeout(captureToCanvas, 500);
  } else{}}function error(error) {
  gUM = false;
  return;
}
Copy the code

SetMask is the method of drawing the mask layer, and qrcodeScanLoad is the initial loading method.


<body>
    <div class="body">
      <div id="mainbody" style="display: inline;">
        <div id="outdiv" autoplay muted></div>
      </div>
      <canvas id="qr-canvas" width="800" height="600"></canvas>
      <canvas id="scancode-mask"></canvas>
      <div class="scancode-tips-group" id="scancode-tips-group">
        <span class="tips">Put the QR Code into the box and it will be scanned automatically</span>
        <div class="upload-my-code" onClick="setimg()">My QR Code</div>
      </div>

      <div id="img-upload-container" style="display: none">
        <div id="qrfile">
          <canvas id="out-canvas" width="320" height="240"></canvas>
          <div id="imghelp">
            drag and drop a QRCode here
            <br />or select a file
            <input
              type="file"
              onchange="handleFiles(this.files)"
              id="upload-img"
            />
          </div>
        </div>
      </div>
    </div>
    <script src="./llqrcode.js"></script>
    <script src="./variable.js"></script>
    <script src="./methods.js"></script>
    <script>
      qrcodeScanLoad(320.400);
      setMask();
      // Display my QR Code location
      if (document.body.clientWidth < 1025) {
        document.getElementById("scancode-tips-group").style.top =
          (document.body.clientHeight - document.body.clientWidth) / 2 +
          document.body.clientWidth * 0.9 -
          10 +
          "px";
      } else {
        document.getElementById("scancode-tips-group").style.top = "720px";
      }
    </script>
  </body>
Copy the code

The canvas width is set to 1024 when it exceeds 1024. The details are commented in the code.

More recommended

Advanced_front_end

Daily question

Webpack4 Build Vue application (createVue)