I have an issue with my fiddle. I am trying to upload an image from my PC then it is sliced and all the squares are shuffled. I managed to create functions to upload the image and others to slice and shuffle an image from the web but I cannot make them work together. Can you help me out? the code is the one that follows:
$(function () {
$(":file").change(function () {
if (this.files && this.files[0]) {
var reader = new FileReader();
reader.onload = imageIsLoaded;
reader.readAsDataURL(this.files[0]);
}
});
});
function imageIsLoaded(e) {
$('#myImg').attr('src', e.target.result);
}
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var cw=canvas.width;
var ch=canvas.height;
var rows=6;
var cols=6;
var img=new Image();
img.onload=start;
img = document.getElementById("myImg");
function start(){
var iw=canvas.width=img.width;
var ih=canvas.height=img.height;
var pieceWidth=iw/cols;
var pieceHeight=ih/rows;
var pieces = [
{col:0,row:0},
{col:1,row:0},
{col:2,row:0},
{col:3,row:0},
{col:4,row:0},
{col:5,row:0},
{col:0,row:1},
{col:1,row:1},
{col:2,row:1},
{col:3,row:1},
{col:4,row:1},
{col:5,row:1},
{col:0,row:2},
{col:1,row:2},
{col:2,row:2},
{col:3,row:2},
{col:4,row:2},
{col:5,row:2},
{col:0,row:3},
{col:1,row:3},
{col:2,row:3},
{col:3,row:3},
{col:4,row:3},
{col:5,row:3},
{col:0,row:4},
{col:1,row:4},
{col:2,row:4},
{col:3,row:4},
{col:4,row:4},
{col:5,row:4},
{col:0,row:5},
{col:1,row:5},
{col:2,row:5},
{col:3,row:5},
{col:4,row:5},
{col:5,row:5},
]
shuffle(pieces);
var i=0;
for(var y=0;y<rows;y++){
for(var x=0;x<cols;x++){
var p=pieces[i++];
ctx.drawImage(
// from the original image
img,
// take the next x,y piece
x*pieceWidth, y*pieceHeight, pieceWidth, pieceHeight,
// draw it on canvas based on the shuffled pieces[] array
p.col*pieceWidth, p.row*pieceHeight, pieceWidth, pieceHeight
);
}}
}
function shuffle(a){
for(var j, x, i = a.length; i; j = Math.floor(Math.random() * i), x = a[--i], a[i] = a[j], a[j] = x);
return a;
}
body{ background-color: ivory; padding:10px; }
#canvas{border:0px solid ;}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js"></script>
<input type='file' />
<img id="myImg" src="#" alt="your image" />
<canvas id="canvas" width=1080 height=1080></canvas>
In your code you set the onload handler then right after overwrite the img variable. In this code the first 2 lines are essentially dead code because of the 3rd line.
var img=new Image();
img.onload=start;
img = document.getElementById("myImg");
You should instead just grab the element and add your handler after.
var img = document.getElementById("myImg");
img.onload=start;
Related
I am creating a grid of images and I would like to return the image name when the user clicks on the image. My code always returns the name of the last image
(I can work around this using the index but I would like to know what is going on)
last_image = -1
var next_button = document.getElementById('next-image');
var all_button = document.getElementById('all-images');
document.addEventListener("DOMContentLoaded", (event) => {
allImages()
});
function allImages() {
for (let index = 0; index < 5; index++) {
var canvas_name = "display-" + index
var image_name = 'image_' + index + '.png'
drawImage(canvas_name, image_name)
var cell = document.getElementById('display-' + index)
cell.onclick = function() {
cellClick(index, image_name)
};
}
}
function drawImage(canvas_name, image) {
let imageObj = new Image();
imageObj.name = canvas_name;
imageObj.onload = function() {
let canvas = document.getElementById(this.name);
let ctx = canvas.getContext('2d');
ctx.drawImage(this, 0, 0);
};
imageObj.src = image;
}
function cellClick(index, image_name) {
console.log(index, image_name)
}
<div class="display-grid">
<span><canvas class="canvas" id="display-0"></canvas></span>
<span><canvas class="canvas" id="display-1"></canvas></span>
<span><canvas class="canvas" id="display-2"></canvas></span>
<span><canvas class="canvas" id="display-3"></canvas></span>
<span><canvas class="canvas" id="display-4"></canvas></span>
</div>
Unless you really need to add numerically indexed IDs to each of the canvas elements you can remove them. They will have an index when processed using querySelectorAll which can then be used, if you wish, to build the image src (ie: image_0.png etc
By passing the canvas as an argument to drawImage and returning the canvas you can assign the event listener within allImages and access the newly assigned name of the canvas - effectively the src of the image passed to the drawImage` method.
For ease of viewing rather than blank images I used a placeholder but you'll see the commented out original. In the original code the clickhandler, when invoked, used the last index because the loop had already been run rather than picking an index from the current clicked item.
document.addEventListener("DOMContentLoaded", ()=>{
const next_button=document.getElementById('next-image');
const all_button=document.getElementById('all-images');
const col=document.querySelectorAll('.display-grid span canvas');
const allImages=function(){
col.forEach((cnvs,i)=>{
//let src='image_'+i+'.png';
let src='http://placekitten.com/452/450?image='+i;
cnvs=drawImage( cnvs, src );
cnvs.addEventListener('click',function(e){
alert(this.name)
})
})
}
function drawImage( canvas, src ) {
let oImg = new Image();
oImg.onload = function() {
canvas.name=src;
let ctx = canvas.getContext('2d');
ctx.drawImage( this, 0, 0 );
};
oImg.src = src;
return canvas;
}
allImages()
});
span{display:block;border:1px solid red;padding:1rem;margin:0.25rem auto;}
canvas{border:1px solid black;margin:1rem;}
img{border:1px solid blue;}
<div class="display-grid">
<span><canvas></canvas></span>
<span><canvas></canvas></span>
<span><canvas></canvas></span>
<span><canvas></canvas></span>
<span><canvas></canvas></span>
</div>
<input type='button' id='next-image' value='Next' />
<input type='button' id='all-images' value='All' />
Store the src into the Canvas EL data-* attribute
Get all your canvas elements using Element.querySelectorAll()
Iterate your Canvas Elements using Nodelist.prototype.forEach()
PS: instead of using DOMContentLoaded just make sure to always use <script> tags right before the closing </body> tag (so not in HEAD or disseminated around your document).
const canvases = document.querySelectorAll('.display-grid canvas');
document.addEventListener("DOMContentLoaded", allImages);
function allImages() {
canvases.forEach((EL, i) => {
EL.dataset.src = `https://via.placeholder.com/100/?text=Image-${i}`;
drawImage(EL, i);
EL.addEventListener("click", () => canvasClick(EL, i));
});
}
function drawImage(EL, i) {
const img = new Image();
img.addEventListener("load", () => {
const ctx = EL.getContext('2d');
ctx.canvas.width = img.naturalWidth;
ctx.canvas.height = img.naturalHeight;
ctx.drawImage(img, 0, 0);
})
img.src = EL.dataset.src
}
function canvasClick(EL, i) {
console.log(i, EL.dataset.src);
}
canvas {
border: 3px solid red;
}
<div class="display-grid">
<span><canvas class="canvas"></canvas></span>
<span><canvas class="canvas"></canvas></span>
<span><canvas class="canvas"></canvas></span>
<span><canvas class="canvas"></canvas></span>
<span><canvas class="canvas"></canvas></span>
</div>
I got a code for loading an image into a canvas, and it works fine, but when I try to iterate to do it multiple times, it only loads one image (the last one). Anyone knows why?
<pre id="output_1"></pre>
<canvas id="canvas_1"></canvas>
<pre id="output_2"></pre>
<canvas id="canvas_2"></canvas>
<script type="text/javascript">
var pics = [ 'Desert.jpg', 'Hydrangeas.jpg' ];
for(var i=0; i < pics.length; i++) {
var img = new Image();
var ctx = $( '#canvas_'+(i+1) )[0].getContext('2d');
img.onload = function() {
ctx.drawImage(img, 0, 0);
};
img.src = pics[i];
}
</script>
The var img is being overwritten as you loop. The onloads will not be called until the current execution is complete. By that time img will equal the 3rd iteration.
To fix
var pics = [ 'Desert.jpg', 'Hydrangeas.jpg' ];
function loadImage(i){
var img = new Image();
var ctx = $( '#canvas_'+(i+1) )[0].getContext('2d');
img.onload = function() {
ctx.drawImage(img, 0, 0);
};
img.src = pics[i];
}
for(var i=0; i < pics.length; i++) {
loadImage(i);
}
The call to the function loadImage creates a new reference to all the variable each time it is called. It differs from onload because it is called immediately, while onload is a callback and must wait until the currently executing context has completely exited (in other words returned until there are no more returns), then and only then can the onload happen.
I know we can use this code to enable CORS on a single image
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
var img = new Image();
img.crossOrigin = '';
img.src = 'http://crossdomain.com/image.jpg';
canvas.width = img.width;
canvas.height = img.height;
ctx.drawImage(img, 0, 0, img.width, img.height);
Is there any way to do it for multiple image URLs at once?
URL array
To load several images enabling CORS request, you can use an array which is practical for this purpose.
One thing to be aware of is that requesting CORS can be denied by server. The browser may fail loading the image in those cases so you will need to know in advance if CORS need to be requested or not.
Example loader
var urls = [url1, url2, url3, ...]; // etc. replace with actual URLs
var images = []; // store the loaded images
var i = 0, len = urls.length;
var count = len; // for load and error handlers
for(; i < len; i++) {
var img = new Image();
img.onload = loadHandler;
img.onerror = img.onabort = errorHandler;
img.crossOrigin = ""; // enable CORS request
img.src = urls[i]; // set src last
images.push(img); // store in array
}
function loadHandler() {
if (!--count) callback(); // loading done
}
function errorHandler() {
// handle errors here
loadHandler(); // make sure to update counter/callback
}
function callback() {
// ... ready, continue from here
}
Demo
var urls = [
"http://i.imgur.com/0LINzxs.jpg", // random urls from imgur..
"http://i.imgur.com/6ksiMgS.jpg",
"http://i.imgur.com/aGQSLi9.jpg"
];
var images = []; // store the loaded images
var i = 0, len = urls.length;
var count = len; // for load and error handlers
for(; i < len; i++) {
var img = new Image();
img.onload = loadHandler;
img.onerror = img.onabort = errorHandler;
img.crossOrigin = ""; // enable CORS request
img.src = urls[i]; // set src last
images.push(img); // store in array
}
function loadHandler() {
if (!--count) callback(); // loading done
}
function errorHandler() {
// handle errors here
loadHandler(); // make sure to update
}
function callback() {
// ... ready, continue from here
console.log(images);
var ctx = document.querySelector("canvas").getContext("2d");
ctx.drawImage(images[0], 0, 0);
ctx.drawImage(images[1], 0, 0);
ctx.drawImage(images[2], 0, 0);
console.log(ctx.canvas.toDataURL()); // OK if CORS is OK!
}
<canvas></canvas>
Put the srcs in an array and iterate over them
working fiddle
https://jsfiddle.net/ps50po4z/
This allows you to use multiple images from different sources and display them.
use this function as a template to iterate through all the cors src images
<html>
<head>
<style>
body {
margin: 0px;
padding: 0px;
}
</style>
</head>
<body>
<canvas id="myCanvas" width="578" height="200"></canvas>
<script>
function loadImages(sources, callback) {
var images = {};
var loadedImages = 0;
var numImages = 0;
// get num of sources
for(var src in sources) {
numImages++;
}
for(var src in sources) {
images[src] = new Image();
images[src].onload = function() {
if(++loadedImages >= numImages) {
callback(images);
}
};
images[src].src = sources[src];
}
}
var canvas = document.getElementById('myCanvas');
var context = canvas.getContext('2d');
var sources = {
darthVader: 'http://www.html5canvastutorials.com/demos/assets/darth-vader.jpg',
yoda: 'http://www.html5canvastutorials.com/demos/assets/yoda.jpg'
};
loadImages(sources, function(images) {
context.drawImage(images.darthVader, 100, 30, 200, 137);
context.drawImage(images.yoda, 350, 55, 93, 104);
});
</script>
</body>
</html>
I'm merging two images, but result is empty, I've following function:
function merge_avatar_flag(avatar_url,country) {
var flag = new Image('allflags/'+ country.toLowerCase().split(' ').join('_') +'.png');
var avatar = new Image(avatar_url);
var img = new Image();
avatar.onload = function() {
flag.onload = function() {
var canvas = document.createElement("canvas");
var ctx = canvas.getContext("2d");
ctx.drawImage(flag, 0, 0);
ctx.drawImage(avatar, 0, 0);
img.src = canvas.toDataURL();
return img;
}
}
}
And the result is merged like this:
$("#changeAvatar").append(merge_avatar_flag(random_avatar,country));
Nothing gets added.
Anything obvious I'm missing here?
You must fully load all images before you try to drawImage them:
After the images are all fully loaded you can combine the images into an img element like this:
$("#combine").click(function(){
var canvas = document.createElement("canvas");
var ctx = canvas.getContext("2d");
canvas.width=imgs[0].width;
canvas.height=imgs[0].height;
ctx.drawImage(imgs[0],0,0); // imgs[0] is the flag
ctx.drawImage(imgs[1],0,0); // imgs[1] is the avatar
$("#changeAvatar").attr("src",canvas.toDataURL());
});
Demo: http://jsfiddle.net/m1erickson/m2DWP/
Code example:
<!doctype html>
<html>
<head>
<link rel="stylesheet" type="text/css" media="all" href="css/reset.css" /> <!-- reset css -->
<script type="text/javascript" src="http://code.jquery.com/jquery.min.js"></script>
<style>
body{ background-color: ivory; }
#combine{display:none;}
</style>
<script>
$(function(){
var imageURLs=[]; // put the paths to your images here
var imagesOK=0;
var imgs=[];
imageURLs.push("https://dl.dropboxusercontent.com/u/139992952/stack1/norwayFlag.jpg");
imageURLs.push("https://dl.dropboxusercontent.com/u/139992952/stack1/avatar.png");
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(){
$("#combine").show();
}
$("#combine").click(function(){
var canvas = document.createElement("canvas");
var ctx = canvas.getContext("2d");
canvas.width=imgs[0].width;
canvas.height=imgs[0].height;
ctx.drawImage(imgs[0],0,0);
ctx.drawImage(imgs[1],0,0);
$("#changeAvatar").attr("src",canvas.toDataURL());
});
}); // end $(function(){});
</script>
</head>
<body>
<button id="combine">Combine</button><br>
<img id="changeAvatar">
</body>
</html>
I'm using html5 to create drag and drop image upload functionality. This works great for me in firefox but in chrome the image onload event only fires the first time. If I drag multiple images in only the first works and if I drag a second in it fails. I believe the problem is with the image onload.
here is the way my code works I have removed the irrelevant sections:
var img = document.createElement("img");
var reader = new FileReader();
var canvas = document.createElement("canvas");
var canvasData;
var ctx = canvas.getContext("2d");
var myFiles;
var i = 0;
reader.onload = (function (aImg)
{
return function (e)
{
aImg.src = e.target.result;
};
})(img);
img.onload = function (){
//resizes image
//draws it to the canvas
//posts to server
i++;
if(i < myFiles.length){
processNext(i);
}
}
function processNext(filei) {
var file = myFiles[filei];
img.file = file;
reader.readAsDataURL(file);
}
i = 0;
myFiles = files;
processNext(0);
Does anyone know why this works in firefox but not chrome?
Explanation from chromium tracker:
This is not a bug. WebKit is just more strict. You must instantiate a new Image() object before the replacement, like this:
var photo = document.getElementById('image_id');
var img = new Image();
img.addEventListener('load', myFunction, false);
img.src = 'http://newimgsource.jpg';
photo.src = img.src;
source: http://code.google.com/p/chromium/issues/detail?id=7731#c12
This is strange, none of the above worked for me. I was defining the image variable as local and change it to global and it started working. Does this make sense? Can somebody explain it?
This didnt worked for me:
function loadImage() {
var ImageToLoad = new Image();
ImageToLoad.onload = function() {
console.log("finish loading");
};
ImageToLoad.src = "myimage.png";
}
This did work:
var ImageToLoad = new Image();
function loadImage() {
ImageToLoad.onload = function() {
console.log("finish loading");
};
ImageToLoad.src = "myimage.png";
}