To fill a canvas with several images i am using the following code:
<canvas id="myCanvas"></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');
fitToContainer(canvas);
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, 0,0);
context.drawImage(images.yoda, 0, 0);
});
function fitToContainer(canvas) {
canvas.style.width = '100%';
canvas.style.height = '100%';
canvas.width = canvas.offsetWidth;
canvas.height = canvas.offsetHeight;
}
</script>
Although this is in fact filling the canvas with images, i am not able to position the images in order not to overlap them. I dont know ahead of time the sizes of pictures. Is there any library or framework than can easily do this for me or what direction can i go?
And the canvas has a limited size since it is inside a parent html element.
Just keep track of the accumulated width of your images.
Then draw the next image to the right of that accumulated width
loadImages(sources, function(images) {
var nextX=0;
context.drawImage(images.darthVader, nextX,0);
nextX+=images.darthVader.width;
context.drawImage(images.yoda, nextX, 0);
nextX+=images.yoda.width;
// ...and so on...
});
Related
This question already has answers here:
Callback/scope understanding
(3 answers)
Closed 1 year ago.
I am having trouble drawing an image with CSS filters onto a canvas. I want to apply the CSS filters with sliders/range inputs that I have. The image that the filters will be applied to is an image that you upload from your files. This image is put into a variable in a function I have that will upload the image. The function for the slider is separate, and therefore I can't apply the filters and draw the image again in the function that is activated when the sliders are being dragged.
I need it to be drawn instead of just putting the filters on the canvas itself so that I can download the image with filters.
Here is the javascript code:
var imageLoader = document.getElementById('imageLoader');
imageLoader.addEventListener('change', handleImage, false);
var canvas = document.getElementById('imageCanvas');
var ctx = canvas.getContext('2d');
function handleImage(e) {
var reader = new FileReader();
reader.onload = function(event) {
var img = new Image();
img.onload = function() {
var ratio = this.height / this.width;
canvas.height = canvas.width * ratio;
ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
}
img.src = event.target.result;
}
reader.readAsDataURL(e.target.files[0]);
}
var container = document.getElementById("container")
var download = document.getElementById("download")
var adjustments = document.getElementById("adjustments")
download.addEventListener("click", function() {
var link = document.createElement('a');
link.download = 'filename.png';
link.href = canvas.toDataURL();
link.click();
});
var prop1 = document.getElementById("prop1");
var open = true;
adjustments.addEventListener("click", function() {
if (open === true) {
prop1.style = "margin-left: -240px;";
adjustments.classList.remove("line");
open = false;
} else if (open === false) {
prop1.style = "margin-left: 50px;";
adjustments.classList.add("line");
open = true;
}
});
var contrast = document.getElementById("contrast");
var brightness = document.getElementById("brightness");
var slider1 = contrast.value;
var slider2 = brightness.value;
function effect() {
slider1 = contrast.value;
slider2 = brightness.value;
ctx.filter = "contrast(" + slider1 + ") brightness(" + slider2 + ")";
ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
}
contrast.oninput = function() {
effect()
};
brightness.oninput = function() {
effect()
};
You could try clearing the canvas with ctx.clearRect() then redraw everything on the canvas. I'd also recommend creating a function to redraw the items just for easier reading.
I would like to combine two images (one uploaded from the device and another from the website) and export them as one image. The image uploaded from the device would be the background image and the one from the website is a logo to make in to a watermark in the bottom corner.
Is this possible and if so where should I start?
Thank you
I have been able to add an image on top of another image using the Javascript code below:
function watermarkLogo(elemImage) {
var canvas = document.getElementById("canvas");
canvas.width = elemImage.naturalWidth;
canvas.height = elemImage.naturalHeight;
var myVar = canvas.getContext("2d");
var img1 = loadImage(elemImage.src, myFunction);
var img2 = loadImage('icon.png', myFunction);
var numberOfImages = 0;
function myFunction() {
numberOfImages += 1;
if(numberOfImages == 2) {
myVar.drawImage(img1, 0, 0);
myVar.globalAlpha = 0.5;
var widthOffset = (canvas.width/3)*2;
var heightOffset = (canvas.height/3)*2;
myVar.drawImage(img2, widthOffset-50, heightOffset-50, 100, 100);
}
}
function loadImage(src, onload) {
var img = new Image();
img.onload = onload;
img.src = src;
return img;
}
}
I am currently building an application, in which a user can upload several images and work with them simultaneously. Since some processes performed poorly due to large image files, I want to resize them, before the user gets them.
In my resizer() function I try to resize using canvas. It works, but since the 'canvas.toDataURL()' is inside the img.onload function, I don't know how to return the value and parse it to the handleFiles() function.
Also...I tried some cases in which I got some code from the handleFiles() - like:
var preview = document.getElementById("img"+count);
var surface = document.getElementById("cubface"+count);
var count = counterImg();
preview.src = resizer(e.target.result, count);
surface.src = resizer(e.target.result, count);
And put them in the end of the img.onload function like
var preview = document.getElementById("img"+number);
var surface = document.getElementById("cubface"+number);
preview.src = canvas.toDataURL;
surface.src = canvas.toDataURL;
I got the resize, but I got only the last image from the loop to be processed.
So the questions are:
In the resizer() function, how to return the canvas.toDataURL value which is in img.onload?
Why does the loop cover only the last instance and not every image and how to solve that?
Full code:
JavaScript:
function resizer(base64, number){
// Max size for thumbnail
if(typeof(maxWidth) === 'undefined') maxWidth = 1200;
if(typeof(maxHeight) === 'undefined') maxHeight = 1200;
var img = new Image();
img.src = base64;
// Create and initialize two canvas
var canvas = document.createElement("canvas");
var ctx = canvas.getContext("2d");
var canvasCopy = document.createElement("canvas");
var copyContext = canvasCopy.getContext("2d");
img.onload = function() {
// Determine new ratio based on max size
var ratio = 1;
if (img.width > maxWidth)
ratio = maxWidth / img.width;
else if (img.height > maxHeight)
ratio = maxHeight / img.height;
// Draw original image in second canvas
canvasCopy.width = img.width;
canvasCopy.height = img.height;
copyContext.drawImage(img, 0, 0);
// Copy and resize second canvas to first canvas
canvas.width = img.width * ratio;
canvas.height = img.height * ratio;
ctx.drawImage(canvasCopy, 0, 0, canvasCopy.width, canvasCopy.height, 0, 0, canvas.width, canvas.height);
return canvas.toDataURL();
};
return img.onload;
}
function handleFiles(files) {
for (var i = 0; i < files.length; i++) {
var file = files[i];
var count = counterImg();
var preview = document.getElementById("img"+count);
var surface = document.getElementById("cubface"+count);
var reader = new FileReader();
reader.onload = (function (preview, surface) {
return function (e) {
var newimage = resizer(e.target.result, count);
preview.src = newimage;
surface.src = newimage;
$('#image-cropper'+count).cropit('imageSrc', e.target.result);
}
})(preview, surface);
reader.readAsDataURL(file);
}
}
The variable count is not scoped properly.
There's a delay between the time you declare the function and the time you use it, therefore, each execution ends up with the same value. So the jquery selector will always return the same element. Which explains why only the last image is modified.
Here's a jsfiddle that demonstrate the execution.
https://jsfiddle.net/Lc6bngv5/1/
To fix this, simply pass count to the function that encapsulate your preview and surface :
reader.onload = (function (preview, surface, count) {
return function (e) {
var newimage = resizer(e.target.result, count);
preview.src = newimage;
surface.src = newimage;
$('#image-cropper'+count).cropit('imageSrc', e.target.result);
}
})(preview, surface, count);
For the second question :
The resize function returns a function. It does not return the result of that function. To get the url properly, I would use a callback function :
reader.onload = (function (preview, surface, count) {
return function (e) {
var newimage = resizer(e.target.result, count, function(url){
preview.src = url;
surface.src = url;
$('#image-cropper'+count).cropit('imageSrc', e.target.result);
});
}
})(preview, surface, count);
And you would have to do the following changes in resize :
function resizer(base64, number, cb){
...
img.onload = function() {
...
// return canvas.toDataURL();
cb(canvas.toDataURL());
};
}
I have an image on canvas or a set of images that i draw on the canvas. I can draw them fine but I want to change the window level of the image on canvas by dragging the mouse over it. I've seen a lot of external javascript api's but they're quite huge and i don't want to use them for this purpose.
here's my JSFIDDLE that is basically drawing an image on the canvas
this is how my code looks
// Grab the Canvas and Drawing Context
var canvas = document.getElementById('c');
var ctx = canvas.getContext('2d');
// Create an image element
var img = document.createElement('IMG');
// When the image is loaded, draw it
img.onload = function () {
ctx.drawImage(img, 0, 0);
}
// Specify the src to load the image
img.src = "http://imgsv.imaging.nikon.com/lineup/lens/zoom/normalzoom/af-s_dx_18-140mmf_35-56g_ed_vr/img/sample/sample5_l.jpg";
i need something with basic JS or JQuery. it would be great if you can point me in the right direction using a sample code.
Thanks in advance.
If you want to use JavaScript, try something like:
<script>
var c = document.getElementById("drawingboard");
var ctx = c.getContext("2d");
var mouseDown = 0;
document.body.ontouchstart = function() {
mouseDown+=1;
}
document.body.ontouchend = function() {
mouseDown-=1;
}
function draw(event){
ctx.color='gold';
ctx.fillStyle = "lime";
ctx.drawImage(img,event.clientX,event.clientY);}
</script>
This is what I gained after some research:
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
const rangeMin = level - window / 2;
const rangeMax = level + window / 2;
const totalSize = canvas.width * canvas.height;
for (let idx = 0; idx < totalSize; idx++) {
if (imageData.data[idx] < rangeMin) {
imageData.data[idx] = 0;
} else if (imageData.data[idx] > rangeMax) {
imageData.data[idx] = 255;
}
}
ctx.putImageData(imageData, 0, 0);
I tried rendering PDF document using pdf.js library. I know only basics in javascript and I am new to promises, so at first I followed advice on this page: Render .pdf to single Canvas using pdf.js and ImageData (2. answer). But as a result, I rendered my document with all pages blank. All pictures and colors are fine, but not even a line of text. I also tried some other tutorials, but either I get the same result, or the document is completely missing.
Right now, my code looks like this: (It's almost identical to the tutorial)
function loadPDFJS(pid, pageUrl){
PDFJS.disableWorker = true;
PDFJS.workerSrc = 'pdfjs/build/pdf.worker.js';
var canvas = document.createElement('canvas');
var ctx = canvas.getContext('2d');
var pages = [];
var currentPage = 1;
var url = '/search/nimg/IMG_FULL/' + pid + '#page=1';
PDFJS.getDocument(url).then(function (pdf) {
if(currentPage <= pdf.numPages) getPage();
function getPage() {
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
};
page.render(renderContext).then(function() {
pages.push(canvas.toDataURL());
if(currentPage < pdf.numPages) {
currentPage++;
getPage();
} else {
done();
}
});
});
}
});
function done() {
for(var i = 0; i < pages.length; i++){
drawPage(i, addPage);
}
}
function addPage(img){
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);
}
img.src = pages[index];
}
}
K so I just looked at my code again and I started all over. I made it simpler and I finally got it to work. Now it looks like this:
var canvasContainer = document.getElementById('pdfImageImg');
function loadPDFJS(pid, pageUrl){
PDFJS.workerSrc = 'pdfjs/build/pdf.worker.js';
var currentPage = 1;
var pages = [];
var url = '/search/nimg/IMG_FULL/' + pid + '#page=1';
PDFJS.getDocument(url).then(function(pdf) {
pdf.getPage(currentPage).then(renderPage);
function renderPage(page) {
var height = 700;
var viewport = page.getViewport(1);
var scale = height / viewport.height;
var scaledViewport = page.getViewport(scale);
var canvas = document.createElement('canvas');
var context = canvas.getContext('2d');
canvas.height = scaledViewport.height;
canvas.width = scaledViewport.width;
var renderContext = {
canvasContext: context,
viewport: scaledViewport
};
page.render(renderContext).then(function () {
if(currentPage < pdf.numPages) {
pages[currentPage] = canvas;
currentPage++;
pdf.getPage(currentPage).then(renderPage);
} else {
for (var i = 1; i < pages.length; i++) {
document.getElementById('pdfImageImg').appendChild(pages[i]);
}
}
});
}
});
}
Thank you #user3913960, your concept worked for me. I found some issues in your code which I fixed. Here is the code:
function loadPDFJS(pageUrl) {
PDFJS.workerSrc = 'resources/js/pdfjs/pdf.worker.js';
var currentPage = 1;
var pages = [];
var globalPdf = null;
var container = document.getElementById('pdf-container');
function renderPage(page) {
//
// Prepare canvas using PDF page dimensions
//
var canvas = document.createElement('canvas');
// Link: http://stackoverflow.com/a/13039183/1577396
// Canvas width should be set to the window's width for appropriate
// scaling factor of the document with respect to the canvas width
var viewport = page.getViewport(window.screen.width / page.getViewport(1.0).width);
// append the created canvas to the container
container.appendChild(canvas);
// Get context of the canvas
var context = canvas.getContext('2d');
canvas.height = viewport.height;
canvas.width = viewport.width;
//
// Render PDF page into canvas context
//
var renderContext = {
canvasContext: context,
viewport: viewport
};
page.render(renderContext).then(function () {
if (currentPage < globalPdf.numPages) {
pages[currentPage] = canvas;
currentPage++;
globalPdf.getPage(currentPage).then(renderPage);
} else {
// Callback function here, which will trigger when all pages are loaded
}
});
}
PDFJS.getDocument(pageUrl).then(function (pdf) {
if(!globalPdf){
globalPdf = pdf;
}
pdf.getPage(currentPage).then(renderPage);
});
}
loadPDFJS("somepdffilenamehere.pdf");
The pdfjs-dist library contains parts for building PDF viewer. You can use PDFPageView to render all pages. Based on https://github.com/mozilla/pdf.js/blob/master/examples/components/pageviewer.html :
var url = "https://cdn.mozilla.net/pdfjs/tracemonkey.pdf";
var container = document.getElementById('container');
// Load document
PDFJS.getDocument(url).then(function (doc) {
var promise = Promise.resolve();
for (var i = 0; i < doc.numPages; i++) {
// One-by-one load pages
promise = promise.then(function (id) {
return doc.getPage(id + 1).then(function (pdfPage) {
// Add div with page view.
var SCALE = 1.0;
var pdfPageView = new PDFJS.PDFPageView({
container: container,
id: id,
scale: SCALE,
defaultViewport: pdfPage.getViewport(SCALE),
// We can enable text/annotations layers, if needed
textLayerFactory: new PDFJS.DefaultTextLayerFactory(),
annotationLayerFactory: new PDFJS.DefaultAnnotationLayerFactory()
});
// Associates the actual page with the view, and drawing it
pdfPageView.setPdfPage(pdfPage);
return pdfPageView.draw();
});
}.bind(null, i));
}
return promise;
});
#container > *:not(:first-child) {
border-top: solid 1px black;
}
<link href="https://npmcdn.com/pdfjs-dist/web/pdf_viewer.css" rel="stylesheet"/>
<script src="https://npmcdn.com/pdfjs-dist/web/compatibility.js"></script>
<script src="https://npmcdn.com/pdfjs-dist/build/pdf.js"></script>
<script src="https://npmcdn.com/pdfjs-dist/web/pdf_viewer.js"></script>
<div id="container" class="pdfViewer singlePageView"></div>
See also How to display whole PDF (not only one page) with PDF.JS?
I have used below code to render a pdf with multiple pages.
PDFJS.disableWorker = true; // due to CORS
var canvas = document.createElement('canvas'),
ctx = canvas.getContext('2d'),
pages = [],
currentPage = 1,
url = 'your_pdf.pdf';
PDFJS.getDocument(url).then(function(pdf) {
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;
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++;
getPage(); // get next page
} else {
// after all the pages are parsed
for (var i = 0; i < pages.length; i++) {
drawPage(i);
}
}
});
});
}
});
function drawPage(index, callback) {
var img = new Image;
img.onload = function() {
ctx.drawImage(this, 0, 0, ctx.canvas.width, ctx.canvas.height);
if (index > 0) img.style.display = 'inline-block';
document.body.appendChild(img);
}
img.src = pages[index]; // start loading the data-uri as source
}