I have a canvas on one page that will be edited by the user (drawn on), and I want that same canvas to appear in the next page they visit. For example, the first page (floorplan.php) is where the user can create a floor plan, and the next page (furnish.php) is where they can drag and drop furniture on the canvas.
I've already checked out this solution, but it doesn't seem to work for me (http://stackoverflow.com/questions/4405336/how-to-copy-contents-of-one-canvas-to-another-canvas-locally)
Here is my JS code from furnish.php: (my original canvas that I want to copy is called just 'canvas'). Also - I didn't have a save function in my floorplan.php code - maybe this is the issue?
<script>
window.onload = function(){
var canvas2 = document.getElementById( 'canvas2' );
var context2 = canvas.getContext( "2d" );
var destX = 0;
var destY = 0;
var imageObj = new Image();
imageObj.onload = function(){
context.drawImage(canvas, destX, destY);
};
imageObj.src = "images/grid.png";
};
$( document ).ready( function()
{
$( '#clear' ).click( function ()
{
context.clearRect( 0, 0, 700, 700);
} );
} );
</script>
You appear to be expecting the variable canvas to be defined on the new page. Keep in mind that when a new page is loaded the previous page (and the javascript objects for that page) are basically gone.
You will need to serialize the canvas image data, and pass it along in the URL (or somewhere) so that it can be reconstituted on the new page. See this SO question about canvas serialization.
You may also wish to consider making the two phases of your floorplan application work on the same page, so that this isn't necessary.
Related
I'm trying a basic display of a preloaded image with p5.js library (instantiation mode):
var sketch = function(p) {
var fondo;
p.preload = function() {
fondo = p.loadImage('app/themes/mrg/dist/images/tramas/example.jpg');
};
var viewportWidth = $(window).width();
p.setup = function(){
canvas = p.createCanvas(viewportWidth, 200);
canvas.background(255);
canvas.image(fondo, 0, 0);
};
};
new p5(sketch);
The canvas was created but no image is there.
Here is a working example:
https://stage.margenesdelarte.org/
The canvas is at the end of the page (with white background) but no image is rendered inside.
Image path is right, since there is no error in the console and it can be reached in its place:
https://stage.margenesdelarte.org/app/themes/mrg/dist/images/tramas/example.jpg
What is wrong, and how can I display this image? Thanks!
That's correct version? (I used BASE64 because I didn't want to run a local server)
var sketch = function(p) {
var fondo;
p.preload = function() {
fondo = p.loadImage("data:image/gif;base64,R0lGODdhMAAwAPAAAAAAAP///ywAAAAAMAAwAAAC8IyPqcvt3wCcDkiLc7C0qwyGHhSWpjQu5yqmCYsapyuvUUlvONmOZtfzgFzByTB10QgxOR0TqBQejhRNzOfkVJ+5YiUqrXF5Y5lKh/DeuNcP5yLWGsEbtLiOSpa/TPg7JpJHxyendzWTBfX0cxOnKPjgBzi4diinWGdkF8kjdfnycQZXZeYGejmJlZeGl9i2icVqaNVailT6F5iJ90m6mvuTS4OK05M0vDk0Q4XUtwvKOzrcd3iq9uisF81M1OIcR7lEewwcLp7tuNNkM3uNna3F2JQFo97Vriy/Xl4/f1cf5VWzXyym7PHhhx4dbgYKAAA7");
};
var viewportWidth = 500;
p.setup = function(){
var canvas = p.createCanvas(viewportWidth, 200);
canvas.image(fondo, 0, 0); // doesn't work
p.image(fondo, 0, 0); // works fine
console.log(p.image, canvas.image); //there are different functions
};
};
new p5(sketch);
https://codepen.io/anon/pen/yPENXx?editors=1111
Explanation:
Both p and canvas has a image function but there are different image functions. You have to use p.image(). I think canvas.image() is has some relations with https://p5js.org/reference/#/p5.Image, but that's only my assumptions.
Is your file being localhosted? For p5 to access local files such as images, it needs to be localhosted... I recommend apache
i'm working on this graphic visualizer. following is a example code of what i'm doing.
first i create new canvas and there is a simple jQuery click event to add image objects to canvas.and after working on this canvas i need to load data from database, I've managed to save data on database by "Serialization" which default support by fabric-js. and retrieve data as a json object to load in to canvas. what i want is to completely remove current working canvas and load a new with database retrieved data. so here what i've done so far...
(function() {
var canvasOffsetHeight = '400';
var canvasOffsetWidth = '600';
var canvas = new fabric.Canvas('canvas');
window.addEventListener('resize', resizeCanvas, false);
function resizeCanvas() {
canvas.setHeight(canvasOffsetHeight);
canvas.setWidth(canvasOffsetWidth);
canvas.renderAll();
}
// resize on init
resizeCanvas();
jQuery('.category ul').on('click', 'li', function (e) {
var imgElement = jQuery(this).children("img")[0];
var imgInstance = new fabric.Image(imgElement, {
left: 100,
top: 100,
angle: 0,
opacity: 1
});
canvas.add(imgInstance);
canvas.renderAll();
return false;
});
jQuery('#obj').click(function(){
canvas_data = '{"objects":[{"type":"rect","left":50,"top":50,"width":20,"height":20,"fill":"green","overlayFill":null,"stroke":null,"strokeWidth":1,"strokeDashArray":null,"scaleX":1,"scaleY":1,"angle":0,"flipX":false,"flipY":false,"opacity":1,"selectable":true,"hasControls":true,"hasBorders":true,"hasRotatingPoint":false,"transparentCorners":true,"perPixelTargetFind":false,"rx":0,"ry":0}],"background":"rgba(0, 0, 0, 0)"}';
var canvas = new fabric.Canvas('canvas');
canvas.loadFromJSON(canvas_data,canvas.renderAll.bind(canvas));
});
})();
here lets assume "canvas_data" is the data from database.
the problem is when i load data from an object it appears on canvas correctly but soon as i click on them they just vanishing.
i think because main function run onLoad so when i click it triggers the main function and load the previous canvas. what i want is to wipe out old canvas and load new one with database data. please help.
they say "loadFromJSON" automatically does this for us but it seems not working for me.
No need to create another canvas object. So your code in the #obj.click becomes:
jQuery('#obj').click(function(){
canvas_data = '{"objects":[{"type":"rect","left":50,"top":50,"width":20,"height":20,"fill":"green","overlayFill":null,"stroke":null,"strokeWidth":1,"strokeDashArray":null,"scaleX":1,"scaleY":1,"angle":0,"flipX":false,"flipY":false,"opacity":1,"selectable":true,"hasControls":true,"hasBorders":true,"hasRotatingPoint":false,"transparentCorners":true,"perPixelTargetFind":false,"rx":0,"ry":0}],"background":"rgba(0, 0, 0, 0)"}';
canvas.loadFromJSON(canvas_data,canvas.renderAll.bind(canvas));
});
I am trying to make an upload script which resizes multiple images client side before the saveimage.php handles them. The reason for this is because they will be uploaded at an outside location where there might be very slow internet.
I have been able to find pieces of code around here which helped me put it together. I am only a beginner so it's probably very messy!
What it currently does is it checks whenever files are being input in the 'file_input' field. Then it looks at the amount of files and loop through them until every file has been placed in the canvas and upload while it's in there.
However, the problem is: when I don't add an alert for each file, the loop goes too fast and only processes 1 or 2 out of 10 images for example. Because the upload script will go faster than the canvas can be updated with a new image.
This is my current page, simplified to the core:
<!DOCTYPE html>
<html>
<head>
<script type="text/javascript" src="http://code.jquery.com/jquery.min.js"></script>
</head>
<body>
<form>
<input id="file_input" type='file' multiple />
</form>
<div id="preview">
<canvas id="canvas"></canvas>
</div>
<script>
var input = document.getElementById('file_input');
input.addEventListener('change', handleFiles);
function handleFiles(e) {
var files = input.files;
alert(files.length +" files are being uploaded!"); // display amount of files for testing purpose
for (var i = 0; i < files.length; ++i) {
alert("Upload file number: "+i); // display file # for testing purpose, when this is removed the script will go too fast
var MAX_HEIGHT = 1000;
var ctx = document.getElementById('canvas').getContext('2d');
var img = new Image;
img.onload = function(){
if(img.height > MAX_HEIGHT) {
img.width *= MAX_HEIGHT / img.height;
img.height = MAX_HEIGHT;
}
var ctx = canvas.getContext("2d");
ctx.clearRect(0, 0, canvas.width, canvas.height);
preview.style.width = img.width + "px";
preview.style.height = img.height + "px";
canvas.width = img.width;
canvas.height = img.height;
ctx.drawImage(img, 0, 0, img.width, img.height);
// the canvas should contain an image now and this will send it through saveimage.php
var myDrawing = document.getElementById("canvas");
var drawingString = myDrawing.toDataURL("image/jpeg", 0.5);
var postData = "canvasData="+drawingString;
var ajax = new XMLHttpRequest();
ajax.open("POST",'saveimage.php',true);
ajax.setRequestHeader('Content-Type', 'canvas/upload');
ajax.onreadystatechange=function()
{
if (ajax.readyState == 4);
}
ajax.send(postData);
};
img.src = URL.createObjectURL(e.target.files[i]);
}
}
</script>
</body>
</html>
And this is my saveimage.php which works well, but just for the complete overview:
<?php
if (isset($GLOBALS["HTTP_RAW_POST_DATA"]))
{
$rawImage=$GLOBALS['HTTP_RAW_POST_DATA'];
$removeHeaders=substr($rawImage, strpos($rawImage, ",")+1);
$decode=base64_decode($removeHeaders);
// check if the file already exists and keep adding +1 intil it's unique
$i = 1;
while(file_exists('uploads/image'.$i.'.jpg')) {
$i++;
}
$fopen = fopen( 'uploads/image'.$i.'.jpg', 'wb' );
fwrite( $fopen, $decode);
fclose( $fopen );
}
?>
A bit of extra information:
I have added canvas, because as far as I can understand that's the
only way to resize images client side. In the final script it will be
set to hidden.
If the canvas could be skipped and the data sent to saveimage.php
immediately then that would solve the problem too I think.
As I said I am not very experienced with javascript, so if there is a
much simpler way to achieve this goal. Please let me know!
Thanks in advance!
JavaScript loads image asynchronously. That means once it is assigned a new image's .src it will begin loading the image but will simultaneously continue processing the javascript after the .src while the image takes time to load.
You're using the same image variable (var img) inside your for loop. Therefore, each time through the loop you are overwriting the previous img before the previous image has been fully loaded.
Here's an image loader that fully loads all image and then calls the start() function where you can do you processing:
// image loader
// put the paths to your images in imageURLs[]
var imageURLs=[];
imageURLs.push("myImage1.png");
imageURLs.push("myImage2.png");
// etc, etc.
// the loaded images will be placed in imgs[]
var imgs=[];
var imagesOK=0;
loadAllImages(start);
function loadAllImages(callback){
for (var i=0; i<imageURLs.length; i++) {
var img = new Image();
imgs.push(img);
img.onload = function(){
imagesOK++;
if (imagesOK>=imageURLs.length ) {
callback();
}
};
img.onerror=function(){alert("image load failed");}
img.crossOrigin="anonymous";
img.src = imageURLs[i];
}
}
function start(){
// the imgs[] array now holds fully loaded images
// the imgs[] are in the same order as imageURLs[]
}
In the onload callback use the this keyword(instead of img) to for the current image, img may/will be overwritten in the for loop before the callback fires.
img.height --> this.height etc
Also is there a reason you're using a custom content type instead of application/x-www-form-urlencoded ?
Url encoded form data is much easier to work with.
I have struggled with the same problem for some time now, but I finally
cobbled together another solution, using bits of code from others, who have addressed this problem,
and it works. It may not be perfect, and I would welcome any improvement suggestions.
The concept is that you have two Javascript
Functions, which are identicle called Resize0 and Resize1, and they call each other
until all photos have been resized then the Function which resizes the
last image Submits the Form. The two Functions pass parameters between
each other so that one of them will know when to Submit the Form.
Two of those parameters are, Number_Of_Images and current Image_Number.
You can also download a working Example Web page from
http://www.wantitconsulting.co.nz/ExampleResizeUpload.zip .
You will need to create a folder in the base directory of your test web site
for the Images to go into. The foldername is TestResizeUpload. The Server
side uses php.
I'm having an issue while using canvas in a background page to create data URLs for desktop notifications' images.
I want to use the "image" notifications which require a 3:2 ratio to display properly. The images I want to use (from hulu.com) are a different ratio, so I decided to use the canvas element to create the corresponding data URL off of these images so that the ratio is correct. It kind of works in theory, but…
…I'm having issues if I'm creating more than one canvas/notification in the background page. One image is created properly, but the rest comes out empty.
Confusingly, opening the same background page in a new tab (i.e. exact same code) makes everything works just fine: all the notifications are created with the images loaded from hulu.com. Also, just changing the dimensions from 360x240 to 300x200 makes it work. Finally, though they're similar computers with the same Chrome version (34.0.1847.116), it works without modification at work while it doesn't on my own laptop.
I made a test extension available at the bottom of this post. Basically, it only has a generated background page. The code for that page is this:
var images = ["http://ib2.huluim.com/video/60376901?size=290x160&img=1",
"http://ib2.huluim.com/video/60366793?size=290x160&img=1",
"http://ib4.huluim.com/video/60372951?size=290x160&img=1",
"http://ib1.huluim.com/video/60365336?size=290x160&img=1",
"http://ib3.huluim.com/video/60376290?size=290x160&img=1",
"http://ib4.huluim.com/video/60377231?size=290x160&img=1",
"http://ib4.huluim.com/video/60312203?size=290x160&img=1",
"http://ib1.huluim.com/video/60376972?size=290x160&img=1",
"http://ib4.huluim.com/video/60376971?size=290x160&img=1",
"http://ib1.huluim.com/video/60376616?size=290x160&img=1"];
for (var i = 0; i < 10; i++) {
getDataURL(i);
}
/*
* Gets the data URL for an image URL
*/
function getDataURL(i) {
var img = new Image();
img.onload = function() {
var canvas = document.createElement('canvas');
canvas.width = 360;
canvas.height = 240;
var ctx = canvas.getContext('2d');
ctx.drawImage(this, 0, 0);
ctx.fillStyle = "rgb(200,0,0)";
ctx.fillRect (10, 10, 55, 50);
var dataURL = canvas.toDataURL('image/png');
chrome.notifications.create('', {
type: 'image',
iconUrl: 'logo_128x128.png',
title: String(i),
message: 'message',
imageUrl: dataURL
}, function(id) {});
}
//img.src = chrome.extension.getURL('logo_128x128.png');;
img.src = images[i];
}
The commented out line for img.src = ... is a test where it loads a local file instead of a remote one. In that case, all the images are created.
The red rectangle added to the canvas is to show that it's not just the remote image that is an issue: the whole resulting canvas is empty, without any red rectangle.
If you download and add the test extension below, you should get 10 notifications but only one with an image.
Then, to open the background page in a new tab, you can inspect the background page, type this in the console:
chrome.extension.getURL('_generated_background_page.html')
and right-click the URL, and click "Open in a new Tab" (or window). Once open you should get 10 notifications that look fine.
Any idea of what is going on? I haven't been able to find any kind of limitations for background pages relevant to that. Any help would be appreciated, because this has been driving me crazy!
Files available here: https://www.dropbox.com/s/ejbh6wq0qixb7a8/canvastest.zip
edit: based on #GameAlchemist's comment, I also tried the following: same getDataURL method, but the loop wrapped inside an onload for the logo:
function loop() {
for (var i = 0; i < 10; i++) {
getDataURL(i);
}
}
var logo = new Image();
logo.onload = function () {
loop();
}
logo.src = chrome.extension.getURL('logo_128x128.png');
Remember that the create() method is asynchronous and you should use a callback with. The callback can invoke next image fetching.
I would suggest doing this in two steps:
Load all the images first
Process the image queue
The reason is that you can utilize the asynchronous image loading better this way instead of chaining the callbacks which would force you to load one and one image.
For example:
Image loader
var urls = ["http://ib2.huluim.com/video/60376901?size=290x160&img=1",
"http://ib2.huluim.com/video/60366793?size=290x160&img=1",
"http://ib4.huluim.com/video/60372951?size=290x160&img=1",
"http://ib1.huluim.com/video/60365336?size=290x160&img=1",
"http://ib3.huluim.com/video/60376290?size=290x160&img=1",
"http://ib4.huluim.com/video/60377231?size=290x160&img=1",
"http://ib4.huluim.com/video/60312203?size=290x160&img=1",
"http://ib1.huluim.com/video/60376972?size=290x160&img=1",
"http://ib4.huluim.com/video/60376971?size=290x160&img=1",
"http://ib1.huluim.com/video/60376616?size=290x160&img=1"];
var images = [], // store image objects
count = urls.length; // for loader
for (var i = 0; i < urls.length; i++) {
var img = new Image; // create image
img.onload = loader; // share loader handler
img.src = urls[i]; // start loading
images.push(img); // push image object in array
}
function loader() {
count--;
if (count === 0) process(); // all loaded, start processing
}
//TODO need error handling here as well
Fiddle with concept code for loader
Processing
Now the processing can be isolated from the loading:
function process() {
// share a single canvas (use clearRect() later if needed)
var canvas = document.createElement('canvas'),
ctx = canvas.getContext('2d'),
current = 0;
canvas.width = 360;
canvas.height = 240;
createImage(); // invoke processing for first image
function createImage() {
ctx.drawImage(images[current], 0, 0); // draw current image
ctx.fillStyle = "rgb(200,0,0)";
ctx.fillRect (10, 10, 55, 50);
chrome.notifications.create('', {
type : 'image',
iconUrl : 'logo_128x128.png',
title : String(i),
message : 'message',
imageUrl: canvas.toDataURL() // png is default
},
function(id) { // use callback
current++; // next in queue
if (current < images.length) {
createImage(); // call again if more images
}
else {
done(); // we're done -> continue to done()
}
});
}
}
Disclaimer: I don't have a test environment to test Chrome extensions so typos/errors may be present.
Hope this helps!
I've got multiple images, and I'd like to load them each into a single <canvas> element at different points in time and then manipulate them using CamanJS. I can get the first image to appear like this:
Caman('#canvas-element', '/images/one.jpg');
But then when I subsequently try to update that same element using the following code, it does not work.
Caman('#canvas-element', '/images/two.jpg');
Is there some way to reset/clear/flush the canvas and load new image data into it, or do I really need to create separate <canvas> elements for each image I want to load? I'd prefer a single element because I don't want to eat up all the memory.
Remove the Caman attribute (data-caman-id) from the IMG or CANVAS element, change the image, and then re-render Caman.
document
.querySelector('#view_image')
.removeAttribute('data-caman-id');
const switch_img = '/to/dir/img.png';
Caman("#view_image", switch_img, function() {
this.render();
});
Hope followed code can help others who have same require.
function loadImage(source) {
var canvas = document.getElementById('image_id');
var context = canvas.getContext('2d');
var image = new Image();
image.onload = function() {
context.drawImage(image, 0, 0, 960, 600);
};
image.src = source;
}
function change_image(source) {
loadImage(source);
Caman('#image_id', source, function () {
this.reloadCanvasData();
this.exposure(-10);
this.brightness(5);
this.render();
});
}
Just figured this one out with a lot of trial and error and then a duh moment!
Instead of creating my canvas directly in my html, I created a container and then just did the following:
var retStr = "<canvas id=\"" + myName + "Canvas\"></canvas>";
document.getElementById('photoFilterCanvasContainer').innerHTML = retStr;
Caman("#" + myName + "Canvas", myUrl, function() {
this.render();
});
You want the canvas id to be unique each time you access the Caman function with a new image.