javascriptnode.jswebcam.js

Can't figure out how to fix this: "Uncaught TypeError (webcam.snap is not a function)" <-- javascript


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;
    },

Solution

  • 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