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.
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) {
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.
Related
Following is my code. I have face issue when I drag a point. The problem is that whenever I drag point all the line connect to one point. I need to have draggable points in a html5 Canvas. But I have a supplementary constraint: the 2 points must be linked by a line. When I drag a point, the line must be dynamically drawn, and still linked to the 2 points.
let points= [];
let drag_point= -1;
let pointSize= 6;
let canvas = document.querySelector("#myCanvas");
let w = canvas.width;
let h = canvas.height;
var ctx = canvas.getContext("2d");
$("#myCanvas").mousedown(function (e) {
var pos = getPosition(e);
drag_point = getPointAt(pos.x, pos.y);
console.log("pos", drag_point);
if (drag_point == -1) {
// no point at that position, add new point
drawlines(pos.x, pos.y);
points.push(pos);
}
});
$("#myCanvas").mousemove(function (e) {
if (drag_point != -1) {
// if currently dragging a point...
var pos = getPosition(e);
//...update that.points position...
points[drag_point].x = pos.x;
points[drag_point].y = pos.y;
redraw(); // ... and redraw myCanvas
}
});
$("#myCanvas").mouseup(function (e) {
drag_point = -1;
});
function getPosition(event) {
var rect = canvas.getBoundingClientRect();
var x = event.clientX - rect.left;
var y = event.clientY - rect.top;
console.log(x, y);
return { x: x, y: y };
}
function getPointAt(x, y) {
for (var i = 0; i < points.length; i++) {
if (
Math.abs(points[i].x - x) < pointSize &&
Math.abs(points[i].y - y) < pointSize
)
// check if x,y is inside points bounding box. replace with pythagoras theorem if you like.
return i;
}
return -1; // no point at x,y
}
function redraw() {
ctx.clearRect(0, 0, canvas.width, canvas.height); // clear canvas
for (var i = 0; i < points.length; i++) {
// draw all points again
drawlines(points[i].x, points[i].y);
}
}
function drawlines(x, y) {
drawImages(x, y);
if (points.length > 0) {
var last = points[points.length - 1];
ctx.beginPath();
ctx.moveTo(last.x, last.y);
ctx.lineTo(x, y);
ctx.strokeStyle = "blue";
ctx.stroke();
}
}
function drawImages(x, y) {
var ctx = document.getElementById("myCanvas").getContext("2d");
ctx.beginPath();
ctx.arc(x, y, pointSize, 0, Math.PI * 2, true);
ctx.strokeStyle = "red";
ctx.stroke();
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<canvas
id="myCanvas"
width="1000"
height="1000"
style="border: 1px solid #d3d3d3"
></canvas>
.
See code below...
I refactored your drawlines and drawImages to be called independently not inside a common loop, in my code we draw all lines, then we draw all circles, that way we don't have to change colors back and forth all the time and prevents any overlaps of the lines over the circles, also another change is in the mousedown I call the redraw instead of drawlines.
Looking at your code educated guess the problem is in your:
var last = points[points.length - 1];
that seems very off, technically that is making the last always be the same
let points = [{x:10,y:10},{x:55,y:50},{x:100,y:10}];
let drag_point = -1;
let pointSize = 6;
let canvas = document.getElementById("myCanvas");
var ctx = canvas.getContext("2d");
canvas.onmousedown = function(e) {
var pos = getPosition(e);
drag_point = getPointAt(pos.x, pos.y);
if (drag_point == -1) {
points.push(pos);
redraw();
}
};
canvas.onmousemove = function(e) {
if (drag_point != -1) {
var pos = getPosition(e);
points[drag_point].x = pos.x;
points[drag_point].y = pos.y;
redraw();
}
};
canvas.onmouseup = function(e) {
drag_point = -1;
};
function getPosition(event) {
var rect = canvas.getBoundingClientRect();
var x = event.clientX - rect.left;
var y = event.clientY - rect.top;
return {x, y};
}
function getPointAt(x, y) {
for (var i = 0; i < points.length; i++) {
if (
Math.abs(points[i].x - x) < pointSize &&
Math.abs(points[i].y - y) < pointSize
)
return i;
}
return -1;
}
function redraw() {
if (points.length > 0) {
ctx.clearRect(0, 0, canvas.width, canvas.height);
drawLines()
drawCircles()
}
}
function drawLines() {
ctx.beginPath();
ctx.moveTo(points[0].x, points[0].y);
ctx.strokeStyle = "blue";
ctx.lineWidth = 2;
points.forEach((p) => {
ctx.lineTo(p.x, p.y);
})
ctx.stroke();
}
function drawCircles() {
ctx.strokeStyle = "red";
ctx.lineWidth = 4;
points.forEach((p) => {
ctx.beginPath();
ctx.arc(p.x, p.y, pointSize, 0, Math.PI * 2, true);
ctx.stroke();
})
}
redraw()
<canvas id="myCanvas" width=160 height=160 style="border: 1px solid"></canvas>
When I scroll slightly down on my canvas, it does not allow me to drag my text at all. Examples (These are GIFs) -
https://gyazo.com/e60d2efd924ced758c2c6441391804db
GIF explained: So you saw the drag was working when I was on top of the page but when I scrolled slightly down. It completely stopped working. I added a few console.logs around, and what I know is the click event listener for the canvas is working but it isn't detected the text when I slightly scroll down on the page.
I based my drag code from: http://jsfiddle.net/m1erickson/9xAGa/ | What you can see is if you change the canvas size width: 667 and height: 800, and when you scroll slightly down, you will have the same issue I am having.
HTML Code:
<div id="middle_container">
<div class="center_container">
<canvas id="canvas" width="667px" height="800px"></canvas>
</div>
</div>
JavaScript Code:
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var $canvas = $("#canvas");
var BB=canvas.getBoundingClientRect();
var offsetX = BB.left;
var offsetY = BB.top;
var mx;
var my;
var texts = [];
var images = [];
var dragF = -1;
var mode = "none";
function draw() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
for(const { text, x, y, width, height } of texts) {
ctx.fillText(text, x, y);
}
}
function addNewText(string_text) {
var y = texts.length * 20 + 20;
var text = {
text: string_text,
x: 20,
y: y
};
ctx.font = "32px verdana";
ctx.textBaseline = "top";
text.width = ctx.measureText(text.text).width;
text.height = 32;
texts.push(text);
draw();
}
function hitDrag(x,y,textIndex) {
var r=texts[textIndex];
return (x>r.x && x<r.x+r.width && y>r.y && y<r.y+r.height);
}
function myDrag(a,e) {
if (a == "down") {
e.preventDefault();
e.stopPropagation();
mx=parseInt(e.clientX-offsetX);
my=parseInt(e.clientY-offsetY);
for(var i=0;i<texts.length;i++){
if(hitDrag(mx,my,i)){
console.log("found");
dragF = i;
}
}
}
}
addNewText("Hello World")
$("#canvas").mousedown(function(e) {
myDrag("down", e);
});
The problem is this line of code:
var BB=canvas.getBoundingClientRect();
which is only populated once at the start of your script. The .getBoundingClientRect() method returns the position of a HTML element relative to the viewport.
Well if you scroll the window, the viewport moves - as the canvas element - but the BB object still holds the position of your canvas at startup.
The fix is rather simple - you need to use the actual position of the canvas element by calling .getBoundingClientRect() again on mouse down and on mouse move.
I've prepared a little sample based on your code and the fiddle you've linked:
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var $canvas = $("#canvas");
var BB = canvas.getBoundingClientRect();
var offsetX = BB.left;
var offsetY = BB.top;
var mx;
var my;
var texts = [];
var images = [];
var dragF = -1;
var mode = "none";
function draw() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
for (const {
text,
x,
y,
width,
height
} of texts) {
ctx.fillText(text, x, y);
}
}
function addNewText(string_text) {
var y = texts.length * 20 + 20;
var text = {
text: string_text,
x: 20,
y: y
};
ctx.font = "32px verdana";
ctx.textBaseline = "top";
text.width = ctx.measureText(text.text).width;
text.height = 32;
texts.push(text);
draw();
}
function myDrag(a, e) {
if (a == "down") {
e.preventDefault();
e.stopPropagation();
let rect = canvas.getBoundingClientRect();
mx = parseInt(e.clientX - rect.left);
my = parseInt(e.clientY - rect.top);
for (var i = 0; i < texts.length; i++) {
if (hitDrag(mx, my, i)) {
// console.log("found");
dragF = i;
}
}
}
}
function hitDrag(x, y, textIndex) {
var r = texts[textIndex];
return (x > r.x && x < r.x + r.width && y > r.y && y < r.y + r.height);
}
function handleMouseMove(e) {
if (dragF < 0) {
return;
}
e.preventDefault();
let rect = canvas.getBoundingClientRect();
mouseX = parseInt(e.clientX - rect.left);
mouseY = parseInt(e.clientY - rect.top);
var dx = mouseX - mx;
var dy = mouseY - my;
mx = mouseX;
my = mouseY;
var text = texts[dragF];
text.x += dx;
text.y += dy;
draw();
}
function handleMouseUp(e) {
e.preventDefault();
dragF = -1;
}
addNewText("Hello World")
$("#canvas").mousedown(function(e) {
myDrag("down", e);
});
$("#canvas").mousemove(function(e) {
handleMouseMove(e);
});
$("#canvas").mouseup(function(e) {
handleMouseUp(e);
});
body {
background-color: ivory;
}
#canvas {
border: 1px solid red;
}
#theText {
width: 10em;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="middle_container">
<div class="center_container">
<canvas id="canvas" width="667px" height="800px"></canvas>
</div>
</div>
<br><br><br><br><br><br><br><br><br><br>
<br><br><br><br><br><br><br><br><br><br>
Here I'm trying to draw on a html5 canvas as well as upload some images and drag them within the canvas. The problem is I can do one or the other but not both.
What I've realised is that the canvas must be Cleared before dragging takes place but by doing that I'm clearing the drawing and if I don't clear the canvas it draws but dragging an image leaves trail behind. Can anyone point me to right direction please.
function init() {
canvas = document.getElementById('can');
ctx = canvas.getContext("2d");
w = canvas.width;
h = canvas.height;
img = document.getElementById("drag");
canvas.addEventListener("mousemove", function (e) { findxy('move', e) }, false);
canvas.addEventListener("mousedown", function (e) { findxy('down', e) }, false);
canvas.addEventListener("mouseup", function (e) { findxy('up', e) }, false);
canvas.addEventListener("mouseout", function (e) { findxy('out', e) }, false);
imageLoader = document.getElementById('imageLoader');
imageLoader.addEventListener('change', handleImage, false);
var contexts = [];
contexts.push(canvas.getContext('2d'));
function clearAll() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
}
canvas.onclick = function (e) { handleClick(e, 1); };
function handleClick(e, contextIndex) {
e.stopPropagation();
var mouseX = parseInt(e.clientX - e.target.offsetLeft);
var mouseY = parseInt(e.clientY - e.target.offsetTop);
// clearAll();
for (var i = 0; i < states.length; i++) {
var state = states[i];
if (state.dragging) {
state.dragging = false;
state.draw();
continue;
}
if (state.contextIndex === contextIndex
&& mouseX > state.x && mouseX < state.x + state.width
&& mouseY > state.y && mouseY < state.y + state.height)
{
state.dragging = true;
state.offsetX = mouseX - state.x;
state.offsetY = mouseY - state.y;
state.contextIndex = contextIndex;
}
state.draw();
}
}
canvas.onmousemove = function (e) { handleMousemove(e, 1); }
function handleMousemove(e, contextIndex) {
e.stopPropagation();
var mouseX = parseInt(e.clientX - e.target.offsetLeft);
var mouseY = parseInt(e.clientY - e.target.offsetTop);
// clearAll();
for (var i = 0; i < states.length; i++) {
var state = states[i];
if (state.dragging) {
state.x = mouseX - state.offsetX;
state.y = mouseY - state.offsetY;
state.contextIndex = contextIndex;
}
state.draw();
}
}
var states = [];
states.push(addState(0, 0, img));
function addState(x, y, image) {
state = {}
state.dragging = false;
state.contextIndex = 1;
state.image = image;
state.x = x;
state.y = y;
state.width = image.width;
state.height = image.height;
state.offsetX = 0;
state.offsetY = 0;
state.draw = function () {
var context = contexts[this.contextIndex - 1];
if (this.dragging) {
context.strokeStyle = 'red';
context.strokeRect(this.x, this.y, this.width + 5, this.height + 5);
}
context.drawImage(this.image, this.x, this.y);
};
state.draw();
return(state);
}
}//end of init()
var imgArray = [];
function handleImage(e) {
var reader = new FileReader();
reader.onload = function (event) {
imgArray.push(img);
for(i = 0; i < imgArray.length; i++){
img.src = imgArray[i];
img.setAtX = i * 50;
img.setAtY = i * 0;
img.onload = function() {
ctx.drawImage(this, this.setAtX, this.setAtY);
};
img.src = event.target.result;
}
};
reader.readAsDataURL(e.target.files[0]);
}
function findxy(res, e) {
if (res === 'down') {
prevX = currX;
prevY = currY;
currX = e.clientX - canvas.offsetLeft;
currY = e.clientY - canvas.offsetTop;
flag = true;
dot_flag = true;
if (dot_flag) {
ctx.beginPath();
ctx.fillStyle = x;
ctx.fillRect(currX, currY, 2, 2);
ctx.closePath();
dot_flag = false;
}
}
if (res === 'up' || res === "out") {
flag = false;
}
if (res === 'move') {
if (flag) {
prevX = currX;
prevY = currY;
currX = e.clientX - canvas.offsetLeft;
currY = e.clientY - canvas.offsetTop;
draw();
}
}
}
//Draw lines or text
function draw() {
ctx.beginPath();
ctx.moveTo(prevX, prevY);
ctx.lineTo(currX, currY);
ctx.strokeStyle = x;
ctx.lineWidth = y;
ctx.stroke();
ctx.closePath();
ctx.fillStyle = x;
ctx.font = "Italic Bold 14pt Times, serif";
ctx.fillText(message, prevX, prevY);
}
Here is a mashup of the code i linked to and your own.
All parameters related to drawing the image and translating it around the canvas is covered.
var canvas = document.getElementById("canvas");
var ctx;
var x = 75;
var y = 50;
var WIDTH = 400;
var HEIGHT = 300;
var dragok = false;
var img = document.getElementById("img");
var lines = [{
x: x,
y: y
}];
function rect(x, y, w, h) {
ctx.beginPath();
ctx.rect(x, y, w, h);
ctx.closePath();
ctx.fill();
}
function clear() {
ctx.clearRect(0, 0, WIDTH, HEIGHT);
}
function init() {
canvas = document.getElementById("canvas");
ctx = canvas.getContext("2d");
return setInterval(draw, 1000/24);
}
function draw() {
clear();
ctx.drawImage(img, x - img.width / 2, y - img.height / 2);
ctx.beginPath();
ctx.moveTo(lines[0].x,lines[0].y);
for(var i = 1; i < lines.length; i++) {
var line = lines[i];
ctx.lineTo(lines[i].x,lines[i].y);
}
ctx.stroke();
ctx.closePath();
//rect(x - 15, y - 15, 30, 30);
}
function myMove(e) {
if (dragok) {
x = e.pageX - canvas.offsetLeft;
y = e.pageY - canvas.offsetTop;
lines = lines.slice(lines.length - 100)
lines.push({
x: x,
y: y
});
}
}
function myDown(e) {
if (e.pageX < x + img.width / 2 + canvas.offsetLeft && e.pageX > x - img.width / 2 +
canvas.offsetLeft && e.pageY < y + img.height / 2 + canvas.offsetTop &&
e.pageY > y - img.height / 2 + canvas.offsetTop) {
x = e.pageX - canvas.offsetLeft;
y = e.pageY - canvas.offsetTop;
dragok = true;
canvas.onmousemove = myMove;
}
}
function myUp() {
dragok = false;
canvas.onmousemove = null;
}
init();
canvas.onmousedown = myDown;
canvas.onmouseup = myUp;
//Change image for the heck of it ^^
setInterval(function() {
img.src = 'https://placeholdit.imgix.net/~text?txtsize=60&txt=' +
Math.floor(Math.random() * 10) +
'&w=100&h=100';
}, 3000)
<canvas id="canvas" width="400" height="300">
This text is displayed if your browser does not support HTML5 Canvas.
</canvas>
<img id="img" src="https://placeholdit.imgix.net/~text?txtsize=60&txt=1&w=100&h=100">
Updated my answer to draw lines
My final code that's working , I had to add two canvases one for the drawing and one for the dragging images. Fabric frame work helped a lot. https://jsfiddle.net/tn07Bond/982s4bgv/2/
<div id="canvasesdiv">
<canvas id="images" width=600 height=400 style="position: absolute;
left: 0;
top: 0;
border: 1px solid blue;
z-index: 1;">
This text is displayed if your browser does not support HTML5 Canvas</canvas>
<canvas id="drawing" width=600 height=400 style="position: absolute;
left: 0;
top: 0;
border: 1px solid red;
z-index: 2;">
This text is displayed if your browser does not support HTML5 Canvas</canvas>
<input type="file" id="imageLoader" class="imageLoader" name="imageLoader"/>
<input type="image" src="images/PenForCanvas.png" alt="pen" title="Draw on canvas"
id="Drawing" style="cursor: pointer; margin: 5px 0 0 200px;">
<input type="image" src="images/drag.png" alt="drag" title="Drag this image" id="Images" style="cursor: pointer;">
</div>
<script>
var drawing, drawingCtx,images,imagesCtx, message = "", img, imageLoader, prevX = 10, currX = 10, prevY = 10, currY = 10,
dot_flag = false, flag = false, x = "black", y = 2, formElement, canvasLeft, canvasTop, imgData, data,
startOffsetX = 0, startOffsetY = 0;
(function () {
// Drawing canvas
drawing = document.getElementById("drawing");
drawingCtx = drawing.getContext("2d");
//Uploading and dragging images
images = new fabric.Canvas('images');
document.getElementById('imageLoader').addEventListener("change", function (e) {
var file = e.target.files[0];
var reader = new FileReader();
reader.onload = function (f) {
var data = f.target.result;
fabric.Image.fromURL(data, function (img) {
var oImg = img.set({left: 0, top: 0, angle: 00,width:40, height:40}).scale(0.9);
images.add(oImg).renderAll();
var a = images.setActiveObject(oImg);
var dataURL = images.toDataURL({format: 'png', quality: 0.8});
});
};
reader.readAsDataURL(file);
});
w = drawing.width;
h = drawing.height;
//Mouse events
drawing.addEventListener("mousemove", function (e) { findxy('move', e) }, false);
drawing.addEventListener("mousedown", function (e) { findxy('down', e) }, false);
drawing.addEventListener("mouseup", function (e) { findxy('up', e) }, false);
drawing.addEventListener("mouseout", function (e) { findxy('out', e) }, false);
Drawing = document.getElementById("Drawing");
Drawing.addEventListener("click", bringDrawingToFront);
Images = document.getElementById("Images");
Images.addEventListener("click", bringImagesToFront);
imageLoader = document.getElementById('imageLoader');
imageLoader.addEventListener('change', bringImagesToFront);
//Drawing
function findxy(res, e) {
if (res === 'down') {
prevX = currX;
prevY = currY;
currX = e.clientX - drawing.offsetLeft;
currY = e.clientY - drawing.offsetTop;
flag = true;
dot_flag = true;
if (dot_flag) {
drawingCtx.beginPath();
drawingCtx.fillStyle = x;
drawingCtx.fillRect(currX, currY, 2, 2);
drawingCtx.closePath();
dot_flag = false;
}
}
if (res === 'up' || res === "out") {
flag = false;
}
if (res === 'move') {
if (flag) {
prevX = currX;
prevY = currY;
currX = e.clientX - drawing.offsetLeft;
currY = e.clientY - drawing.offsetTop;
draw();
}
}
}
function save() {
imgData = drawingCtx.getImageData(0, 0, drawing.width, drawing.height);
}
function restore() {
drawingCtx.putImageData(imgData, 0, 0);
}
function bringDrawingToFront() {
drawing.style.zIndex = 1;
document.getElementById("images").style.zIndex = 0;
}
function bringImagesToFront() {
drawing.style.zIndex = 0;
document.getElementById("images").style.zIndex = 1;
}
//Draw lines or text
function draw() {
drawingCtx.beginPath();
drawingCtx.moveTo(prevX, prevY);
drawingCtx.lineTo(currX, currY);
drawingCtx.strokeStyle = x;
drawingCtx.lineWidth = y;
drawingCtx.stroke();
drawingCtx.closePath();
drawingCtx.fillStyle = x;
drawingCtx.font = "Italic Bold 14pt Times, serif";
drawingCtx.fillText(message, prevX, prevY);
save();
}
})();
</script>
I want to make blur erasing and blur drawing effect in JQuery or Javascript. I have written the code for erasing the blurred image when I hover it and to reveal the unblurred image. Here's the screenshot.
I am able to erase the blur image when I hover over the image, but I couldn't redraw the blurred image at the same position, may be like after 500ms. How do I redraw the blur again over where I hovered with my mouse?
Here's the code for reference:
<!DOCTYPE html>
<html>
<head>
<title>Blur Testing</title>
<meta name="content-type" content="text/html; charset=UTF-8">
<style>
body {
margin: 0;
}
#item {
background: url("http://i66.tinypic.com/2z6uq9f.jpg");
background-size: cover;
background-position: center;
}
</style>
<script src="https://code.jquery.com/jquery-1.12.4.min.js" type="text/javascript"></script>
</head>
<body>
<canvas id="item"></canvas>
<script>
var canvas = document.getElementById('item');
var ctx = canvas.getContext('2d'),
img = new Image,
radius = 30;
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
$('#item').css({
"-webkit-filter": "blur(0px)",
"filter": "blur(0px)"
});
$(img).on('load', function () {
$('#item').mouseover(function (e) {
erase(getXY(e));
}).mousemove(function (e) {
erase(getXY(e));
//setTimeout(redraw(getXY(e)), 400);
});
ctx.drawImage(img, 0, 0);
ctx.globalCompositeOperation = 'destination-out';
});
img.src = 'http://i64.tinypic.com/14mt7yx.jpg';
img.width = window.width;
img.height = window.height;
function getXY(e) {
var r = $('#item')[0].getBoundingClientRect();
return {x: e.clientX - r.left, y: e.clientY - r.top};
}
// function redraw(pos) {
// ctx.globalCompositeOperation = 'source-in';
// ctx.beginPath();
// ctx.arc(pos.x, pos.y, radius, 0, 2 * Math.PI);
// ctx.closePath();
// ctx.fill();
// };
function erase(pos) {
ctx.globalCompositeOperation = 'destination-out';
ctx.beginPath();
ctx.arc(pos.x, pos.y, radius, 0, 2 * Math.PI);
ctx.closePath();
ctx.fill();
}
</script>
</body>
</html>
The following is a quick modification on Alpha Mask Filter question. I have simply reversed the blur and sharp images. See the linked question for details.
The extra bit to fade out the blur mask
blurMaskFadeCounter += 1;
if((blurMaskFadeCounter % blurMaskFadeRate) === 0){
maskImage.ctx.globalCompositeOperation = "destination-out";
maskImage.ctx.fillStyle = "#000";
maskImage.ctx.globalAlpha = 0.1;
maskImage.ctx.fillRect(0,0,maskImage.width,maskImage.height);
maskImage.ctx.globalAlpha = 1;
maskImage.ctx.globalCompositeOperation = "source-over";
}
Simply draws over the mask with destination-out composition and alpha set low. It is timed to every so many frames to slow it down. If you set alpha below 0.1 you get some pixels that wont completely clear so the frame skipping give a slower response
var imageLoadedCount = 0;
var error = false;
var maskImage;
var flowerImage;
var flowerImageBlur;
/** ImageTools.js begin **/
var imageTools = (function () {
var tools = {
canvas : function (width, height) { // create a blank image (canvas)
var c = document.createElement("canvas");
c.width = width;
c.height = height;
return c;
},
createImage : function (width, height) {
var image = this.canvas(width, height);
image.ctx = image.getContext("2d");
return image;
},
loadImage : function (url, callback) {
var image = new Image();
image.src = url;
image.addEventListener('load', callback);
image.addEventListener('error', callback);
return image;
}
};
return tools;
})();
var mouse;
var demo = function(){
/** fullScreenCanvas.js begin **/
var canvas = (function(){
var canvas = document.getElementById("canv");
if(canvas !== null){
document.body.removeChild(canvas);
}
// creates a blank image with 2d context
canvas = document.createElement("canvas");
canvas.id = "canv";
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
canvas.style.position = "absolute";
canvas.style.top = "0px";
canvas.style.left = "0px";
canvas.style.zIndex = 1000;
canvas.ctx = canvas.getContext("2d");
document.body.appendChild(canvas);
return canvas;
})();
var ctx = canvas.ctx;
/** fullScreenCanvas.js end **/
/** MouseFull.js begin **/
if(typeof mouse !== "undefined"){ // if the mouse exists
if( mouse.removeMouse !== undefined){
mouse.removeMouse(); // remove previouse events
}
}else{
var mouse;
}
var canvasMouseCallBack = undefined; // if needed
mouse = (function(){
var mouse = {
x : 0, y : 0, w : 0, alt : false, shift : false, ctrl : false,
interfaceId : 0, buttonLastRaw : 0, buttonRaw : 0,
over : false, // mouse is over the element
bm : [1, 2, 4, 6, 5, 3], // masks for setting and clearing button raw bits;
getInterfaceId : function () { return this.interfaceId++; }, // For UI functions
startMouse:undefined,
mouseEvents : "mousemove,mousedown,mouseup,mouseout,mouseover,mousewheel,DOMMouseScroll".split(",")
};
function mouseMove(e) {
var t = e.type, m = mouse;
m.x = e.offsetX; m.y = e.offsetY;
if (m.x === undefined) { m.x = e.clientX; m.y = e.clientY; }
m.alt = e.altKey;m.shift = e.shiftKey;m.ctrl = e.ctrlKey;
if (t === "mousedown") { m.buttonRaw |= m.bm[e.which-1];
} else if (t === "mouseup") { m.buttonRaw &= m.bm[e.which + 2];
} else if (t === "mouseout") { m.buttonRaw = 0; m.over = false;
} else if (t === "mouseover") { m.over = true;
} else if (t === "mousewheel") { m.w = e.wheelDelta;
} else if (t === "DOMMouseScroll") { m.w = -e.detail;}
if (canvasMouseCallBack) { canvasMouseCallBack(mouse); }
e.preventDefault();
}
function startMouse(element){
if(element === undefined){
element = document;
}
mouse.element = element;
mouse.mouseEvents.forEach(
function(n){
element.addEventListener(n, mouseMove);
}
);
element.addEventListener("contextmenu", function (e) {e.preventDefault();}, false);
}
mouse.removeMouse = function(){
if(mouse.element !== undefined){
mouse.mouseEvents.forEach(
function(n){
mouse.element.removeEventListener(n, mouseMove);
}
);
canvasMouseCallBack = undefined;
}
}
mouse.mouseStart = startMouse;
return mouse;
})();
if(typeof canvas !== "undefined"){
mouse.mouseStart(canvas);
}else{
mouse.mouseStart();
}
/** MouseFull.js end **/
// load the images and create the mask
if(imageLoadedCount === 0){
imageLoadedCount = 0;
error = false;
maskImage;
flowerImage = imageTools.loadImage("http://www.createjs.com/demos/_assets/art/flowers.jpg", function (event) {
if (event.type === "load") {
imageLoadedCount += 1;
} else {
error = true;
}
})
flowerImageBlur = imageTools.loadImage("http://i.stack.imgur.com/3S5m8.jpg", function () {
if (event.type === "load") {
maskImage = imageTools.createImage(this.width, this.height);
imageLoadedCount += 1;
} else {
error = true;
}
})
}
// set up the canvas
var w = canvas.width;
var h = canvas.height;
var cw = w / 2;
var ch = h / 2;
// calculate time to download image using the MS algorithum. As this code is a highly gaurded secret I have obsficated it for your personal safty.
var calculateTimeToGo= (function(){var b="# SecondQMinuteQHourQDayQWeekQMonthQMomentQTick#.,Some time soon,Maybe Tomorrow.".replace(/Q/g,"#.,# ").split(","),r=Math.random,f=Math.floor,lc=0,pc=0,lt=0,lp=0;var cttg=function(a){if(lc===0){lc=100+r(r()*60);lt=f(r()*40);if(pc===0||r()<(lp/b.length)-0.2){lp=f(r()*b.length);pc=1+f(r()*10)}else{pc-=1}}else{lc-=1}a=lt;if(lp===0){a=lt;if(r()<0.01){lt-=1}}var s=b[lp].replace("#",a);if(a===1){s=s.replace("#","")}else{s=s.replace("#","s")}return s};return cttg})();
// draws circle with gradient
function drawCircle(ctx, x, y, r) {
var gr = ctx.createRadialGradient(x, y, 0, x, y, r)
gr.addColorStop(1, "rgba(0,0,0,0)")
gr.addColorStop(0.5, "rgba(0,0,0,0.08)")
gr.addColorStop(0, "rgba(0,0,0,0.1)")
ctx.fillStyle = gr;
ctx.beginPath();
ctx.arc(x, y, r, 0, Math.PI * 2);
ctx.fill();
}
// draw text
function drawText(ctx, text, size, x, y, c) {
ctx.fillStyle = c;
ctx.strokeStyle = "black";
ctx.lineWidth = 5;
ctx.lineJoin = "round";
ctx.font = size + "px Arial Black";
ctx.textAlign = "center";
ctx.textBaseline = "middle";
if (c !== "black") {
ctx.strokeText(text, x, y + 1);
}
ctx.fillText(text, x, y);
}
// draw the image to fit the current canvas size
function drawImageCentered(ctx, image, x, y) {
var scale = Math.min(w / image.width, h / image.height);
ctx.setTransform(scale, 0, 0, scale, cw, ch);
ctx.drawImage(image, -image.width / 2, -image.height / 2);
ctx.setTransform(1, 0, 0, 1, 0, 0);
}
// how often to fade blur mask
var blurMaskFadeRate =8; // number of frames between fading mask out
var blurMaskFadeCounter = 0;
// points for filling gaps between mouse moves.
var lastMX,lastMY;
// update function will try 60fps but setting will slow this down.
function update(time){
ctx.setTransform(1, 0, 0, 1, 0, 0); // restore transform
ctx.clearRect(0, 0, w, h); // clear rhe canvas
// have the images loaded???
if (imageLoadedCount === 2) {
// draw the unblured image that will appear at the top
ctx.globalCompositeOperation = "source-over";
drawImageCentered(ctx, flowerImageBlur, cw, ch);
drawText(ctx, "Move mouse over image to unblur.", 20 + Math.sin(time / 100), cw, ch - 30, "White");
// Mask out the parts when the mask image has pixels
ctx.globalCompositeOperation = "destination-out";
drawImageCentered(ctx, maskImage, cw, ch);
// draw the blured image only where the destination has been masked
ctx.globalCompositeOperation = "destination-atop";
drawImageCentered(ctx, flowerImage, cw, ch);
blurMaskFadeCounter += 1;
if((blurMaskFadeCounter % blurMaskFadeRate) === 0){
maskImage.ctx.globalCompositeOperation = "destination-out";
maskImage.ctx.fillStyle = "#000";
maskImage.ctx.globalAlpha = 0.1;
maskImage.ctx.fillRect(0,0,maskImage.width,maskImage.height);
maskImage.ctx.globalAlpha = 1;
maskImage.ctx.globalCompositeOperation = "source-over";
}
// because image has been scaled need to get mouse coords on image
var scale = Math.min(w / flowerImage.width, h / flowerImage.height);
var x = (mouse.x - (cw - (maskImage.width / 2) * scale)) / scale;
var y = (mouse.y - (ch - (maskImage.height / 2) * scale)) / scale;
// draw circle on mask
drawCircle(maskImage.ctx, x, y, 60);
// if mouse is draging then draw some points between to fill the gaps
if (lastMX !== undefined) {
drawCircle(maskImage.ctx, ((x + lastMX) / 2 + x) / 2, ((y + lastMY) / 2 + y) / 2, 60);
drawCircle(maskImage.ctx, (x + lastMX) / 2, (y + lastMY) / 2, 60);
drawCircle(maskImage.ctx, ((x + lastMX) / 2 + lastMX) / 2, ((y + lastMY) / 2 + lastMY) / 2, 60);
}
// save las mouse pos on image
lastMX = x;
lastMY = y;
} else {
// Laoding images so please wait.
drawText(ctx, "Please wait.", 40 + Math.sin(time / 100), cw, ch - 30, "White");
drawText(ctx, "loading images... ", 12, cw, ch, "black")
drawText(ctx, "ETA " + calculateTimeToGo(time), 14, cw, ch + 20, "black")
}
// if not restart the request animation frame
if(!STOP){
requestAnimationFrame(update);
}else{
var can = document.getElementById("canv");
if(can !== null){
document.body.removeChild(can);
}
STOP = false;
}
}
update();
}
var STOP = false; // flag to tell demo app to stop
function resizeEvent() {
var waitForStopped = function () {
if (!STOP) { // wait for stop to return to false
demo();
return;
}
setTimeout(waitForStopped, 200);
}
STOP = true;
setTimeout(waitForStopped, 100);
}
window.addEventListener("resize", resizeEvent);
demo();
I'm having a problem whit my code.
I draw some circles in a circular path and I expect when to click on them to return something other than 0 in firebug console but that's not happening;
I don't know what is wrong with my code and i hope someone will tell me.
Here's my code:
var canvas, ctx;
var circle_data = [];
function circles(x, y, radius) {
this.x = x;
this.y = y;
this.radius = radius;
circle_data.push(this);
}
circles.prototype = {
draw: function (context) {
context.beginPath();
context.arc(this.x, this.y, this.radius / 5, 0, 2 * Math.PI, false);
context.fillStyle = "red";
context.fill();
}
}
function draw() {
ctx.translate(250, 250);
for (var n = 0; n < 10; n++) {
var radi = (Math.PI / 180);
var x = Math.sin(radi * n * 36) * 70;
var y = Math.cos(radi * n * 36) * 70;
var radius = 50;
var thiscircle = new circles(x, y, radius);
thiscircle.draw(ctx);
}
}
function mouseDown(e) {
var img_data = ctx.getImageData(e.pageX, e.pageY, 1, 1);
console.log(img_data.data[3]);
}
function init() {
canvas = document.getElementById('canvas'),
ctx = canvas.getContext('2d');
canvas.addEventListener('mousedown', mouseDown, false);
}
init();
It dosen't matter is i use data[3];
I tried whit console.log(img_data.data[0]+" "+img_data.data[1]+" "+img_data.data[2]);
Still getting 0 0 0
Your detecting the mouse position relative to the page and not the canvas, you need to get the position of the canvas on the page and subtract that from the X and Y of the mouse to find you position relative to the canvas. I use functions similar to the ones below when working with canvas.
getOffsetPosition = function(obj){
/*obj is the Canvas element*/
var offsetX = offsetY = 0;
if (obj.offsetParent) {
do {
offsetX += obj.offsetLeft;
offsetY += obj.offsetTop;
}while(obj = obj.offsetParent);
}
return [offsetX,offsetY];
}
getMouse = function(e,canvasElement){
OFFSET = getOffsetPosition(canvasElement);
mouse_x = (e.pageX || (e.clientX + document.body.scrollLeft + document.documentElement.scrollLeft)) - OFFSET[0];
mouse_y = (e.pageY || (e.clientY + document.body.scrollTop + document.documentElement.scrollTop)) - OFFSET[1];
return [mouse_x,mouse_y];
}
The following code works.
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<title>Untitled Document</title>
</head>
<body>
<canvas id="canvas" width="500" height="500" style="background-color:#999999;"></canvas>
</body>
<script>
var canvas,ctx;
var circle_data = [];
function circles(x,y,radius)
{
this.x = x;
this.y = y;
this.radius = radius;
circle_data.push(this);
}
circles.prototype = {
draw: function(context){
context.beginPath();
context.arc(this.x, this.y, this.radius / 5, 0, 2* Math.PI, false);
context.fillStyle = "red";
context.fill();
}
}
getOffsetPosition = function(obj){
/*obj is the Canvas element*/
var offsetX = offsetY = 0;
if (obj.offsetParent) {
do {
offsetX += obj.offsetLeft;
offsetY += obj.offsetTop;
}while(obj = obj.offsetParent);
}
return [offsetX,offsetY];
}
getMouse = function(e,canvasElement){
OFFSET = getOffsetPosition(canvasElement);
mouse_x = (e.pageX || (e.clientX + document.body.scrollLeft + document.documentElement.scrollLeft)) - OFFSET[0];
mouse_y = (e.pageY || (e.clientY + document.body.scrollTop + document.documentElement.scrollTop)) - OFFSET[1];
return [mouse_x,mouse_y];
}
function draw(){
ctx.translate(250, 250);
for (var n = 0; n < 10; n++) {
var radi = (Math.PI/180);
var x = Math.sin(radi*n*36)*70;
var y = Math.cos(radi*n*36)*70;
var radius = 50;
var thiscircle = new circles(x,y,radius);
thiscircle.draw(ctx);
}
}
function mouseDown(e)
{
var pos = getMouse(e,ctx.canvas);
var img_data = ctx.getImageData(pos[0],pos[1],1,1);
console.log(img_data.data[3]);
}
function init() {
canvas = document.getElementById('canvas'),
ctx = canvas.getContext('2d');
draw();
canvas.addEventListener('mousedown', mouseDown, false);
}
init();
</script>
</html>