how to get the image name on click in javascript - javascript

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>

Related

How to draw a dragged and droppped image on to a canvas

I drag and drop images on to a canvas using below code . Can someone please tell me how to draw it on a canvas? Thanks in advance.Earlier my canvas used to be a div and I used to append img to that div and it worked.But now I want it to be drawn to a canvas.
function dropb(ev) {
ev.preventDefault();
const dt = ev.dataTransfer;
const files = dt.files;
handleFilesb(files);
}
function handleFilesb(files) {
for (let i = 0; i < files.length; i++) {
const file = files[i];
if (!file.type.startsWith('image/')) {
continue
}
const img = document.createElement("img");
img.className = "my_image";
img.setAttribute("width", "300px");
img.setAttribute("height", "300px");
img.classList.add("obj");
img.file = file;
document.getElementById("area_c"); // canvas
var ctx = c.getContext("2d");
ctx.drawImage(img, 10, 10); // this line is not working
const reader = new FileReader();
reader.onload = (function(aImg) {
return function(e) {
aImg.src = e.target.result;
};
})(img);
reader.readAsDataURL(file);
}
}
<div style="display:flex">
<canvas id ="area_c" style="width:300px;height:300px;border:3px solid black;z-index:1" ondrop="dropb(event)" ondragover="myfkb(event)" >
</canvas>
<div id ="area_c2" style="width:300px;height:300px;border:3px solid black;z-index:1" >
</div>
<div >
<input type="button" value="Crop"
onclick="crop()">
</div>
</div>
ctx.drawImage(img) comes before img.src... There is no way your image gets drawn.
You need to wait until it loads, and that will be only after the FileReader onload event fired, and even after the img's onload fires:
img.onload = e => {
ctx.drawImage(img, 10, 10);
};
const reader = new FileReader();
reader.onload = (function(aImg) {
return function(e) {
aImg.src = e.target.result;
};
})(img);
reader.readAsDataURL(file);
But you don't need the FileReader here, use a blob URL instead:
img.onload = e => {
ctx.drawImage(img, 10, 10);
};
img.src = URL.createObjetURL(file);
const c = document.getElementById("area_c");
c.ondragover = e => {
e.preventDefault();
}
c.ondrop = dropb;
const ctx = c.getContext("2d");
function dropb(ev) {
ev.preventDefault();
const dt = ev.dataTransfer;
const files = dt.files;
handleFilesb(files);
}
function handleFilesb(files) {
for (let i = 0; i < files.length; i++) {
const file = files[i];
if (!file.type.startsWith('image/')) {
continue
}
const img = document.createElement("img");
img.className = "my_image";
img.setAttribute("width", "300px");
img.setAttribute("height", "300px");
img.classList.add("obj");
img.file = file;
img.onload = e => {
ctx.drawImage(img, 10, 10);
};
img.src = URL.createObjectURL(file);
}
}
canvas {
border: 1px solid
}
<canvas id="area_c">
</canvas>

Upload Image, shuffle it and display

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;

Canvas doesn't save as image properly, just blank transparent image

I'm looping through an array of image elements (from a local folder) and attaching them to a canvas and at the end I would like to save the canvas as an image but it is not being saved properly. The resulting image matches the dimensions of the canvas but it is just a blank transparent image.
If in test.js I don't save the canvas to an image and I instead use resolve(canvas) I can display it perfectly in index.html so I know that the canvas is being written properly.
There are also no error messages displayed in the Google Chrome console.
Test.js
class Test {
constructor() {}
createPoster(images) {
return new Promise((resolve, reject) => {
let canvas = document.createElement('canvas');
let ctx = canvas.getContext('2d');
let locX = 0;
for (let i = 0, len = images.length; i < len; ++i) {
let image = images[i];
image.onload = () => {
ctx.drawImage(image, locX, 0);
locX += image.width;
};
}
let poster = new Image();
poster.crossOrigin = 'anonymous';
poster.onload = () => {
resolve(poster);
};
poster.src = canvas.toDataURL('image/png').replace('image/png', 'image/octet-stream');
});
}
}
Index.html
<script type="module">
const Test = require('./test.js');
let test = new Test();
async function main() {
// let images = Get array of image elements from another library.
let poster = await test.createPoster(images).catch((err) => console.log(err));
document.body.appendChild(poster); // Displays just a transparent image with the same dimensions as the canvas.
}
</script>
You need to check for all image loaded before you create your poster image.
createPoster(images) {
return new Promise((resolve, reject) => {
let canvas = document.createElement('canvas');
let ctx = canvas.getContext('2d');
let locX = 0;
let imageLoaded = 0;
for (let i = 0, len = images.length; i < len; ++i) {
let image = images[i];
image.onload = () => {
ctx.drawImage(image, locX, 0);
locX += image.width;
imageLoaded++;
if (imageLoaded == len) {
let poster = new Image();
poster.crossOrigin = 'anonymous';
poster.onload = () => {
resolve(poster);
};
poster.src = canvas.toDataURL('image/png').replace('image/png', 'image/octet-stream');
}
};
// image.src = ..
}
});
}

javascript fabricjs filter do not return filtered canvas to url

export function filter(url) {
var c = document.createElement('canvas')
c.id = "canvas_greyscale"
var canvas = new fabric.Canvas('canvas_greyscale')
fabric.Image.fromURL(url, function(oImg) {
c.height = oImg.height
c.width = oImg.width
oImg.filters.push(new fabric.Image.filters.Grayscale())
oImg.applyFilters(canvas.renderAll.bind(canvas))
canvas.add(oImg)
var img = canvas.toDataURL('image/png')
console.log(img)
return img
}, {crossOrigin: "Anonymous"})
}
Here my canvas is rendered with grayscale filter. It does not have issue but when I try to convert canvas to url it is giving me the canvas without filter.
I dont know whats wrong in here..
What am I doing wrong. I want to convert the canvas that have filter to url
var img = canvas.toDataURL('image/png') gives me image without filter
Need help
applyFilters is asynchronous (that's why you pass a renderAll callback in it).
You need to call toDataURL in its callback otherwise you're exporting the canvas before the filter is applied.
Here is a rough adaptation of your code :
function filter(url, callback) {
var c = document.createElement('canvas');
c.id = "canvas_greyscale";
var canvas = new fabric.Canvas('canvas_greyscale');
// the applyFilters' callback
var onend = function() {
canvas.renderAll();
var img = canvas.toDataURL('image/png');
callback(img);
}
fabric.Image.fromURL(url, function(oImg) {
canvas.setDimensions({width:oImg.width, height:oImg.height});
oImg.filters.push(new fabric.Image.filters.Grayscale())
// here we pass the export function
oImg.applyFilters(onend)
canvas.add(oImg)
}, {
crossOrigin: "Anonymous"
})
}
var url = 'https://upload.wikimedia.org/wikipedia/commons/5/5b/Supernumerary_rainbow_03_contrast.jpg'
filter(url, function(dataURI) {
output.src = dataURI;
original.src = url
})
img{ width: 50%}
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.6.4/fabric.js"></script>
<img id="output"><br>
original Image: © Andrew Dunn CC-By-SA 2.0 <br>
<img id="original">

HTML5 Canvas toDataURL is always the same in a for loop

Consider this JSFiddle. In it I select photos which I then want to base64 encode using canvas so I can store them in sessionStorage for deferred uploading. Because I have it set for multiple files, I loop through each one and create an image and a canvas, but no matter what it just seems to output the exact same base64 encoded image every time. Through testing I know that on each loop iteration the image is different and does indeed point to a different file blob, but the canvas is just outputting the same thing over and over, which I think is also the last file in the files list. Sometimes it will also just output a "data," string and that's it. I'd love it if someone can point me in the right direction.
Code is show below:
HTML
<style type="text/css">
input[type="file"] {
display: none;
}
a {
display: inline-block;
margin: 6px;
}
</style>
<form action="#" method="post">
<input type="file" accept="image/*" multiple />
<button type="button">Select Photos</button>
</form>
<nav></nav>
JavaScript
console.clear();
$(function () {
$("button[type=button]").on("click", function (e) {
$("input[type=file]").trigger("click");
});
$("input[type=file]").on("change", function (e) {
var nav = $("nav").empty();
for (var i = 0, l = this.files.length; i < l; i++) {
var file = this.files[i],
image = new Image();
$(image).on("load", i, function (e) {
var canvas = document.createElement("canvas"),
context = canvas.getContext("2d");
canvas.height = this.height;
canvas.width = this.width;
context.drawImage(this, 0, 0);
nav.append("" + e.data + "");
URL.revokeObjectURL(this.src);
});
image.src = URL.createObjectURL(file);
}
});
});
but no matter what it just seems to output the exact same base64
encoded image every time.
.load() event is asynchronous. You can use $.when() , $.Deferred(), substitute $.map() for for loop to handle asynchronously loaded img elements. The caveat that the displayed a element text may not be in numerical order; which can be adjusted by sorting the elements at .then(); though not addressed at Question, if required, the listing or loading of images sequentially can also be achieved.
$("input[type=file]").on("change", function(e) {
var nav = $("nav").empty();
var file = this.files
$.when.apply($, $.map(file, function(img, i) {
return new $.Deferred(function(dfd) {
var image = new Image();
$(image).on("load", i, function(e) {
var canvas = document.createElement("canvas")
, context = canvas.getContext("2d");
canvas.height = this.height;
canvas.width = this.width;
context.drawImage(this, 0, 0);
nav.append("<a href=\""
+ canvas.toDataURL()
+ "\" target=\"_blank\">"
+ e.data + "</a>");
dfd.resolve()
URL.revokeObjectURL(this.src);
});
image.src = URL.createObjectURL(img);
return dfd.promise()
})
})).then(function() {
nav.find("a").each(function() {
console.log(this.href + "\n");
})
})
});
})
jsfiddle https://jsfiddle.net/bc6x3s02/19/

Categories

Resources