1

I'm trying to add a signature feature to a web app. Users should have the possibility to sign with a pencil on a touch screen device. I'm using HTML5 canvas, JavaScript and PHP to get things done. I found a script for both mouse and touch support.

The drawing on the canvas works fine. When I submit the form, the scribble will get cropped properly (so it seems). But the generated base64-png data (saved in a database) is an empty image.

Here's the JS code I'm using:

var canvas, ctx;
var mouseX, mouseY, mouseDown = 0;
var touchX, touchY;
var lastX, lastY = -1;

canvas = document.getElementById('scribble-canvas');
ctx = canvas.getContext('2d');

canvas.addEventListener('mousedown', scribbleMouseDown, false);
canvas.addEventListener('mousemove', scribbleMouseMove, false);
canvas.addEventListener('mouseup', scribbleMouseUp, false);

canvas.addEventListener('touchstart', scribbleTouchStart, false);
canvas.addEventListener('touchend', scribbleTouchEnd, false);
canvas.addEventListener('touchmove', scribbleTouchMove, false);


function drawLine(ctx, x, y, size) {

    if (lastX == -1) {
        lastX = x;
        lastY = y;
    }

    ctx.strokeStyle = "#000000";
    ctx.lineCap = "round";
    ctx.lineWidth = size;

    ctx.beginPath();
    ctx.moveTo(lastX, lastY);
    ctx.lineTo(x, y);
    ctx.stroke();
    ctx.closePath();

    lastX = x;
    lastY = y;
} 


function scribbleMouseDown() {
    mouseDown = 1;
    drawLine(ctx, mouseX, mouseY, 3);
}


function scribbleMouseUp() {
    mouseDown = 0;
    lastX = -1;
    lastY = -1;
}


function scribbleMouseMove(e) { 

    getMousePos(e);

    if (mouseDown == 1) {
        drawLine(ctx, mouseX, mouseY, 3);
    }
}


function getMousePos(e) {

    if ( ! e) {
        var e = event;
    }

    if (e.offsetX) {
        mouseX = e.offsetX;
        mouseY = e.offsetY;
    }

    else if (e.layerX) {
        mouseX = e.layerX;
        mouseY = e.layerY;
    }
}


function scribbleTouchStart() {

    getTouchPos();
    drawLine(ctx, touchX, touchY, 3);

    event.preventDefault();
}


function scribbleTouchEnd() {

    lastX = -1;
    lastY = -1;
}


function scribbleTouchMove(e) { 

    getTouchPos(e);
    drawLine(ctx, touchX, touchY, 3); 

    event.preventDefault();
}


function getTouchPos(e) {

    if ( ! e) {
        var e = event;
    }

    if (e.touches) {

        if (e.touches.length == 1) {
            var touch = e.touches[0];
            touchX = touch.pageX - touch.target.offsetLeft;
            touchY = touch.pageY - touch.target.offsetTop;
        }
    }
}


function crop(canvas) {
    var w = canvas.width, h = canvas.height;
    var pix = {x:[], y:[]};
    var imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
    var x, y, index;

    for (y = 0; y < h; y++) {
        for (x = 0; x < w; x++) {
                index = (y * w + x) * 4;
 
             if (imageData.data[index+3] > 0) {
             pix.x.push(x);
             pix.y.push(y);
            } 
        }
    }

    pix.x.sort(function(a,b){return a-b});
    pix.y.sort(function(a,b){return a-b});

    var n = pix.x.length-1;
    
    w = 1 + pix.x[n] - pix.x[0];
    h = 1 + pix.y[n] - pix.y[0];
    var cut = ctx.getImageData(pix.x[0], pix.y[0], w, h);

    document.body.style.opacity = 0;

    canvas.width = w;
    canvas.height = h;
    ctx.putImageData(cut, 0, 0);
    
    return canvas.toDataURL();
}

On the PHP side I just pick the data returned by the crop() function and save it to the database.

What I tried so far is to strip off the touch support to see if it works just on mouse devices.


Additional code as suggested by user traktor:

The following jQuery code submits the form with the image data to the PHP script:

$(document).on('click', '#scribble-submit', function(e) {

    e.preventDefault();
    var btn = $(this);
    var data = crop(document.querySelector('#scribble-canvas'));

    $.post('/scribble/ax/ins_scribble', $('#form-scribble').serialize()  + '&data=' + data, function(ans) {

        window.location.href = '/' + btn.data('redirect');
    });
});

The PHP part looks like this (using the CI3 framework):

$post = $this->input->post();

$this->db->where('id', $id);
$this->db->update('personen', ['signatur' => $post['data']]);

echo TRUE;

So far, jQuery and PHP works fine. The data is successfully inserted into the database. The LONGBLOB field contains this code:

data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMQAAAC3CAYAAABNEPEEAAAAAXNSR0IArs4c6QAAAERlWElmTU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAA6ABAAMAAAABAAEAAKACAAQAAAABAAAAxKADAAQAAAABAAAAtwAAAAB1wSLaAAAYVElEQVR4Ae2dCZQeVZXHmyUhIYQ1gBAgHSDsCAxbWNPsO4ZBBBkwERxQcEARUUdQdJyj6DjA0VE2YdhBhs2ASAhDwo4gy4GwBmggILIZgbAKM79/893O7er1W r7arn3nH /W  reu/ef71Xb63qhdpCssbAMhg0CowBoyuh4iaDYeAeUI0szcnt4P9AJ/gbGEiGer6dN5/EZoG7wGvgzyC3slBuLe9tuArRSGAFSjdsCWA3bmv0RcFsMFih4JRuWQVNUIGaC14E1YpsWA8sAp4DSmtxsGQlXKoSynbFy 48yrsY/VfwPHgDqLKsAN4G/wVuAJmWVlUIFYC9we5AN78TJAvpROJUcPp7IlpBVwFSQV8ebABCssvAFzDtwuya19aWZoVYGceXBWPBumAXoEKsp8Z4sBoYDkL6Z AFfnoJPANWAu B/h4Q/NSn2IMjrS6TunXLgTXAYC2bWtf/BTeCaeBNkClpZIVYGM9EyilAXYyRoFVPbBWieeDlSvgB4afAh2A2SLZGRPUrjewyfUQufwQq6OpGqIvhQxV4HVsou/MievipQowB6goqVHnYAWwC1HXycg4Hvwb3 8hW6/VWiHE4sBFYC6wH9gbqugxVHuDE50AnSBbSicQN1mVS/ipUtwEVNPVbld7HICQ7DKgFOQkcmzDpKY6fANcCVZDcip4AVwENnlRo 8M7/Canp4PbwXXgSLAlEEkh5WFgPK7uB84CyfKibuAIkEuZjNV3AjX/Scfs Ap U8uhZjQkGPAMqFulnoTGE1ZeFP4eHAByI8OxdEdwA/COSL8UfA3sDDT9GRIMDMbAlzlBPYdXgZUnTR5MApmX1bDw6 A YMYrVK0OCQbqYeBfuNiXKc1CfgNktku9Lcb9DmiGxgx/EP1woBmYkGCgHgZW5eKDgR6uVr7sYauylzlJGipjJ2XOyjAo7wzshAO QkifAZbKkmMrYoymxczQu9HXz5KBYUuhGNgcbzQbaeVNocYai4GWywpYcDzwgx7NHIUEA2kysB2JPwx8pTgqzQyHmrb6b96oY4Z6YZwXDNTJwIZc/9/Ayp/WuzYGLZVvk7sZpMW1kGCgmQzsQGZW/hQ ALQG1hLR2EEbsMygE1piRWRadgb2hYCbgZXDa9C1s7mpYmOHl8nVDFm7qRZEZsHAAga iGrlUOFdQFP TZPPkpM3IMYOTaM MuqDAe2BOg68BqxcXt/HealFaQOeZawZppBgIAsM3IQRVi470XdvllHaeWgZq3KEBANZYOAIjHgMWNl8E33dtA0bSwbqo1mm/5R2hpF MFAFAxtwru86nVjFtTWd2sFVflv3hJpSiYuCgfQYUCWwB7YqhypJKjKCVH8ILDNt6AsJBrLGgLpJ6i5ZOX0cXd2phss4UtR dMtIc8AhwUAWGdCAuhNYWU3l4a1MLIMP0UOCgSwzoKlXK6 z0dsbYezCLpFxTo/ZJUdGqJlkQBXi7Ypl sDFXo228uckaDXuB41OPNILBlJg4GjStDL7CnpHvXn4FkI7DE003xsSDGSdAbUSeoFNsjzYvEtrwJ/9ScO/97BOA9KMJIKBZjDwTTKxVkIPcr22ULdcQAqW6Bz0JetOMRIIBprDQAfZ GlYleW65QZSsApxed2pRQLBQHMZuJDsrPyqcnTUmr2NIfQtUZN7TYkwGMgJA2dipxboJKNBzWMJqxCjupL65M9sp4caDOSBAX2Y4FxnaAd6uzsesqoKIWjPuUnyo8MWH2EwkGUG1LPRxJBkT7BXl1bDHw2g/aBkzRrSiEuCgSwwcDRG2FiipnUJtQ6LAfW7TGz1z44jDAbywsD1GFr3uoS6S1arFNq4Ii8khJ3BgGeg7nUJLcJZhXjDpxx6MJBDBjqw2Q8BLqjWh824wCrEC9VeHOcHAxlkoOZ1CXWPRjiHooVwZISaWwZqXpdQhRjl3J7v9FCDgbwyUPO6hCrESOf1PKeHGgzkmYF7Mf7VigNVrUvoyxo2hrgizwyE7cFAgoGjObayPaR1CbUQi7tE3nJ6qMFA3hmoel1CFWI95/USTg81GMg7A504MNM5cRj6gO9LqEKs5S5YxumhBgNFYEBjCev5aM1t6kBOqUIs4k7QR8pCgoEiMTATZ6Y5h7ZHX90d91BVIea6GK 76FCDgVwzcB7W/6Xigb5G2V7RewVRIXpREhEFZGAGPs1yfv2D03uoqhAhwUAZGNBYwmRzU5JhVIgkI3FcVAbud47pA8n6RmwviQrRi5KIKCgDnfh1R8U3LTX0Of0aFaLCUASFZ0Af8j7feTkFfXV33KVGhUgyEsdFZkCb/p6qOLgN4cSks1EhkozEcZEZ0Jf93nEOHuz0LjUqRJKROC46A36tbZWks1EhkozEcdEZuMQ5OAq9x2xTVAjHTqilYOBuvLTZpjXRe8w2RYUoRRkIJx0DA842RYVwTIVaGgY02/RoxVvNNrVX9PgGkxERYakY0GzTI87j7r1N0UI4VkItFQN97m1atFQUhLP9MTCMH8aD5cFOYFMwHGiK8kWwNLA3K19H14tk hcK2kF6MXgF5E383qZJGL8z0K7YtpOBvYgtPaQcDCyOm uDq8AD4AVg5aCaUF/JUxp5k9Ux Elgvp4nB9RCjJVSEa9bXITFYGAcbuj at59QgW7EPoPXXNYtej6/cCz4Aygp wTIOsfzdZs05 AuJB0lX1VCL9a5/Wus JP7hnYEw9UYDVw7B489uPVXOJfA/pgnd5D1vGLoK8u03LEq4UZCSTt4CdSkGuA8sy6aE3ioIqRqgtdok91WLMxvRIXQb4ZaMd83egfAz2t7f72FX7A778Fa4BhoBoZw8lXgmS6HxOnSpH1r7hs72zXrFPX9wVOc5GXoofklwEVaG1Y0/9ISBZSHWsgfCc4BajCbAG6CgFhraJCr0H4t8CzwOer CyLXhTy9nZV4CNdpD4SG5IvBlbFXBVutfR6ynUCf5NNP4P47UDa8iwZWJ6qJFkWzZaZrQq7hgxfcJEXZ9n6sK0HA q7bwZ CvxN9fq5/PZ5oL5 s0SVwGx4tlmZ1piPxg1/cfZ2faPssy7i6hoTjsuax8DWZHU4 BV4Gljh8 HpxGsdoRWibpK35QaONT7JqszBMLNXXci2vV3EDEWEZJKBXbHqIvAUsBvow07irwN7ALUerRL1w/Vg1cDa7JvSKmOGkK/GVGZnh87fwUXcoYiQTDGwJdZ8GWhl1W5cMtw9UxZ/Ysx9zl5V1G0yaKNM gMwPicrYisX8YgiQjLBwCismATUKtgNs3AucRo77AtGgCyKCtczwNush2/W5BIMMhsPkXEbuQj1SUNay4BmjfTE/wl4EtjNsvA84nTP8iC7YKTZrVAzYVkTde/Mxp/JuHVcxOuKCGkZA0uS8zRgN8jCF4g7FaiA5Um0m3o6MD86M2i8xs1mn7p2bXoiWcQ76K0ckMmesoq6GBrgfQTsfliobm1eZR8MNz8Ufh10LYBlxCHNgpl90ttWcBH6YVlFhjSNgeHktCPwN0b3Qa3CEWApkGdR4VclsEKnUJUkK3I2hpht0rtqq0UonKDIkKYwsBq5qLD4GRndA3UziiadOGTlrKtrkhEHT3Z2nayVuvfBa2AMkGh2IyR9BnYli68CrRvoPkgeAr8AN qgYDIbf8ZVfFKvJJOiQc H4BVn3XJODzUdBjpI9iSgroNVBtS2Y8FvgKZViyb JSK1FN7vzPiqCiF57pOg669mOkLSY0CV4HSwrcviHvQNwCwXVzT1fueQtki0u PMqFYh3nUWre30UBvHgHidWsGnXbKa9lO8uhRFlkdx7lnnoNZZMifWbPmp1sHeqsqcEzkxaE/s/M ErcdxrPWFMojGqk A8RVnMzlWtQrxV3dHbHDtokKtg4GjufYzQAugXjTDpKnVMom6hrtXHJaeObEu00xn2YpOD7U Bjbi8ilAK8xaAJVoilWLcGWrDPI982ItxO3O0rXQlwevurhQq2NgLKerVdgfbOwu1bTq5u64bOpE57DXXXRrVasQWof4G1gKKG40iAoBCTWI1he AtQ1GFG5/i3CO8FFleOyBppuNfG6xbUiXNpl6vUeW3W3dCeFOjQG1LIeCNQ31s322I/jkLa20yDBeJGeBfGbD6dbCyHDngc2A6CWIqQ6Bk7ldM0kedF44Shwr48ssd7ufPe6i266Osrl IENqhX3uvtBY4iQoTGgbQjXgj0Sp1/G8aEgKsMCYnw3yesLzmi Nt9lOc23EC 7H2LHqyNjEFUVYV93zlPo3wVXuLhQP2Gg0xHhdRfdVFU9IesVKeMHfYXQwNok1iKMiYFDDZy1J8lETxuNJUJ6M6CWdH0Xva7TW6VuRsafcpnP6a/LJONDBmZAlUErz2u402Y5PdSeDGzPoX/jz28X6nlm845UKZeoZKdurx82dH3QymYAfts8m3KZk/Yi3QKML4UvAb9hj8OQCgPthNrLZHy9U4lvZaClhQucTV9KGqO sBl8R/LHOO5mQC3DzcC4Uvg08N0BDkMcA1PRPV/a09RqaceAj4DZ1WsPn/pT9uNj6CG9GeirZRBXURl6c VjpnJgZUthFl6A6nA2aeG0S/yg k2LJFS/WH2rt11c2VVVBn0YusMRcSv63qCbUPdbqAsYmIn6S7AmeA9cD1otKzoD7nJ6t6pla1 LN n JRSNDW5J8BMtQ77LxQnufp5prvhZJrUGfuywkp1U8nAM/h8LOhwPP0ffAsx2caHmiwFtvzfR2lGfog1o1kpc3ecZ5YucgsvzHC Po2uGIiTfDFyD VbWu/ea RZC7vnV6lXy7W9DrF GVDR3rhVNyYNA8 kxZhAb RXdTz8RoinzPmU7Yq3WaGpMBaKsohXnE8EzwDjZtaxkFMzvg/FHg3u7r8v155/2dWi2yU70 zz6u6ao8Qc6HsTHd4C1FEX1uSx XYGjVsbvH8zpOe5kza6UUdQS3AOMtBllJKGgPm NXxpE2709ZzA/f 9OnjzYyQX8Xa3A5Y4DEXdAAf0sq0uHu3s7F30jT0RyUK3f/MB6ZX9ySXS1Djs4Xy9DVxMbkn8GRuLCps6NS9AfcsdtfVUI1RoTvzXW4ooeamJh YqTWmf4YdEdLpF/mlnazfl7u9O71L4qxEvupLK1EIfi 1bOf1UGrUiH5J BVXHhc2B158p0p/er7s8vNuAo02ByS/x 0vmu5tSvZnIYkmMGOrDdyrVC7VoekkzkLLvwYfSFhnRV/k/Sa5/m98f5dyc8SDDg9y7dmfit 7CvLtMb3b9 8kVq60 76MKpG PRPs4rVY6Q4jAwDFe2du5c5fRB1VGcMQ/Y0/KQQa/I9wmbYP6vwPvAfB6Xb5fC gQDB3Ps1x56TLUmzu3z8FZirXCoL11kOQznzNe30PcqsrMl9K0dn/3a2ukca/q1Kjmbs62QdKKvV9XV Tl5V0y9D5ivg65a5se1sLTCwEHu/uo D6 FGS1M6evUVlA U0siGb9mEey7yPkoX/fIuM1hXvUM/JhLrByfW/3lC644yyX07wuiC6N14MnTwMg6vzCehSPGgD4tql3bdo8/bz/UEv6zS6jPd05rSTRD13zV deJvmGGbAtT6mdAuyyuBFYZFGqlumZRAbHE5qNrwa4osjKOqEUw/75fFMfCj24GjkDTa9F2j8/s/qVGZTTXneISvBl9yRrTytplGhNpRsnI2ilrBoY9dTGg7Rm/A3Z//4S Tl0pVi5em/AjYAkf24hEW5yGFuEudz5d2mJ7IvvGMrAFyWlq9TVg5XbbRmah2mUJX93IhFuUlgZW5s/z6EWdUm4RvS3P9hh3fz9E/yIY8lTrwkMw/0V3zopOz6O6GEZrE5/JdJRH7SDC3DOgWaWdnRdaVzoPfODi6la/TQr2RM37VuhN8eVx58/n6mYnEsgKA7q3GudaWdWAelIaxq3rMnkHfZ80MmlCmuPJ4z AEaZwiSbkG1mkz4Ame74H/L3VTGIqsgypPgAss0tSySX9RHdzPsiXIe HT9 0yKFOBrT/zG/eU VIVfzepvfI6degPdUcG5/490nSKrWa1pBiMKAey03A7q1CLcqlKpq2ehn4TKemmmNjE1 J5PwT5KjGJh ptZCBU8nbyuW96NrO3xS5llwsY4VTm5JrYzLRCyF6C87sb29MspFKCxlYjby1/eZ YPdVq9N1yaJVXD2Nc1WoRgI9bWeCPMg4jNwMLFQx9jLCzooeQX4Z0H/8 YUz/1z0Ge441H4Y2Jd4e4IoDMk/A/ IC3cBu695Xw5o6h053hF3aVNzjszSYEAtwyxglUHhoWlkVMQ0tRKvptTI 1oRnSyRT1vj62nufuq XlEi/ t2dQIpPAmsQvhl/boTjwSaysD25HY9sHup8BbQ0I17pFdo0e7cvwMjcVShvS2uc7vg2lnuPup /gwMAyFDZGAc52le2irDkUO8Lk7LFgPasPcwsPuo8ERgs4aoIUNhIGaXhsJSts ZjHnJD0EclG2Ts2tdzC5l994MxbIDOEmbSX3LoC5wqlLNwlyqhjQ4cc0u Rd/7mlw pFcegy0k7QGylpr0CKwibZ3a1U6pAYGYnapBtIycslh2OFbBemKC6mDgZhdqoO8Fl2q926 Am4EvkKMaZE9hcl2HJ7E7FL bucxmOwrwlscxxuNDbiP30wQ24AkI4kUGdBWDN2z24BViD nmF/pkvbb1GMQlv3brzUFqwgKHwJae2iJFHGWabRjcq7TQ80WA9tgzm5gb2fWneiKD2kgA38gLXvi/FsD042kGsfAMiTlv5Ch 3UdmNS4LCIlY8BXCD2BQrLDgGaMpoD7wEfAHlyXo4ekxMCjpGtEb5FSHpFs9QxswCX/CjRgtvuj8C6gRbeQFBhYnTT9xxBWTSGPSLI6Btbi9APBOcBXBOnqJoWkyMBE0jbS9f73kinmFUkPzoDGCslZJN2fS8GuYGkQkiIDU0jbKsQdKeYTSQ/MgI0VLua0Z4DdE4XHAb9HicOQtBjQrJKR/6O0Mol0B2Sgv7HCDK7SDtaQJjGgt6f B1iF0G7JkOYxMNBY4TvNMyNyMgaWRXkdWIXQloCQ5jAQY4Xm8FxVLnr/wSqDQrUYIekyEGOFdPmtK/WtuNoqxJy6UoqLh8KAvp6uLuo8YLwrjLECJGRB9P6t3RjtiQlpPAPDSVLTpSeA5Iv/4j73Y4Uibe6bwA0xedWUCBvGwIaktCOYCjYGXh7k4Fvgjz4y9NYycD3ZWwtxa2tNKUzuvkW40vFrPCs8G6xTFI L1EL4QfR7RblBLfRjoBZhJnZp28Ud4G5QGClShdCmvl0qd0Z6SPUMqEXoAOoSbQn6WsuZSvxVQK94Fk6KVCHmubvjdRcd6gAMlLJFSPJRpAqR9C2OB2dALYK dLETGAc DZIylYjCtghJZ6NCJBkpz7G1CHrBf2zC7ZkcF3KMkPCz12FUiF6UFDqi9GOEwe5uVIjBGCrO79YiTMUlDZq9aGV/NpgGzvc/lE0vUoXwzb7Xy3ZPvb/RIng2hqAXqUKs4vz1uosulTpQizATJko5RhisBBSpQnzgnF3C6WVSo0Uo090exNeT N22FOjbrmWTvXD4YvAcMB4svIW4bwC9cx5SEgb0yRkrAE g64WhIov82wR8CfwSPA3Mfx9OIX40CCkZA PxV3uYrDAkZ1KKQoe6RRoffA94f81vhZo1ihYBEsouj0CAFYxb0IvSRdDndLSafBTQf N8CpifPpxP/BlgfRASDLRdDQf6HpMVklkF4GQPfDgdvOT8Mv8UaszwU6AxxBogJBjowYAqgRUYVY77wOQeZ2T3YASm6eMIGhfo/Y6HgMZD5k8yPIDfQoKBARlQN0mVwrcUz3CsreELgyyKFhIPAb8BL4Bkwbfjm/jte0CtRnzsCxIaLQs1OsEMpXcDtuhFeC8zONCClLYpqOC9AV4FzRR9b3YloLfM1MVZE2wOVgOLgb5EXcHvgsf6 jHiGsdAkSuECtu2QN0K9a/7kueJfB10gjOB unqovhFPg5rFj3FVQGWA5uCbcCEik7QrzzOL 8C2SO7bgPxjgckpC1FrhDGnQrh5WCsRQwSvs3vmtNXRVEhHAPeB3oLr69CqXRXAZoO1bWqTCuApcDKQDNE/T35 amH/Iij88GcHrFx0DQGylAhROYOQPPyS4M3gQrwSkCFvRXydzLVgPn2SqgK CCQbSEtZKAsFaIvilUp1J05HuhJLi70HnEaMp9E9dRXq6MB/oUgvgwCCVmTMleI5L1Qt2Y0sO7Oiujq73eAYWCwLpOe hqsa5pXT3oV/rcqoSrEeyAk4wz8P tCAtne45WMAAAAAElFTkSuQmCC

Using this data as an image source for the img tag results in the empty image.

blackpool
  • 11
  • 4
  • Does this answer your question? [HTML5 Canvas toDataURL returns blank](https://stackoverflow.com/questions/31193418/html5-canvas-todataurl-returns-blank) – winner_joiner Jul 11 '22 at 06:24
  • The posted code works fine - function `crop` returns a valid data URL or a cropped signature when using a mouse. It helps to comment out `document.body.style.opacity = 0` when testing. to see results. The error must be occurring in code that has not been included in the postl. Please investigate where the data goes missing further. – traktor Jul 11 '22 at 07:00
  • Here's a data URL produced by your code for a single dot: `data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAAECAYAAACp8Z5+AAAAGklEQVQIW2NkgIBIKL2cEcpZBhWIwiqAogUAlaMEtwv5zoUAAAAASUVORK5CYII=` Feel free to hard code it when testing saving a data URL sent from the front end for storing on the server. – traktor Jul 11 '22 at 07:35
  • I edited the question and put in the rest of the code (jQuery for the ajax submit and PHP to save data in the database). I also added the data being generated by the code and that results in an empty image. – blackpool Jul 11 '22 at 10:32
  • 1
    @winner_joiner: I don't think it's the same problem. As far as I understood, the question you linked is to generate an pre existing image with a canvas. – blackpool Jul 11 '22 at 10:39
  • @blackpool okay, other possible angle, are you using `http` or `https` _(are you getting errors/warning in the browser console)_, since canvas and plain `http` might cause issues. – winner_joiner Jul 11 '22 at 10:45
  • @winner_joiner: It's all https and there are no errors or warnings in the console. I tried it with Safari and Firefox. – blackpool Jul 11 '22 at 10:54
  • I found the problem. It this jQuery line: ```$.post('/scribble/ax/ins_scribble', $('#form-scribble').serialize() + '&data=' + data, function(ans) {``` Combining the serialize function and an addition of &data= didn't work. So I changed it to: ```$.post('/scribble/ax/ins_scribble', { data: data }, function(ans) {``` – blackpool Jul 11 '22 at 13:19
  • Do you mean you can now use the (stored) image url to create a successful image? If so please answer you own question and accept it to show it has been solved (note no reputation points are awared for own answers). If you can't and there is still an error, the LONGBLOB field contents in the post contains spaces which are not valid in [base64 encoding](https://stackoverflow.com/q/13195143/5217142). – traktor Jul 11 '22 at 23:43

2 Answers2

0

The problem is this jQuery line:

$.post('/scribble/ax/ins_scribble', $('#form-scribble').serialize()  + '&data=' + data, function(ans) { 

Combining the serialize function and an addition of &data= didn't work. So I changed it to:

$.post('/scribble/ax/ins_scribble', { data: data }, function(ans) {
blackpool
  • 11
  • 4
-1

You code runs fine:

var canvas, ctx;
var mouseX, mouseY, mouseDown = 0;
var touchX, touchY;
var lastX, lastY = -1;

canvas = document.getElementById('scribble-canvas');
ctx = canvas.getContext('2d');

canvas.addEventListener('mousedown', scribbleMouseDown, false);
canvas.addEventListener('mousemove', scribbleMouseMove, false);
canvas.addEventListener('mouseup', scribbleMouseUp, false);

canvas.addEventListener('touchstart', scribbleTouchStart, false);
canvas.addEventListener('touchend', scribbleTouchEnd, false);
canvas.addEventListener('touchmove', scribbleTouchMove, false);


function drawLine(ctx, x, y, size) {

  if (lastX == -1) {
    lastX = x;
    lastY = y;
  }

  ctx.strokeStyle = "#000000";
  ctx.lineCap = "round";
  ctx.lineWidth = size;

  ctx.beginPath();
  ctx.moveTo(lastX, lastY);
  ctx.lineTo(x, y);
  ctx.stroke();
  ctx.closePath();

  lastX = x;
  lastY = y;
}


function scribbleMouseDown() {
  mouseDown = 1;
  drawLine(ctx, mouseX, mouseY, 3);
}


function scribbleMouseUp() {
  mouseDown = 0;
  lastX = -1;
  lastY = -1;
}


function scribbleMouseMove(e) {

  getMousePos(e);

  if (mouseDown == 1) {
    drawLine(ctx, mouseX, mouseY, 3);
  }
}


function getMousePos(e) {

  if (!e) {
    var e = event;
  }

  if (e.offsetX) {
    mouseX = e.offsetX;
    mouseY = e.offsetY;
  } else if (e.layerX) {
    mouseX = e.layerX;
    mouseY = e.layerY;
  }
}


function scribbleTouchStart() {

  getTouchPos();
  drawLine(ctx, touchX, touchY, 3);

  event.preventDefault();
}


function scribbleTouchEnd() {

  lastX = -1;
  lastY = -1;
}


function scribbleTouchMove(e) {

  getTouchPos(e);
  drawLine(ctx, touchX, touchY, 3);

  event.preventDefault();
}


function getTouchPos(e) {

  if (!e) {
    var e = event;
  }

  if (e.touches) {

    if (e.touches.length == 1) {
      var touch = e.touches[0];
      touchX = touch.pageX - touch.target.offsetLeft;
      touchY = touch.pageY - touch.target.offsetTop;
    }
  }
}


function crop(canvas) {
  var w = canvas.width,
    h = canvas.height;
  var pix = {
    x: [],
    y: []
  };
  var imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
  var x, y, index;

  for (y = 0; y < h; y++) {
    for (x = 0; x < w; x++) {
      index = (y * w + x) * 4;

      if (imageData.data[index + 3] > 0) {
        pix.x.push(x);
        pix.y.push(y);
      }
    }
  }

  pix.x.sort(function(a, b) {
    return a - b
  });
  pix.y.sort(function(a, b) {
    return a - b
  });
  if (pix.x.length == 0) {
    return '#';
  }

  var n = pix.x.length - 1;

  w = 1 + pix.x[n] - pix.x[0];
  h = 1 + pix.y[n] - pix.y[0];


  var cut = ctx.getImageData(pix.x[0], pix.y[0], w, h);

  //document.body.style.opacity = 0;

  canvas.width = w;
  canvas.height = h;
  ctx.putImageData(cut, 0, 0);

  return canvas.toDataURL();
}

document.getElementById('scribble-submit').addEventListener('click', function(e) {

  e.preventDefault();
  var data = crop(document.querySelector('#scribble-canvas'));
  document.getElementById('scribble-result').setAttribute('src', data);
});
<html>

  <body>
    <canvas id="scribble-canvas" style="width:200; height:200; border: 1px solid blue;"></canvas>
    <br>
    <button id="scribble-submit">
      Run
    </button>
    <br>
    Output:
    <br>
    <img src="#" id="scribble-result" />
  </body>

</html>

You either send the wrong data or use different data to render.

If you are saving into a .png file, you may want to strip out the initial data:image/png;base64, before turning that into bytes.

Noman_1
  • 473
  • 1
  • 6
  • 17
  • The code posted supports a canvas being used to display the signature of somebody signing their name on a touch sensitive screen or touch pad with support for mouse emulation or touch events. There is no image involved to load or process. – traktor Jul 11 '22 at 07:11
  • @traktor Edited the answer with a working snippet – Noman_1 Jul 13 '22 at 10:49