Why my canvas getImageData doesn't work? - html

I am trying to pick up a color from a image pixel with that simple code :
<body>
<img src="image.png" id="monImage"/>
<canvas id="myCanvas"></canvas>
<script src="jquery.js"></script>
<script>
$(function() {
var img = $('#monImage')[0];
var canvas = $('#myCanvas')[0];
canvas.width = img.width;
canvas.height = img.height;
canvas.getContext('2d').drawImage(img, 0, 0, img.width, img.height);
var pixelData = canvas.getContext('2d').getImageData(0, 0, 1, 1);
alert("test");
});
</script>
</body>
I am using jquery so it's easier to select element. If I comment this line :
var pixelData = canvas.getContext('2d').getImageData(1, 1, 1, 1);
Then the alert() works and apears on my screen. But if I don't comment it doesn't, so that line doesn't works. Why ?
Thanks

At the point your function executes the image is not done loading (image data is not available).
The image element is valid however so you can read its properties. This is why it doesn't fail before, but its width and height properties will be 0.
A little bitsurprising is that you can execute the line with drawImage. However at the point you get to getImageData there is no data to retrieve and therefor it will fail.
You need to either put your code inside a window load event handler or add an event listener to the image element.
For example:
window.onload = function() {
/// your function goes here
}

Related

how to render offscreen canvas properly

I'm trying to create a basic example of offscreen rendering canvas but I'm error in js "cannot read property of context". actually my idea is to create a demo like I saw in https://yalantis.com/ I want to create my name initial. If there is any better idea to achieve this then please enlighten me.
Thanks here is my basic attempt before the actual implementation :)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Off Screen Canvas</title>
<script>
function createOffscreenCanvas() {
var offScreenCanvas= document.createElement('canvas');
offScreenCanvas.width= '1360px';
offScreenCanvas.height= '400px';
var context= offScreenCanvas.getContext("2d");
context.fillRect(10,10,200,200);
}
function copyToOnScreen(offScreenCanvas) {
var onScreenContext=document.getElementById('onScreen').getContext('2d');
var offScreenContext=offScreenCanvas.getContext('2d');
var image=offScreenCanvas.getImageData(10,10,200,200);
onScreenContext.putImageData(image,0,0);
}
function main() {
copyToOnScreen(createOffscreenCanvas());
}
</script>
<style>
#onScreen {
width:1360px;
height: 400px;
}
</style>
</head>
<body onload="main()">
<canvas id="onScreen"></canvas>
</body>
</html>
You could achieve this in the following way ...
function createOffscreenCanvas() {
var offScreenCanvas = document.createElement('canvas');
offScreenCanvas.width = '1360';
offScreenCanvas.height = '400';
var context = offScreenCanvas.getContext("2d");
context.fillStyle = 'orange'; //set fill color
context.fillRect(10, 10, 200, 200);
return offScreenCanvas; //return canvas element
}
function copyToOnScreen(offScreenCanvas) {
var onScreenContext = document.getElementById('onScreen').getContext('2d');
onScreenContext.drawImage(offScreenCanvas, 0, 0);
}
function main() {
copyToOnScreen(createOffscreenCanvas());
}
canvas {
border: 1px solid black;
}
<body onload="main()">
<canvas id="onScreen" width="1360" height="400"></canvas>
note : never set canvas's width and height using css. instead use the native width and height property of the canvas.
You can try to do that with experimental OffScreenCanvas API. I'm leaving the link here.
So you don't actually need a canvas element attached to DOM with this experimental API, furthermore you can perform your drawing in webworkers, so it will not block the browser's main thread if you're drawing thousands of objects.
const offscreen = new OffscreenCanvas(256, 256);
offscreen.getContext("2d") // or webgl, etc.
Please beware that it's experimental. You can try to check like "OffscreenCanvas" in window and use that, if it's not available, you can fallback to document.createElement("canvas").
Return offScreenCanvas in your function createOffscreenCanvas
function createOffscreenCanvas() {
var offScreenCanvas= document.createElement('canvas');
offScreenCanvas.width= '1360px';
offScreenCanvas.height= '400px';
var context= offScreenCanvas.getContext("2d");
context.fillRect(10,10,200,200);
return offScreenCanvas;
}
Edit
You were getting image date from canvas not context.
function copyToOnScreen(offScreenCanvas) {
var onScreenContext=document.getElementById('onScreen').getContext('2d');
var offScreenContext = offScreenCanvas.getContext('2d');
var image=offScreenContext.getImageData(10,10,200,200);
onScreenContext.putImageData(image,0,0);
}

How to integrate a progress bar into angularjs directive with file upload

I need to create a progress bar for file uploads. I know my progress event listener is working. Is there are more "angular way" to be doing this? How can I update the progress bar from inside my event listener?
As a preface, please feel free to correct and critique the general logic flow if it too needs help.
I have this canvas...
<canvas id="progress"></canvas>
I have an angularjs directive that uploads files. I added a progress event listener (only showing relevant parts)...
link: function postLink(scope, element, attrs, ctrl) {
var fileUpload = function (img, file) {
var xhr = new XMLHttpRequest();
xhr.upload.addEventListener("progress", function(e) {
if (e.lengthComputable) {
var percentage = Math.round((e.loaded * 100) / e.total);
// UPDATE THE PROGRESS BAR HERE
}
}, false);
}
element.bind('change', function (evt) {
var files = evt.target.files;
var canvas = document.getElementById("progress"),
context = canvas.getContext("2d");
context.fillStyle = 'lighgray';
context.fillRect(0, 0, 200, 18);
context.moveTo(0, 0);
context.fillStyle = 'darkgray';
context.fillRect(0, 0, 1, 18);
for(var i = 0; i < files.length; i++) {
fileUpload(files, files[i]);
}
}
}
Have a look at Angular UI Bootstrap: http://angular-ui.github.io/bootstrap/
It provides directives for Bootstrap UI elements including the progress bar.
It looks like you'd just have to bind the upload progress value with the bar, and it will automatically update.
(Gotta love two-way data binding.)
take a look at HTML5 progress tag, your code will look like
if (e.lengthComputable) {
var percentage = Math.round((e.loaded * 100) / e.total);
$('progress').val(percentage);
}
I do know it has many problems with compatibility and it looks different in different browsers (though you can fix it using css3: nice example)
in your case you should fill rectangle again and again
if (e.lengthComputable) {
var percentage = e.loaded / e.total;
context.fillRect(0, 0, progressWidth*percentage, 18);
}
I get rid of *100% because you'd better use var progressWidth = 200 when you create context
look this example, it shows better what I mean (and sorry for my English)

How to print a canvas element?

I have a canvas element on my page. I draw an image over it and some data that the user entered. On a press of a button I want to send the canvas to printer, to print it on paper. I tried to use this plug-in: jQuery.printElement, like that:
the button code:
PRINT
'print_voucher()' function:
function print_voucher()
{
$("#canvas_voucher").printElement();
}
canvas_voucher is the ID of my canvas element. It printed the page, but didn't print the canvas. I tried to use it like that as well:
$("#canvas_voucher img").printElement();
But that didn't even start the printer.
So how can I do that? How can I print the content of the canvas?
**EDIT**
Here's the code that creates my canvas and tries to create an image with it:
function create_voucher(visitor_name, visitor_identity_num, unique_number)
{
var canvas = $("#canvas_voucher")[0];
if (canvas.getContext)
{
var ctx = canvas.getContext('2d');
// Draw image
var img = new Image();
img.src = 'https://someurl.com/image.jpg';
img.onload = function(){
ctx.drawImage(img,0,0);
ctx.fillStyle="#CCC";
ctx.font="bold 20px Arial";
ctx.fillText(visitor_name, 750, 270);
ctx.fillText(visitor_identity_num, 750, 295);
ctx.font="bold 25px Arial";
ctx.fillText(unique_number, 620, 325);
}
var voucher = canvas.toDataURL("image/png");
$("#voucher_img").attr("src", voucher);
} else {
alert('You need Safari or Firefox 1.5+ to see this.');
}
}
This will convert the canvas to a .png image URL and open it in a new browser window
The Print Dialog is triggered to let the user print the page.
function print_voucher(){
var win=window.open();
win.document.write("<br><img src='"+canvas.toDataURL()+"'/>");
win.print();
win.location.reload();
}
Here is example code:
<style>
body{ background-color: ivory; }
canvas{border:1px solid red;}
</style>
<script>
$(function(){
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
ctx.fillStyle="gold";
ctx.strokeStyle="blue";
ctx.lineWidth=5;
ctx.rect(50,50,100,100);
ctx.fill();
ctx.stroke();
function print_voucher(){
var win=window.open();
win.document.write("<br><img src='"+canvas.toDataURL()+"'/>");
win.print();
win.location.reload();
}
$("#printVoucher").click(function(){ print_voucher(); });
}); // end $(function(){});
</script>
</head>
<body>
<canvas id="canvas" width=300 height=300></canvas><br>
<button id="printVoucher">Print</button>
</body>
</html>
Found the problem and fixed it.
Apparently it was a security issue at this line:
img.src = 'https://someurl.com/image.jpg';
Once it was pointing out to a server, it was considered as a potential security threat. So I changed it to:
img.src = 'images/image.jpg';
After that I created a function to make an image from the canvas and called it within the 'img.onload' part:
...
img.onload = function(){
ctx.drawImage(img,0,0);
ctx.fillStyle="#CCC";
ctx.font="bold 20px Arial";
ctx.fillText(visitor_name, 750, 270);
ctx.fillText(visitor_identity_num, 750, 295);
ctx.font="bold 25px Arial";
ctx.fillText(unique_number, 620, 325);
draw_voucher_img();
...
function draw_voucher_img()
{
var canvas = $("#canvas_voucher")[0];
var voucher = canvas.toDataURL();
$("#voucher_img").attr("src", voucher);
}
Now it worked!

I'm unable to get image in canvas in html5, what to do?

I have checked this code from my w3schools where its working well.
But when I run this code in my browser, it's not working.
The image does not get displayed in the canvas. Also, I tried the same code in w3schools browser somewhere else, but still it's not working in that browser either.
<!DOCTYPE html>
<html>
<body>
<p>Image to use:</p>
<img id="scream" src="flower.jpg" alt="The Scream" width="220" height="277"><p>Canvas:</p>
<canvas id="myCanvas" width="250" height="300" style="border:1px solid #d3d3d3;">
<script>
var c=document.getElementById("myCanvas");
var ctx=c.getContext("2d");
var img=document.getElementById("scream");
ctx.drawImage(img,10,10);
</script>
</body>
</html>
I would use the image directly, instead of taking it out of the <img>-tag
var c = document.getElementById('myCanvas');
var ctx = canvas.getContext('2d');
var imageObj = new Image();
imageObj.onload = function() {
ctx.drawImage(imageObj, 10, 10);
};
imageObj.src = 'flower.jpg';
The problem is that load of an image is asynchronous, so your Javascript code might run before the image finish his loading. Because of that when ctx.drawImage(img, 10, 10) is called img have no complete data and draws nothing on canvas.
To solve that you need to wait the image to completely load. Javascript give you the ability to setup a function to run when an image is completely loaded.
All code that depends of the image data should be put inside the onload callback.
img.onload = function(){
ctx.drawImage(img,10,10);
}
Your script with the modified parts:
<script>
var c=document.getElementById("myCanvas");
var ctx=c.getContext("2d");
var img=document.getElementById("scream");
img.onload = function(){
ctx.drawImage(img,10,10);
}
</script>
If the src is correct doesn't matter if the image is loaded from a html tag or created dynamically in Javascript with new Image(), the onload method works for both.
You probably need to run the javascript on load, rather than directly in body. Change you javascript into this and it should work (at least it does in this fiddle):
function setup_canvas() {
var c=document.getElementById("myCanvas");
var ctx=c.getContext("2d");
var img=document.getElementById("scream");
ctx.drawImage(img,10,10);
}
window.onload = setup_canvas;
Also, as described in this thread explicitly setting window.onload is not a very good practice, as it might be overwritten by subsequent scripts, so for anything other than testing you would want to use a framework or addEventListener/attachEvent.

Rendering HTML elements to <canvas>

Is there a way to have an arbitrary HTML element rendered in a canvas (and then access its buffer...).
You won't get real HTML rendering to <canvas> per se currently, because canvas context does not have functions to render HTML elements.
There are some emulations:
html2canvas project http://html2canvas.hertzen.com/index.html (basically a HTML renderer attempt built on Javascript + canvas)
HTML to SVG to <canvas> might be possible depending on your use case:
https://github.com/miohtama/Krusovice/blob/master/src/tools/html2svg2canvas.js
Also if you are using Firefox you can hack some extended permissions and then render a DOM window to <canvas>
https://developer.mozilla.org/en-US/docs/HTML/Canvas/Drawing_Graphics_with_Canvas?redirectlocale=en-US&redirectslug=Drawing_Graphics_with_Canvas#Rendering_Web_Content_Into_A_Canvas
Take a look on MDN (archived)
It will render html element using creating SVG images.
For Example:
There is <em>I</em> like <span style="color:white; text-shadow:0 0 2px blue;">cheese</span> HTML element. And I want to add it into <canvas id="canvas" style="border:2px solid black;" width="200" height="200"></canvas> Canvas Element.
Here is Javascript Code to add HTML element to canvas.
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
var data = '<svg xmlns="http://www.w3.org/2000/svg" width="200" height="200">' +
'<foreignObject width="100%" height="100%">' +
'<div xmlns="http://www.w3.org/1999/xhtml" style="font-size:40px">' +
'<em>I</em> like <span style="color:white; text-shadow:0 0 2px blue;">cheese</span>' +
'</div>' +
'</foreignObject>' +
'</svg>';
var DOMURL = window.URL || window.webkitURL || window;
var img = new Image();
var svg = new Blob([data], {
type: 'image/svg+xml;charset=utf-8'
});
var url = DOMURL.createObjectURL(svg);
img.onload = function() {
ctx.drawImage(img, 0, 0);
DOMURL.revokeObjectURL(url);
}
img.src = url;
<canvas id="canvas" style="border:2px solid black;" width="200" height="200"></canvas>
Here is code to render arbitrary HTML into a canvas:
function render_html_to_canvas(html, ctx, x, y, width, height) {
var xml = html_to_xml(html);
xml = xml.replace(/\#/g, '%23');
var data = "data:image/svg+xml;charset=utf-8,"+'<svg xmlns="http://www.w3.org/2000/svg" width="'+width+'" height="'+height+'">' +
'<foreignObject width="100%" height="100%">' +
xml+
'</foreignObject>' +
'</svg>';
var img = new Image();
img.onload = function () {
ctx.drawImage(img, x, y);
}
img.src = data;
}
function html_to_xml(html) {
var doc = document.implementation.createHTMLDocument('');
doc.write(html);
// You must manually set the xmlns if you intend to immediately serialize
// the HTML document to a string as opposed to appending it to a
// <foreignObject> in the DOM
doc.documentElement.setAttribute('xmlns', doc.documentElement.namespaceURI);
// Get well-formed markup
html = (new XMLSerializer).serializeToString(doc.body);
return html;
}
example:
const ctx = document.querySelector('canvas').getContext('2d');
const html = `
<p>this
<p>is <span style="color:red; font-weight: bold;">not</span>
<p><i>xml</i>!
<p><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAABWElEQVQ4jZ2Tu07DQBBFz9jjvEAQqAlQ0CHxERQ0/AItBV9Ew8dQUNBQIho6qCFE4Nhex4u85OHdWAKxzfWsx0d3HpazdGITA4kROjl0ckFrnYJmQlJrKsQZxFOIMyEqIMpADGhSZpikB1hAGsovdxABGuepC/4L0U7xRTG/riG3J8fuvdifPKnmasXp5c2TB1HNPl24gNTnpeqsgmj1eFgayoHvRDWbLBOKJbn9WLGYflCCpmM/2a4Au6/PTjdH+z9lCJQ9vyeq0w/ve2kA3vaOnI6k4Pz+0Y24yP3Gapy+Bw6qdfsCRZfWSWgclCCVXTZu5LZFXKJJ2sepW2KYNCENB3U5pw93zLoDjNK6E7rTFcgbkGYJtiLckxCiw4W1OURsxUE5BokQiQj3JIToVtKwlhsurq+YDYbMBjuU/W3KtT3xIbrpAD7E60lwQohuaMtP8ldI0uMbGfC1r1zyWPUAAAAASUVORK5CYII=">`;
render_html_to_canvas(html, ctx, 0, 0, 300, 150);
function render_html_to_canvas(html, ctx, x, y, width, height) {
var data = "data:image/svg+xml;charset=utf-8," + '<svg xmlns="http://www.w3.org/2000/svg" width="' + width + '" height="' + height + '">' +
'<foreignObject width="100%" height="100%">' +
html_to_xml(html) +
'</foreignObject>' +
'</svg>';
var img = new Image();
img.onload = function() {
ctx.drawImage(img, x, y);
}
img.src = data;
}
function html_to_xml(html) {
var doc = document.implementation.createHTMLDocument('');
doc.write(html);
// You must manually set the xmlns if you intend to immediately serialize
// the HTML document to a string as opposed to appending it to a
// <foreignObject> in the DOM
doc.documentElement.setAttribute('xmlns', doc.documentElement.namespaceURI);
// Get well-formed markup
html = (new XMLSerializer).serializeToString(doc.body);
return html;
}
<canvas></canvas>
The CSS element() function may eventually help some people here, even though it's not a direct answer to the question. It allows you to use an element (and all children, including videos, cross-domain iframes, etc.) as a background image (and anywhere else that you'd normally use url(...) in your CSS code). Here's a blog post that shows what you can do with it.
It has been implemented in Firefox since 2011, and is being considered in Chromium/Chrome (don't forget to give the issue a star if you care about this functionality).
RasterizeHTML is a very good project, but if you need to access the canvas it wont work on chrome. due to the use of <foreignObject>.
If you need to access the canvas then you can use html2canvas
I am trying to find another project as html2canvas is very slow in performance
According to the HTML specification you can't access the elements of the Canvas. You can get its context, and draw in it manipulate it, but that is all.
BUT, you can put both the Canvas and the html element in the same div with a aposition: relative and then set the canvas and the other element to position: absolute.
This ways they will be on the top of each other. Then you can use the left and right CSS properties to position the html element.
If the element doesn't shows up, maybe the canvas is before it, so use the z-index CSS property to bring it before the canvas.