1

I have a JavaScript script that I am getting an error for that I can't figure out. I am trying to take a picture of the webcam feed using JavaScript

The error is:

Uncaught TypeError: webcam.snap is not a function

I am using webcam.js to take the snapshot.

Here is my JavaScript code:

  <script>
    var video = document.createElement("video");
    var canvasElement = document.getElementById("canvas");
    var canvas = canvasElement.getContext("2d");
    var loadingMessage = document.getElementById("loadingMessage");
    var outputContainer = document.getElementById("output");
    var outputMessage = document.getElementById("outputMessage");
    var outputData = document.getElementById("outputData");
    const jsQR = require("jsqr");

    function drawLine(begin, end, color) {
      canvas.beginPath();
      canvas.moveTo(begin.x, begin.y);
      canvas.lineTo(end.x, end.y);
      canvas.lineWidth = 4;
      canvas.strokeStyle = color;
      canvas.stroke();
    }

    // Use facingMode: environment to attemt to get the front camera on phones
    navigator.mediaDevices.getUserMedia({ video: { facingMode: "environment" } }).then(function(stream) {
      video.srcObject = stream;
      video.setAttribute("playsinline", true); // required to tell iOS safari we don't want fullscreen
      video.play();
      requestAnimationFrame(tick);
    });

    function tick() {
      loadingMessage.innerText = "⌛ Loading video..."
      if (video.readyState === video.HAVE_ENOUGH_DATA) {
        loadingMessage.hidden = true;
        canvasElement.hidden = false;
        outputContainer.hidden = false;

        canvasElement.height = video.videoHeight;
        canvasElement.width = video.videoWidth;
        canvas.drawImage(video, 0, 0, canvasElement.width, canvasElement.height);
        var imageData = canvas.getImageData(0, 0, canvasElement.width, canvasElement.height);
        var code = jsQR(imageData.data, imageData.width, imageData.height, {
          inversionAttempts: "invertFirst",
        });
        if (code) {
          drawLine(code.location.topLeftCorner, code.location.topRightCorner, "#FF3B58");
          drawLine(code.location.topRightCorner, code.location.bottomRightCorner, "#FF3B58");
          drawLine(code.location.bottomRightCorner, code.location.bottomLeftCorner, "#FF3B58");
          drawLine(code.location.bottomLeftCorner, code.location.topLeftCorner, "#FF3B58");
          outputMessage.hidden = true;
          outputData.parentElement.hidden = false;
          outputData.innerText = code.data;

          takeSnapShot();
        }

        else {
          outputMessage.hidden = false;
          outputData.parentElement.hidden = true;
        }
      }
      requestAnimationFrame(tick);
    }

    // TAKE A SNAPSHOT.
    takeSnapShot = function () {
        webcam.snap(function (data_uri) {
            downloadImage('video', data_uri);
        });
    }

    // DOWNLOAD THE IMAGE.
    downloadImage = function (name, datauri) {
        var a = document.createElement('a');
        a.setAttribute('download', name + '.png');
        a.setAttribute('href', datauri);
        a.click();
    }

  </script>

This is the first line that causes a problem:

webcam.snap(function (data_uri) {
    downloadImage('video', data_uri);
});

This is the second line that causes a problem:

takeSnapShot();

how do I correct this properly?

****** UPDATE ******

The version of webcam.js I am using is WebcamJS v1.0.26. My application is a Django application that launches the HTML file as defined in main.js.

snap: function(user_callback, user_canvas) {
        // use global callback and canvas if not defined as parameter
        if (!user_callback) user_callback = this.params.user_callback;
        if (!user_canvas) user_canvas = this.params.user_canvas;
        
        // take snapshot and return image data uri
        var self = this;
        var params = this.params;
        
        if (!this.loaded) return this.dispatch('error', new WebcamError("Webcam is not loaded yet"));
        // if (!this.live) return this.dispatch('error', new WebcamError("Webcam is not live yet"));
        if (!user_callback) return this.dispatch('error', new WebcamError("Please provide a callback function or canvas to snap()"));
        
        // if we have an active preview freeze, use that
        if (this.preview_active) {
            this.savePreview( user_callback, user_canvas );
            return null;
        }
        
        // create offscreen canvas element to hold pixels
        var canvas = document.createElement('canvas');
        canvas.width = this.params.dest_width;
        canvas.height = this.params.dest_height;
        var context = canvas.getContext('2d');
        
        // flip canvas horizontally if desired
        if (this.params.flip_horiz) {
            context.translate( params.dest_width, 0 );
            context.scale( -1, 1 );
        }
        
        // create inline function, called after image load (flash) or immediately (native)
        var func = function() {
            // render image if needed (flash)
            if (this.src && this.width && this.height) {
                context.drawImage(this, 0, 0, params.dest_width, params.dest_height);
            }
            
            // crop if desired
            if (params.crop_width && params.crop_height) {
                var crop_canvas = document.createElement('canvas');
                crop_canvas.width = params.crop_width;
                crop_canvas.height = params.crop_height;
                var crop_context = crop_canvas.getContext('2d');
                
                crop_context.drawImage( canvas, 
                    Math.floor( (params.dest_width / 2) - (params.crop_width / 2) ),
                    Math.floor( (params.dest_height / 2) - (params.crop_height / 2) ),
                    params.crop_width,
                    params.crop_height,
                    0,
                    0,
                    params.crop_width,
                    params.crop_height
                );
                
                // swap canvases
                context = crop_context;
                canvas = crop_canvas;
            }
            
            // render to user canvas if desired
            if (user_canvas) {
                var user_context = user_canvas.getContext('2d');
                user_context.drawImage( canvas, 0, 0 );
            }
            
            // fire user callback if desired
            user_callback(
                user_canvas ? null : canvas.toDataURL('image/' + params.image_format, params.jpeg_quality / 100 ),
                canvas,
                context
            );
        };
        
        // grab image frame from userMedia or flash movie
        if (this.userMedia) {
            // native implementation
            context.drawImage(this.video, 0, 0, this.params.dest_width, this.params.dest_height);
            
            // fire callback right away
            func();
        }
        else if (this.iOS) {
            var div = document.getElementById(this.container.id+'-ios_div');
            var img = document.getElementById(this.container.id+'-ios_img');
            var input = document.getElementById(this.container.id+'-ios_input');
            // function for handle snapshot event (call user_callback and reset the interface)
            iFunc = function(event) {
                func.call(img);
                img.removeEventListener('load', iFunc);
                div.style.backgroundImage = 'none';
                img.removeAttribute('src');
                input.value = null;
            };
            if (!input.value) {
                // No image selected yet, activate input field
                img.addEventListener('load', iFunc);
                input.style.display = 'block';
                input.focus();
                input.click();
                input.style.display = 'none';
            } else {
                // Image already selected
                iFunc(null);
            }           
        }
        else {
            // flash fallback
            var raw_data = this.getMovie()._snap();
            
            // render to image, fire callback when complete
            var img = new Image();
            img.onload = func;
            img.src = 'data:image/'+this.params.image_format+';base64,' + raw_data;
        }
        
        return null;
    },
Aziza Kasenova
  • 1,501
  • 2
  • 10
  • 22
ironmantis7x
  • 807
  • 2
  • 23
  • 58
  • You're using requireJS, correct? – code Jun 22 '21 at 21:23
  • @code I did not. Do I need to add that in my code? If so, where does it go? – ironmantis7x Jun 22 '21 at 22:41
  • Then did you specifically define a `require` function? – code Jun 22 '21 at 22:46
  • @code I need help with this.I did not do this and I don't know how. I am a beginner to JS. – ironmantis7x Jun 22 '21 at 22:50
  • 2
    You are using client-side JavaScript; you can only use require in server-side JS (Node.js). – code Jun 22 '21 at 22:55
  • Put this in your HTML before your current script tag: `` and get rid of `require("jsqr");` You can only use require in Node.js, but the link import will work just the same. – code Jun 22 '21 at 22:57
  • @code I made the changes you suggested and it still gives the same error. I am trying to trigger taking a picture of the webcam when a qr code is detected. – ironmantis7x Jun 22 '21 at 23:59
  • the qr code is detected but when I get an error saying webcam is not defined when I try to take a snapshot. Do I need to do something different?? – ironmantis7x Jul 08 '21 at 01:28
  • 1
    console.log(webcam) and see what functions are inside. Also, please let us know which version of webcam JS you are using because your code does not show where you are reading the js file. – Kouta Nakano Jul 08 '21 at 01:40
  • See what @KoutaNakano said. – code Jul 10 '21 at 05:43
  • 1
    A quick look at the [documentation](https://github.com/jhuckaby/webcamjs/blob/master/DOCS.md#quickstart-guide) seems to reveal your issue to be caused by a dum typo. That is `Webcam.snap()`... Notice the capital `W`. ;) – Louys Patrice Bessette Jul 10 '21 at 20:47
  • @ironmantis7x is it solved? let me know I may help. – jatinder bhola Jul 12 '21 at 05:46

2 Answers2

1

It seems like either:

  1. the webcam's code are missing (not imported)
    • in this case you need to first call the script from the URL and add it with script tag
<script src="WEBCAM_JS_SOURCE">

or

  1. they are imported, but used with typo. From the webcam source code it is defined as:
var Webcam = {
   version: '1.0.26',
   
   // globals
   ...
};

so you should use with a capital one.

Aziza Kasenova
  • 1,501
  • 2
  • 10
  • 22
  • I tried both of these suggested solutions and I get the same error. "Uncaught TypeError: Webcam.snap is not a function" – ironmantis7x Jul 12 '21 at 02:04
  • How are you importing it? If you are adding as a code from your server, can you try ‘window.Webcam = /* the rest code */‘ so that it will become a global variable – Aziza Kasenova Jul 12 '21 at 05:26
  • importing it like this: "" in the body of the html page – ironmantis7x Jul 13 '21 at 03:55
  • @ironmantis7x since you are fetching the library script from a CDN can you try to put your webcam execution codes in a window `onload` event ? It means your script would run only after the CDN library has been loaded. something like this ```window.addEventListener('load', (event) => { var video = document.createElement("video"); ..... downloadImage = function (name, datauri) { // ..... etc });``` – Towkir Jul 13 '21 at 04:44
1

Your implementation doesn't need Webcamjs, because you're using navigator media devices.

You can either use WebcamJS by initializing it at first and attaching it to some canvas, like in the following code

Webcam.set({
        width: 320,
        height: 240,
        image_format: 'jpeg',
        jpeg_quality: 90
    });
Webcam.attach( '#my_camera' );

Or you can update your takeSnapShot function to the following :

 takeSnapShot = function () {
        downloadImage('video',canvasElement.toDataURL())
        // Webcam.snap(function (data_uri) {
        //     downloadImage('video', data_uri);
        // });
    }

Here's a working example based on your code https://codepen.io/majdsalloum/pen/RwVKBbK

majdsalloum
  • 516
  • 1
  • 4
  • 17