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>
Related
I am using the code in http://jsfiddle.net/epistemex/LUNaJ/
PDFJS.disableWorker = true; // due to CORS
var canvas = document.createElement('canvas'), // single off-screen canvas
ctx = canvas.getContext('2d'), // to render to
pages = [],
currentPage = 1,
url = 'http://www.corsproxy.com/www.ohio.edu/technology/training/upload/Java-Script-Reference-Guide.pdf';
PDFJS.getDocument(url).then(function (pdf) {
PROGRESS.max = pdf.numPages; // just for demo
PROGRESS.value = 1; // just for demo
// init parsing of first page
if (currentPage <= pdf.numPages) getPage();
// main entry point/function for loop
function getPage() {
// when promise is returned do as usual
pdf.getPage(currentPage).then(function(page) {
var scale = 1.5;
var viewport = page.getViewport(scale);
canvas.height = viewport.height;
canvas.width = viewport.width;
var renderContext = {
canvasContext: ctx,
viewport: viewport
};
// now, tap into the returned promise from render:
page.render(renderContext).then(function() {
// store compressed image data in array
pages.push(canvas.toDataURL());
if (currentPage < pdf.numPages) {
currentPage++;
PROGRESS.value = currentPage; // just for demo
getPage(); // get next page
}
else {
done(); // call done() when all pages are parsed
}
});
});
}
});
function done() {
// NOTE: Just for demo - correct order is not guaranteed here
// as the drawPage is async. use same method as above to make
// sure the order is correct (not for-loop, but use the callback
// to get next page). To present a single page it won't be
// a problem though... (just use drawPage() directly)
for(var i = 0; i < pages.length; i++) {
drawPage(i, addPage);
}
}
function addPage(img) {
img.style.width = '100px';
img.style.height = '120px';
document.body.appendChild(img);
}
function drawPage(index, callback) {
var img = new Image;
img.onload = function() {
ctx.drawImage(this, 0, 0, ctx.canvas.width, ctx.canvas.height);
callback(this); // invoke callback when we're done
}
img.src = pages[index]; // start loading the data-uri as source
}
to render pdf pages to canvas. The problem with this is that it takes along time if the pdf file has large number of files. Is it possible to cache/save these generated files in the users computer/bowser so that if he runs the code a secondary time, he doesn't have to generate them again and instead they can be displayed immediately.
No, dataURI are not "cached" by browser since there is no download involved.
What you can do however, is to store all your pages into a globally accessible array, and check if you already have it before you call PDFJS.getDocument(url) :
PDFJS.disableWorker = true; // due to CORS
var canvas = document.createElement('canvas'), // single off-screen canvas
ctx = canvas.getContext('2d'), // to render to
docs = {}, // an object that will store our pdf documents
urls = ["https://www.ohio.edu/technology/training/upload/html-tag-reference-guide.pdf", "https://www.ohio.edu/technology/training/upload/Java-Script-Reference-Guide.pdf"];
btn0.onclick = getDoc;
btn1.onclick = getDoc;
function getDoc() {
// get the doc's url
var url = urls[+this.id.split('btn')[1]];
// clear the result div
result.innerHTML = '';
// we already have it
if (docs[url]) {
// simply call the callback
done(docs[url]);
}
else {
// create our array for this document
docs[url] = [];
// download and parse the doc
PDFJS.getDocument(url).then(function(pdf) {
PROGRESS.max = pdf.numPages; // just for demo
PROGRESS.value = 1; // just for demo
var currentPage = 1;
// init parsing of first page
if (currentPage <= pdf.numPages) getPage();
// main entry point/function for loop
function getPage() {
// when promise is returned do as usual
pdf.getPage(currentPage).then(function(page) {
var scale = 1.5;
var viewport = page.getViewport(scale);
canvas.height = viewport.height;
canvas.width = viewport.width;
var renderContext = {
canvasContext: ctx,
viewport: viewport
};
// now, tap into the returned promise from render:
page.render(renderContext).then(function() {
// store compressed image data in array
docs[url].push(canvas.toDataURL());
if (currentPage < pdf.numPages) {
currentPage++;
PROGRESS.value = currentPage; // just for demo
getPage(); // get next page
} else {
done(docs[url]); // call done() when all pages are parsed
}
});
});
}
});
}
}
function done(pages) {
for (var i = 0; i < pages.length; i++) {
drawPage(pages[i], addPage);
}
}
function addPage(img) {
img.style.width = '100px';
img.style.height = '120px';
result.appendChild(img);
}
function drawPage(dataURI, callback) {
var img = new Image;
img.onload = function() {
ctx.drawImage(this, 0, 0, ctx.canvas.width, ctx.canvas.height);
callback(this); // invoke callback when we're done
}
img.src = dataURI; // start loading the data-uri as source
}
#PROGRESS {
width: 100%
}
<script src="https://rawgit.com/mozilla/pdf.js/gh-pages/build/pdf.js"></script>
<button id="btn0">1st Doc</button>
<button id="btn1">2nd Doc</button>
<progress id="PROGRESS" value=0></progress>
<div id="result"></div>
I'm trying to make a tick method for this code.
When I try to put a while loop or time interval it just goes blank.
I want the tick method to call this function without the canvas going blank.
How would i make that tick method
function setup(){
var canvas = document.getElementById('my_canvas');
var ctx = canvas.getContext('2d');
canvas.width = 800;
canvas.height = 600;
var gun = new Image();
var badguy = new Image();
var wall1 = 200;
var ground = new Image();
var back = new Image();
var back2 = new Image();
var back3 = new Image();
var wall = new Image();
var wall2 = new Image();
back.onload = function() {
ctx.drawImage(back, 0, 0, 800, 300);
};
back2.onload = function() {
ctx.drawImage(back2, mountainplace, 0, mtnsize1, 300);
};
back3.onload = function() {
ctx.drawImage(back3, mountainplace2, 0, mtnsize2, 300);
};
ground.onload = function() {
ctx.drawImage(ground, groundplace, 300, 1980, 200);
};
wall.onload = function() {
ctx.drawImage(wall, place2, 250, size2, 100);
};
wall2.onload = function() {
ctx.drawImage(wall2, place, 250, size, 100);
};
badguy.onload = function() {
ctx.drawImage(badguy, badguyplace, 250, 100, 100);
};
gun.onload = function() {
ctx.drawImage(gun, 0, 100, 400, 400);
};
back2.src = "moutain1.png";
back3.src = "moutain2.png";
back.src = "backing.png";
ground.src = "ground1.jpg";
wall.src = "wall.png";
wall2.src = "wall2.png";
badguy.src = "santa2.png";
gun.src = "gun1.png";
};
You need to clarify the question because I'm not sure what exactly you are trying to achieve.
I assume you put some code at the end of your setup() function that performs some operations on the images. But before you can do it you need to wait for the images to load.
BTW: another problem with your code is that the images will be drawn on the canvas in the order in which they load, which may be unpredictable. You probably want to avoid this too.
A solution to your problem (or at least to what I think your problem is) is to first start loading the images and then only perform further operations after they have all been loaded.
You can use the following code to do this:
function makeAllLoadedHandler(image_files_count, on_all_loaded) {
return function() {
--image_files_count;
if (image_files_count == 0) {
// All images loaded, call the function.
on_all_loaded();
}
}
}
function loadAllImages(image_files, on_all_loaded) {
var images = {};
var callback = makeAllLoadedHandler(image_files.length, function() { on_all_loaded(images); } );
for (var i = 0; i < image_files.length; ++i) {
var image = new Image;
image.src = image_files[i];
image.onload = callback;
images[image_files[i]] = image;
}
}
The loadAllImages() function takes an array of image file names and a function to call when all the images have been loaded.
You can use it like this in your code:
function setup() {
var image_files = [
"mountain1.png",
"mountain2.png",
"backing.png",
"ground1.jpg",
"wall.png",
"wall2.png",
"santa2.png",
"gun1.png" ];
loadAllImages(image_files, onAllImagesLoaded);
}
function onAllImagesLoaded(images) {
// Draw your images and perform all the other tasks on them.
// The 'images' object stores each of the Image object under a key that is
// its file name.
ctx.drawImage(images['backing.png'], 0, 0, 800, 300);
// ...
// Do other stuff with the images.
}
You just call the setup() function like you used to and then the onAllImagesLoaded function will be called some time later when all the images are available. You continue your processing in there.
I hope this helps. Although it's possible that your problem is completely different ;)
I have an array of enemies sent from the server and I am recreating them because they were serialized. After, I'm trying to get them to render on the canvas, but that isn't working for some reason.
for (var i = 0; i < enemies.length; i++) { // recreate each enemy and render it
var image = new Image();
var currentFish = new Fish();
for (var key in enemies[i]) { // copying properties to object that has the necessary methods
if (enemies[i].hasOwnProperty(key)) {
currentFish[key] = enemies[i][key];
}
}
image.src = currentFish.icon;
image.onload = function () {
ctx.drawImage(image, currentFish.position.x, currentFish.position.y);
};
ctx.fillText('Drone', 250, 200);
}
I think the issue is that image.onload is not called until after or in between frames so it isn't seen. I'm not sure how to work around this.
Edit:
I forgot to mention that I'm using requestAnimationFrame to handle rendering the canvas, so I don't know when the frame is going to be rendered.
The problem is that your outside for loop is overwriting the image variable with each loop (before the image can be loaded and drawn).
Try this alternative image loader which preloads all images into imgs[] and then calls start():
// image loader
var imageURLs = []; // put the paths to your images here
var imagesOK = 0;
var imgs = [];
imageURLs.push("");
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() {
// imgs[] holds fully loaded images
// imgs[] is in the same order as imageURLs[]
}
I tried to create an array of Image to be displayed on a canvas, after each image is loaded. No errors, no draw...
var x=...
var y=...
var canvas = document.getElementById(sCanvasName);
var context = canvas.getContext('2d');
var imageCardObj = [];
//vCards contains the images file names
for (var k=0;k<vCards.length;k++){
imageCardObj[k] = new Image();
var func = function(){
var c = arguments[3];
try{
c.drawImage(arguments[0], arguments[1], arguments[2]);
}catch(e){
alert(e.message)
}
}
imageCardObj[k].onload = func(imageCardObj[k], x, y, context);
imageCardObj[k].src = "res/img/"+vCards[k].trim()+".png";
x+=40;
}
You are calling the func() handler and gives the result to it to the image's onload handler. That won't work so well.. and you cannot pass arguments that way to a handler function.
Try this:
var func = function(){
// "this" will be the current image in here
var c = arguments[3];
try{
c.drawImage(this, x, y); // you need to reference x and y
}catch(e){
alert(e.message)
}
}
imageCardObj[k].onload = func; // only a reference here
If you need different x and y's then you need to maintain those on the side, either in an additional array or use objects to embed the image, its intended x and y and use the url to identify the image in question inside the func() callback.
Also note that load order may vary as the last image loaded could finish before the first one so when you draw the image they may not appear in the same order.
You may want to do this instead:
var files = [url1, url2, url, ...],
images = [],
numOfFiles = files.length,
count = numOfFiles;
// function to load all images in one go
function loadImages() {
// go through array of file names
for(var i = 0; i < numOfFiles; i++) {
// create an image element
var img = document.createElement('img');
// use common loader as we need to count files
img.onload = imageLoaded;
//img.onerror = ... handle errors too ...
//img.onabort = ... handle errors too ...
img.src = files[i];
// push image onto array in the same order as file names
images.push(img);
}
}
function imageLoaded(e) {
// for each successful load we count down
count--;
if (count === 0) draw(); //start when all images are loaded
}
Then you can start the drawing after the images has loaded - the images are now in the same order as the original array:
function draw() {
for(var i = 0, img; img = images[i++];)
ctx.drawImage(img, x, y); // or get x and y from an array
}
Hope this helps!
This is the final (working) version
var x=...
var y=...
var canvas = document.getElementById(sCanvasName);
var context = canvas.getContext('2d');
var imageCardObj = [];
for (var k=0;k<vCards.length;k++){
imageCardObj[k] = new Image();
imageCardObj[k].xxx=x;
var func = function(){
try{
context.drawImage(this, this.xxx, yStreet);
}catch(e){
alert(e.message)
}
}
imageCardObj[k].onload = func;
imageCardObj[k].src = 'res/img/'+vCards[k].trim()+".png";
x +=40;
I am creating a Barcode scanner module for Windows 8 Metro App.
I some how success with my logic but suddenly I saw my application crash due to low memory issue.
<script>
var canvas = null;
var ctx = null;
var livePreview = null;
var count = 0,rescount=0;
function takepicture() {
var Capture = Windows.Media.Capture;
livePreview = document.getElementById("live-preview");
var mediaCapture = new Capture.MediaCapture();
canvas = document.getElementById("Vcanvas");
ctx=canvas.getContext('2d');
livePreview.addEventListener('play', function () { var i = window.setInterval(function () { ctx.drawImage(livePreview, 0, 0, canvas.width, canvas.height); scanCanvasEasy(); }, 20); }, false);
livePreview.addEventListener('pause', function () { window.clearInterval(i); }, false);
livePreview.addEventListener('ended', function () { clearInterval(i); }, false);
/*
var openPicker = new Windows.Storage.Pickers.FileOpenPicker();
openPicker.viewMode = Windows.Storage.Pickers.PickerViewMode.thumbnail;
openPicker.suggestedStartLocation = Windows.Storage.Pickers.PickerLocationId.videosLibrary;
openPicker.fileTypeFilter.replaceAll([".mp4", ".avi", ".ogg"]);
openPicker.pickSingleFileAsync()
.then(function (file) {
if (file) {
// draw the image
var img = new Image;
//img.onload = function () {
// canvas.width = img.width;
// canvas.height = img.height;
// ctx.drawImage(img, 0, 0, img.width, img.height);
// scanCanvasEasy();
//}
//img.src = URL.createObjectURL(file);
// open a stream from the image
livePreview.src = URL.createObjectURL(file);
livePreview.play();
}
})*/
mediaCapture.initializeAsync().then(function () {
livePreview.src = URL.createObjectURL(mediaCapture);
livePreview.play();
});
}
function scanCanvasEasy() {
var imgd = ctx.getImageData(0, 0,canvas.width,canvas.height);
var pix = imgd.data;
var reader = new ZXing.BarcodeReader();
reader.onresultpointfound = function (resultPoint) {
// do something with the resultpoint location
console.log(resultPoint.toString());
}
// try to decode the raw pixel data
var result = reader.decode(pix, canvas.width, canvas.height, ZXing.BitmapFormat.rgba32);
/*
The above line cause that memory issue, without that line there is no change in memory level.
*/
// show the result
if (result) {
document.getElementById("result").innerText ="Result(+"+rescount++ +")==>"+ result.text;
}
else {
document.getElementById("error").innerText = "no barcode found" + count++;
}
}
</script>
I posted the whole code i used here I Just called the takepicture() method from button click event.
var result = reader.decode(pix, canvas.width, canvas.height, ZXing.BitmapFormat.rgba32);
This line cause memory issue.
Thanks in advance.
var reader = new ZXing.BarcodeReader();
Multiple instance of reader cause this issue. Just created one instance of reader and use it for all subsequent scan will fixed that issue.