Website: http://minimedit.com/
Currently implementing an eye dropper. It works fine in my normal resolution of 1080p, but when testing on a higher or lower resolution it doesn't work.
This is the basics of the code:
var ctx = canvas.getContext("2d");
canvas.on('mouse:down', function(e) {
var newColor = dropColor(e, ctx);
}
function dropColor(e, ctx) {
var mouse = canvas.getPointer(e.e),
x = parseInt(mouse.x),
y = parseInt(mouse.y),
px = ctx.getImageData(x, y, 1, 1).data;
return rgb2hex('rgba('+px+')');
}
When I first initiate the canvas I have it resize to fit resolution:
setResolution(16/9);
function setResolution(ratio) {
var conWidth = ($(".c-container").css('width')).replace(/\D/g,'');
var conHeight = ($(".c-container").css('height')).replace(/\D/g,'');
var tempWidth = 0;
var tempHeight = 0;
tempHeight = conWidth / ratio;
tempWidth = conHeight * ratio;
if (tempHeight > conHeight) {
canvas.setWidth(tempWidth);
canvas.setHeight(conHeight);
} else {
canvas.setWidth(conWidth);
canvas.setHeight(tempHeight);
}
}
The x and y mouse coordinates work fine when zoomed in, but they don't line up with the returned image data. It seems as though the ctx isn't changing it's width and height and scaling along with the actual canvas size.
The canvas element is showing the correct width and height before using getContext as well.
Any ideas on a solution?
Feel free to check out the full scripts on the live website at: http://minimedit.com/
Try "fabric.devicePixelRatio" for calculating actual position, for example:
x = parseInt(mouse.x) * fabric.devicePixelRatio
I'm building a new website that will let users apply filters to images (just like Instagram). I will use -webkit-filter for that.
The user must be able to save the filtered image. There is any way I can do that using JavaScript?
You can't save images directly, but you can render them in Canvas, then save from there.
See: Save HTML5 canvas with images as an image
There is no direct/straight forward method to export an image with CSS Filter.
Follow the below steps for Saving/Exporting an Image with -webkit-filter applied on it:
1. Render the image to a canvas:
var canvas = document.createElement('canvas');
canvas.id="canvasPhoto";
canvas.width = imageContaainer.width;
canvas.height = imageContaainer.height;
var ctx = canvas.getContext('2d');
ctx.drawImage(imageContaainer, 0, 0, canvas.width, canvas.height);
Get the ImageData from canvas and apply the filter. Eg: I will apply grayscale filter to the ImageData below:
function grayscale(ctx) {
var pixels = ctx.getImageData(0,0,canvas.width, canvas.height);
var d = pixels.data;
for (var i=0; i<d.length; i+=4) {
var r = d[i];
var g = d[i+1];
var b = d[i+2];
var v = 0.2126*r + 0.7152*g + 0.0722*b;
d[i] = d[i+1] = d[i+2] = v
}
context.putImageData(pixels, 0, 0);
}
Add an event and use the below code to trigger download
function download(canvas) {
var data = canvas.toDataURL("image/png");
if (!window.open(data))
{
document.location.href = data;
}
}
I have a big canvas (5000x5000) and I want to take a picture of it and create a thumbnail on client side. I can capture the image using canvas.toDataURL but how do i re-size it? Do i have to create an new $("<canvas></canvas>") element and then put that image inside and run the canvas2.toDataURL(); Can anyone help me with this? I can't get my head around it how to do it.
var canvas = document.getElementById("main");
var ctx = canvas.getContext("2d");
var tumbnail64 = null;
var image = new Image();
image.src = canvas.toDataURL();
image.onload = function() {
$c2 = $("<canvas></canvas>");
$c2[0].width=100;
$c2[0].height=100;
$c2[0].getContext("2d");
$c2[0].drawImage(image, 0, 0,100,100);
tumbnail64 = $c2[0].toDataURL();
};
Something like this should work, given you don't have security restrictions on the original canvas element:
var resizedCanvas = document.createElement("canvas");
var resizedContext = resizedCanvas.getContext("2d");
resizedCanvas.height = "100";
resizedCanvas.width = "200";
var canvas = document.getElementById("original-canvas");
resizedContext.drawImage(canvas, 0, 0, 200, 100);
var myResizedData = resizedCanvas.toDataURL();
I'm trying to make a backend for publishing simple jigsaw-puzzle games. The game uses 12 premade shapes as masks for making the 12 puzzle pieces. I found the excellent Canvas global CompositeOperation tutorial, and tested it.
The application I'm making is using ajax to send each finished piece to the serverside .php-script. The user loads an image (600x400) using SSE and the app moves the original image inside tempCanvas according to the values of the arrays arr_x and arr_y It's supposed to happen in a for-loop:
function drawPieces(){
var canvas = document.getElementById("myCanvas");
var context =canvas.getContext("2d");
var tempCanvas = document.getElementById("tempCanvas");
var tempContext = tempCanvas.getContext("2d");
var img_mask = new Image();
var w;
var h;
var cvd0,cvd1,cvd2,cvd3,cvd4,cvd5,cvd6,cvd7,cvd8,cvd9,cvd10,cvd11;
var arr_data = [cvd0,cvd1,cvd2,cvd3,cvd4,cvd5,cvd6,cvd7,cvd8,cvd9,cvd10,cvd11];
var arr_x = [0,-136,-289,-414,0,-115,-270,-415,0,-118,-288,-415];
var arr_y = [0,0,0,0,-113,-111,-111,-124,-244,-256,-243,-241];
var img_bg = new Image(600, 400);
for (var i = 0; i<arr_x.length; i++) {
img_bg = original;
// get the mask
img_mask.src = "img/mask"+i+".png";
w = img_mask.width;
h = img_mask.height;
console.log("w = ", img_mask.width, " h = ", img_mask.height);
tempCanvas.width = w;
tempCanvas.height = h;
// Her lages maska
tempContext.drawImage(img_mask,0,0);
tempContext.globalCompositeOperation = 'source-in';
tempContext.drawImage(img_bg,arr_x[i],arr_y[i]);
myCanvas.width = w;
myCanvas.height = h;
// Draws tempCanvas on to myCanvas
console.log("tempCanvas: ", tempCanvas, " img_mask.src = ", img_mask.src);
context.drawImage(tempCanvas, 0, 0);
arr_data[i] = myCanvas;
sendData(arr_data[i], [i]);
};
}
Sending the image data to the server:
function sendData(cvd, index){
var imageData = cvd.toDataURL("image/png");
var ajax = new XMLHttpRequest();
ajax.open("POST", "testsave.php", false);
// ajax.onreadystatechange=function(){
// console.log("index = ", index)
// };
ajax.setRequestHeader('Content-Type', 'application/upload');
ajax.send(imageData+"<split>"+index);
};
I got a button to start drawPieces. But I get a number of issues. Firefox throws an error:
InvalidStateError: An attempt was made to use an object that is not, or is no longer, usable: context.drawImage(tempCanvas, 0, 0);
But if I click the button again the loop is run and I get 12 pieces in my folder. (Using xammp for now). But the pieces are cut wrong! The app doesn't seem to load the correct mask each time the loop runs.
So I tested it without using a loop by having 12 different functions where each function is calling the next one. It worked with one function, but started messing up the masks when I added more:
function drawPiece0(){
img_bg = original;
img_mask.src = "img/mask0.png";
tempCanvas.width = 185;
tempCanvas.height = 145;
// Her lages maska
tempContext.drawImage(img_mask,0,0);
tempContext.globalCompositeOperation = 'source-in';
tempContext.drawImage(img_bg,0,0);
myCanvas.width = 185;
myCanvas.height = 145;
// Tegner tempCanvas over på myCanvas
context.drawImage(tempCanvas, 0, 0);
cvd0 = myCanvas;
sendData(cvd0, 0);
// drawPiece1();
};
Something is absolutely wrong in my setup, but I can't figure out what it is. Someone help me please!
By the way, here is my .php-script too:
<?php
if (isset($GLOBALS["HTTP_RAW_POST_DATA"]))
{
// Get the data
$imageData=$GLOBALS['HTTP_RAW_POST_DATA'];
$parts = explode("<split>", $imageData);
$imageData = $parts[0];
$index= $parts[1];
// 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);
// Save file.
$fp = fopen( "pieces/".$index.".png", "wb" );
fwrite( $fp, $unencodedData);
fclose( $fp );
}
?>
Problem
The problem is that image loading is asynchronous which means they load in the background while your code is continuing.
That means the images won't be ready (loaded and decoded) when you try to use them with drawImage resulting in the error. The error is indirect here though as w and h do not get valid values for the canvas which means canvas will be 0 width and 0 height which will trigger the actual error when attempted drawn.
The reason why it "works" the second time is because an image exists in the browser's cache and may be able to provide the image before it's used.
Another problem is that in your loop you are overwriting the image variable so only the last image will be used when loaded.
Solution
The solution is to make or use an image loader before you start the loop. It's easy to make one but for simplicity I will use the YAIL loader in this example (disclaimer: author ibid) but any kind of loader will do as long as you use a callback for the images:
function drawPieces(){
var canvas = document.getElementById("myCanvas");
var context =canvas.getContext("2d");
var tempCanvas = document.getElementById("tempCanvas");
var tempContext = tempCanvas.getContext("2d");
var img_mask;
var w;
var h;
var cvd0,cvd1,cvd2,cvd3,cvd4,cvd5,cvd6,cvd7,cvd8,cvd9,cvd10,cvd11;
var arr_data = [cvd0,cvd1,cvd2,cvd3,cvd4,cvd5,cvd6,cvd7,cvd8,cvd9,cvd10,cvd11];
var arr_x = [0,-136,-289,-414,0,-115,-270,-415,0,-118,-288,-415];
var arr_y = [0,0,0,0,-113,-111,-111,-124,-244,-256,-243,-241];
var img_bg;
// using some image(s) loader
var loader = new YAIL({done: draw});
for (var i = 0; i<arr_x.length; i++)
loader.add("img/mask"+i+".png");
loader.load(); // start loading images
// this is called when images has loaded
function draw(e) {
for (var i = 0; i<arr_x.length; i++) {
//img_bg = original; ??
// get the mask
var img_mask = e.images[i]; // loaded images in an array
w = img_mask.width; // now you will have a valid
h = img_mask.height; // dimension here
console.log("w = ", img_mask.width, " h = ", img_mask.height);
tempCanvas.width = w;
tempCanvas.height = h;
// Her lages maska
tempContext.drawImage(img_mask,0,0);
tempContext.globalCompositeOperation = 'source-in';
tempContext.drawImage(img_bg,arr_x[i],arr_y[i]);
myCanvas.width = w;
myCanvas.height = h;
// Draws tempCanvas on to myCanvas
console.log("tempCanvas: ", tempCanvas, " img_mask.src = ", img_mask.src);
context.drawImage(tempCanvas, 0, 0);
arr_data[i] = myCanvas;
sendData(arr_data[i], [i]);
}
};
}
Note: loading images will make your code asynchronous in nature. If the code depends on some other function right after the pieces has been drawn then you must invoke that function from within the inner one after the loop has finished.
Hope this helps. If the pieces still doesn't show correctly please set up a fiddle with the images uploaded to f.ex. imgur.com so we can dig deeper into the problem.
Hope this helps!
Is it possible to use canvas.toDataURL() in Adobe AIR?
When I try I get the following error:
Error: SECURITY_ERR: DOM Exception 18
Adobe AIR enforces same origin for images used in the canvas API. Once you've used something from another domain in your canvas, you can't get pixel data back out of it. However, you can make use of the Loader class to get the pixel data, and convert it into a canvas ImageData.
For example:
function getDataURL(url, callback) {
var loader = new air.Loader();
loader.contentLoaderInfo.addEventListener(air.Event.COMPLETE, function(event) {
var bitmapData = loader.content.bitmapData;
var canvas = document.createElement('canvas');
canvas.width = bitmapData.width;
canvas.height = bitmapData.height;
var context = canvas.getContext('2d');
var imageData = context.createImageData(canvas.width, canvas.height);
var dst = imageData.data;
var src = bitmapData.getPixels(bitmapData.rect);
src.position = 0;
var i = 0;
while (i < dst.length) {
var alpha = src.readUnsignedByte();
dst[i++] = src.readUnsignedByte();
dst[i++] = src.readUnsignedByte();
dst[i++] = src.readUnsignedByte();
dst[i++] = alpha;
}
context.putImageData(imageData, 0, 0);
callback(canvas.toDataURL());
});
loader.load(new air.URLRequest(url));
}
window.addEventListener('load', function() {
getDataURL('http://www.google.com/images/logo.gif', function(dataURL) {
air.trace(dataURL);
});
}, false);