Struggling to save a signature created with an HTML5 Canvas to the database

I've been on the hunt for a reliable signature capture script that can save signatures to MySQL, and I finally found one that fits the bill. However, there are two issues that need addressing:

  1. The canvas doesn't clear the signature when the clear button is pressed.

  2. The image isn't submitted to MySQL when the save signature button is clicked.

Despite reaching out to the developer without any response, I'm optimistic that someone here can help me with this code conundrum.

Here's the HTML snippet:

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    <html xmlns="http://www.w3.org/1999/xhtml">

    <head>
    <meta content="text/html; charset=utf-8" http-equiv="Content-Type" />
    <title>Signature Pad</title>
    <script type="text/javascript" src="jquery.min.js"></script>
    </head>
    <body>
    <center>
        <fieldset style="width: 435px">
            <br/>
            <br/>
            <div id="signaturePad" style="border: 1px solid #ccc; height: 55px; width: 400px;"></div>
            <br/>
            <button id="clearSig" type="button">Clear Signature</button>&nbsp;
            <button id="saveSig" type="button">Save Signature</button>
            <div id="imgData"></div>
            <div id="imgData"></div>
            <br/>
            <br/>
        </fieldset>
    </center>
    </body>
    </html>

On top of that, we have the necessary PHP and scripts:

    $(document).ready(function () {
    /** Set Canvas Size **/
    var canvasWidth = 400;
    var canvasHeight = 75;

    /** IE SUPPORT **/
    var canvasDiv = document.getElementById('signaturePad');
    canvas = document.createElement('canvas');
    canvas.setAttribute('width', canvasWidth);
    canvas.setAttribute('height', canvasHeight);
    canvas.setAttribute('id', 'canvas');
    canvasDiv.appendChild(canvas);
    if (typeof G_vmlCanvasManager != 'undefined') {
        canvas = G_vmlCanvasManager.initElement(canvas);
    }
    context = canvas.getContext("2d");

    var clickX = new Array();
    var clickY = new Array();
    var clickDrag = new Array();
    var paint;

    /** Redraw the Canvas **/
    function redraw() {
        canvas.width = canvas.width; // Clears the canvas

        context.strokeStyle = "#000000";
        context.lineJoin = "miter";
        context.lineWidth = 2;

        for (var i = 0; i < clickX.length; i++) {
            context.beginPath();
            if (clickDrag[i] && i) {
                context.moveTo(clickX[i - 1], clickY[i - 1]);
            } else {
                context.moveTo(clickX[i] - 1, clickY[i]);
            }
            context.lineTo(clickX[i], clickY[i]);
            context.closePath();
            context.stroke();
        }
    }

    /** Save Canvas **/
    $("#saveSig").click(function saveSig() {
        var sigData = canvas.toDataURL("image/png");
        $("#imgData").html('Thank you! Your signature was saved');
        var ajax = XMLHttpRequest();
        ajax.open("POST", 'http://www.your-domain.com/folder/post-html.php');
        ajax.setRequestHeader('Content-Type', 'application/upload');
        ajax.send(sigData);
    });

    /** Clear Canvas **/
    function clearSig() {
        clickX = new Array();
        clickY = new Array();
        clickDrag = new Array();
        canvas.width = canvas.width;
        canvas.height = canvas.height;
    }

    /**Draw when moving over Canvas **/
    $('#signaturePad').mousemove(function (e) {
        if (paint) {
            addClick(e.pageX - this.offsetLeft, e.pageY - this.offsetTop, true);
            redraw();
        }
    });

    /**Stop Drawing on Mouseup **/
    $('#signaturePad').mouseup(function (e) {
        paint = false;
    });

    /** Starting a Click **/
    function addClick(x, y, dragging) {
        clickX.push(x);
        clickY.push(y);
        clickDrag.push(dragging);
    }

    $('#signaturePad').mousedown(function (e) {
        var mouseX = e.pageX - this.offsetLeft;
        var mouseY = e.pageY - this.offsetTop;

        paint = true;
        addClick(e.pageX - this.offsetLeft, e.pageY - this.offsetTop);
        redraw();
    });

});

Last but not least, let's take a look at how we handle the MySQL portion:

    <?php
    if (isset($GLOBALS["HTTP_RAW_POST_DATA"]))
    {
    $session_id = $_SERVER['REMOTE_ADDR'];
    // Get the data
    $imageData=$GLOBALS['HTTP_RAW_POST_DATA'];

    // Remove the headers (data:,) part.
    // A real application should use them according to needs such as to check image type
    $filteredData=substr($imageData, strpos($imageData, ",")+1);

    // Need to decode before saving since the data we received is already base64 encoded
    $unencodedData=base64_decode($filteredData);

    //echo "unencodedData".$unencodedData;
    $imageName = "sign_" . rand(5,1000) . rand(1, 10) . rand(10000, 150000) . rand(1500, 100000000) . ".png";
    //Set the absolute path to your folder (i.e. /usr/home/your-domain/your-folder/
    $filepath = "htdocs/alpha/site6/signature/images/" . $imageName;

    $fp = fopen("$filepath", 'wb' );
    fwrite( $fp, $unencodedData);
    fclose( $fp );

    //Connect to a mySQL database and store the user's information so you can link to it later
    $link = mysql_connect('localhost','root', 'pwd') OR DIE(mysql_error);
    mysql_select_db("trial", $link);
    mysql_query("INSERT INTO customer (`session`, `image_location`) VALUES ('$session_id', '$imageName')") OR DIE(mysql_error());
    mysql_close($link);
    }
?>

Answer №1

Unfortunately, I can only offer a partial solution as I am not able to assist with the PHP aspect.

To view the code with some corrections, I have created a jsfiddle link:
http://jsfiddle.net/AbdiasSoftware/M8pzB/

You will notice that the clear button functions as recommended (although in my comment, I referred to ctx instead of context).

In addition, I have eliminated a duplicated ID'ed div (imgData) from the HTML.

The clear function:

$('#clearSig').click(
    function clearSig() {
        clickX = new Array();
        clickY = new Array();
        clickDrag = new Array();
        context.clearRect(0, 0, canvas.width, canvas.height);
});

I have included a debug tag in the HTML to demonstrate that everything is operational until you reach the point where the data needs to be sent to the server. Regrettably, I am unable to test this portion, so I suggest beginning debugging at that stage.

Furthermore, I have implemented URI encoding for the resulting data-url from the canvas, which is essential.

The MIME transfer type can potentially be text/plain, given that what you are transferring is a string. Utilizing jQuery allows you to utilize the built-in ajax-transfer function:
http://api.jquery.com/jQuery.ajax/

When working on the PHP side, you'll need to decode the URI, remove the header of the data-url as you normally would, and then base64 decode it if you intend to store it as binary. Keep in mind that within MySQL, it will need to be stored as a BLOB (distinct from HTML5's Blob object).

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

A step-by-step guide on utilizing links for downloading PDF files in Vue/Nuxt

Having some trouble opening a PDF tab in my Vue application with NUXT and Vuetify... any suggestions? I attempted to use: <a href="../static/docs/filename.pdf" target="_blank">Download PDF</a> I also tried using <nuxt-link> but it didn ...

Saving a MongoDB document within an array in Node.js and retrieving it

I am working on retrieving specific documents from MongoDB using Node.js and storing them in an array. const getStockComments = async (req) => { const stockname = req.params.stockName; var comments = []; var data = []; const stock = await sto ...

"Step-by-step guide on adding and deleting a div element with a double click

$(".sd").dblclick(function() { $(this).parent().remove(); }); <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> <table width="750" border="0" cellpadding="0" cellspacing="0"> <tr> <t ...

Identifying modifications to the content of a text area

Is there a way to determine if the value in my textareaId has changed due to JavaScript? For example: $("#buttonID").on("click, function(){ $("#textareaID").val("lorem ipsum"); }); $("#textareaID").change(function(){ //not working }); $ ...

Is it necessary for a writable stream in NodeJS to wait for the drain event before writing again automatically?

Below is the code I am working with: const { Writable } = require('stream') let writable = new Writable({ highWaterMark: 10 }) writable._write = (chunk, encoding, cb) => { process.stdout.write(chunk) } function writeData(iterations, writ ...

Emerald: Fresh alert for numerous attributes

After updating jade to the latest version, I started seeing a message in the console that says: You should not have jade tags with multiple attributes This change was mentioned as a feature here 0.33.0 / 2013-07-12 Hugely more powerful error reporting ...

Locating the dot character within regular expression challenges

Having difficulty replacing terms like "joe." using a regular expression. Look at the snippet below: var phrases = new Array("joe","sam"); sentence = "joe.id was here as well as sam.id"; for(i = 0; i < phrases.length; i++) { regex = new RegEx ...

Showing information to several classes using JavaScript

I am currently developing a "Gamebook Engine" that enables users to set a custom username. The user name is extracted from an input element with the id="setUserNameInput" and stored using the function setUserName(). It is then displayed or loaded into an e ...

What is the distinction between selecting and entering a date input value?

When a user selects a date, it needs to be immediately sent to the server. If they manually type in the date, it should be sent on blur. The issue arises when the oninput event is triggered for each keydown event, causing unnecessary server requests while ...

Filtering multiple rows in a table using Javascript

I'm currently working on creating a filter that can filter based on multiple inputs, with each input filtering in a separate column. Here is the JavaScript & code I am using: function myFunction(column, input) { var filter, table, tr, td, i, t ...

"Steps to retrieve the button's ID when using the onClick event in Reactjs

Currently, I am working with Reactjs and Nextjs. I am utilizing functional components instead of classes. Within a loop, I have buttons and I am attempting to retrieve the id of the button when clicked using "onClick". Unfortunately, I am encountering th ...

Increasing the identifier of duplicated input fields using jQuery

There is a specific section in my form that consists of a select box and three checkboxes, which needs to be duplicated on request. While I have successfully cloned the div and incremented its specific div, I am facing challenges in incrementing the checkb ...

Move the DIV element to a static section within the DOM

In my Vue app, I have implemented methods to dynamically move a DIV called 'toolbox' to different sections of the DOM. Currently, the DIV is positioned on the bottom right of the screen and remains static even when scrolling. My goal is to use t ...

Experience the classic game of rock-paper-scissors brought to life through JavaScript and HTML,

I've been working on a rock, paper, scissors game in JavaScript and I'm struggling to get the wins and losses to register correctly. The game keeps resulting in a draw even though I've checked my code multiple times. It's frustrating be ...

How to manipulate iframe elements using jQuery or JavaScript

Hey there, I have a simple task at hand: I have a webpage running in an iFrame (located in the same folder on my local machine - no need to run it from a server) I am looking to use JavaScript to access elements within this iFrame from the main page. How ...

Height of the Accordion List

I have a code snippet below for an accordion list that I put together. I initially set the height for all the heading boxes to be uniform, but it seems like box A is displaying larger than the others. Can you help me figure out what went wrong? Any suggest ...

In React's contextApi, the this.context object is constantly devoid of any values

sample.js export default class SampleComponent extends React.Component { static contextType= AppProvider; componentDidMount() { console.log('sample',this.context); } render() { return ( <AppConsumer> ...

Delete the tag that comes before

Is there a specific tag like: <p id="name" onclick="javascript:var ele=context(this);">sumtext here</p><br> <p id="name" onclick="javascript:var ele=context(this);">newtext here</p><br> <script ...

Loading Wordpress shortcodes conditionally

I am currently developing a WordPress plugin that includes a shortcode. This shortcode is experiencing slow execution (4 seconds) due to external API calls that are necessary as they depend on the user id viewing the page. During the development and testi ...

What are some tips for utilizing the "bottom-row" slot within the "b-table" component in bootstrap-vue?

I am working on a component that utilizes the bootstrap-vue b-table component. My goal is to create a bottom row that displays the sum of each column in the table. However, I encountered an issue where the bottom-row only fills the first column, leaving ...