How to load dynamically created img element with jquery into HTML5 canvas - javascript

I cannot load/ display image created with jquery on HTML5 canvas but it works when image object is created as = new Image();
In particular, using this script as example: https://www.html5canvastutorials.com/advanced/html5-canvas-invert-image-colors-tutorial/
but substituting img source with:
imageObj.src = 'https://demo.boundlessgeo.com/geoserver/ows?&service=WMS&request=GetMap&layers=nasa%3Abluemarble&styles=&format=image%2Fjpeg&transparent=false&version=1.1.1&width=256&height=256&srs=EPSG%3A3857&bbox=0,2504688.542848655,2504688.5428486555,5009377.085697314';
and adding attribute: imageObj.crossOrigin = "Anonymous";
just after: var imageObj = new Image();
it allows for editing function to run and the image draws on canvas.
However, by creating the image with the following jquery, the image is not displayed in canvas and no errors are reported in the console:
var imageObj = $("<img />",
{
crossOrigin: "Anonymous",
src: "https://demo.boundlessgeo.com/geoserver/ows?&service=WMS&request=GetMap&layers=nasa%3Abluemarble&styles=&format=image%2Fjpeg&transparent=false&version=1.1.1&width=256&height=256&srs=EPSG%3A3857&bbox=0,2504688.542848655,2504688.5428486555,5009377.085697314"
});
Am I missing a step that binds img to document or is it something else?

In order to change the image creation in the example code you forgot the load event. Hence, your code should be:
var imageObj = $("<img />", {
crossOrigin: "Anonymous",
src: "https://demo.boundlessgeo.com/geoserver/ows?&service=WMS&request=GetMap&layers=nasa%3Abluemarble&styles=&format=image%2Fjpeg&transparent=false&version=1.1.1&width=256&height=256&srs=EPSG%3A3857&bbox=0,2504688.542848655,2504688.5428486555,5009377.085697314"
}).on('load', function (e) {
drawImage(this);
});
var imageObj = $("<img />", {
crossOrigin: "Anonymous",
src: "https://demo.boundlessgeo.com/geoserver/ows?&service=WMS&request=GetMap&layers=nasa%3Abluemarble&styles=&format=image%2Fjpeg&transparent=false&version=1.1.1&width=256&height=256&srs=EPSG%3A3857&bbox=0,2504688.542848655,2504688.5428486555,5009377.085697314"
}).on('load', function (e) {
drawImage(this);
});
function drawImage(imageObj) {
var canvas = document.getElementById('myCanvas');
var context = canvas.getContext('2d');
var x = 69;
var y = 50;
context.drawImage(imageObj, x, y);
var imageData = context.getImageData(x, y, imageObj.width, imageObj.height);
var data = imageData.data;
for (var i = 0; i < data.length; i += 4) {
// red
data[i] = 255 - data[i];
// green
data[i + 1] = 255 - data[i + 1];
// blue
data[i + 2] = 255 - data[i + 2];
}
// overwrite original image
context.putImageData(imageData, x, y);
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<canvas id="myCanvas" width="578" height="400"></canvas>

Related

How do I make the canvas display the recoloured image instead of the same image?

I had an image of Canada and manually recolored its pixels to shades of blue. I then tried to make a script that would do the same(into red) without me going through all the hassle.
But for some reason, the canvas simply repaints the same image of the blue Canada in Mozilla and Chrome(sometimes not even draws it).
Mozilla says accessing ImageData is a security error. What did I get wrong and how do I fix it?
Snippet right here:
canvas = document.getElementById("canvas");
context = canvas.getContext("2d");
var imgPath = "https://upload.wikimedia.org/wikipedia/commons/thumb/d/d8/Copper_sulfate.jpg/234px-Copper_sulfate.jpg";
var imgObj = new Image();
imgObj.crossOrigin = "anonymous";
imgObj.src = imgPath;
imgObj.onload = function() {
context.drawImage(imgObj, 0, 0);
map = context.getImageData(0, 0, 234, 240);
imagedata = map.data;
for (p = 0; p < imagedata.length; p += 4) {
imagedata[p] = imagedata[p + 2];
imagedata[p + 2] = 0;
imagedata[p + 3] = 255;
};
context.putImageData(map, 0, 0);
};
<img id="Canada" src="https://upload.wikimedia.org/wikipedia/commons/thumb/d/d8/Copper_sulfate.jpg/234px-Copper_sulfate.jpg" />
<canvas id='canvas' width='234px' height='240px'></canvas>

Tainted canvas error thrown when refreshing the page with F5

I have problem with my code, which is quite similar to this CodePen.
var img = new Image();
img.src = 'https://upload.wikimedia.org/wikipedia/commons/thumb/6/63/Diceros_bicornis.jpg/320px-Diceros_bicornis.jpg';
img.crossOrigin = "Anonymous"
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
img.onload = function() {
ctx.drawImage(img, 0, 0);
img.style.display = 'none';
};
var color = document.getElementById('color');
function pick(event) {
var x = event.layerX;
var y = event.layerY;
var pixel = ctx.getImageData(x, y, 1, 1);
var data = pixel.data;
var rgba = 'rgba(' + data[0] + ', ' + data[1] +
', ' + data[2] + ', ' + (data[3] / 255) + ')';
color.style.background = rgba;
color.textContent = rgba;
}
canvas.addEventListener('mousemove', pick);
<!-- Learn about this code on MDN: https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Tutorial/Pixel_manipulation_with_canvas -->
<canvas id="canvas" width="300" height="227" style="float:left"></canvas>
<div id="color" style="width:200px;height:50px;float:left"></div>
It somehow certainly works when the page is first open. But when I refresh the page with F5, it SOMETIMES starts throwing the tainted canvas error. Only when I click x to close the tab, and then click the link again to open a brand new tab will it certainly work again.
I already have img.crossOrigin = "Anonymous".
That sounds like a chrome bug, but easily workaround-able :
Set your crossOrigin attribute first.
The first time the request is made, the crossOrigin attribute is set before the image has loaded, hence your browser will redo the request.
But when you reload the page, the image is already cached, and loads before the crossOrigin attribute is set. Then your canvas will be tainted.
For similar little bugs cases, you should also define your onload event handler before setting this src attribute :
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
var img = new Image();
img.crossOrigin = "Anonymous"
img.onload = function() {
ctx.drawImage(img, 0, 0);
img.style.display = 'none';
};
// set the src last
img.src = 'https://upload.wikimedia.org/wikipedia/commons/thumb/6/63/Diceros_bicornis.jpg/320px-Diceros_bicornis.jpg';
var color = document.getElementById('color');
function pick(event) {
var x = event.layerX;
var y = event.layerY;
var pixel = ctx.getImageData(x, y, 1, 1);
var data = pixel.data;
var rgba = 'rgba(' + data[0] + ', ' + data[1] +
', ' + data[2] + ', ' + (data[3] / 255) + ')';
color.style.background = rgba;
color.textContent = rgba;
}
canvas.addEventListener('mousemove', pick);
<!-- Learn about this code on MDN: https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Tutorial/Pixel_manipulation_with_canvas -->
<canvas id="canvas" width="300" height="227" style="float:left"></canvas>
<div id="color" style="width:200px;height:50px;float:left"></div>

Bing Maps API v8 - pushpin SVG URI image

I'm trying to build a custom bing maps v8 API pushpin combining text and a small uri data image, but I get an 'Uncaught InvalidStateError: Failed to execute 'drawImage' on 'CanvasRenderingContext2D': The HTMLImageElement provided is in the 'broken' state.' error on the browser console. Any ideas?
MapOptions = { credentials: 'mycredentials', mapTypeId: Microsoft.Maps.MapTypeId.road, zoom: 10, showMapTypeSelector: true, enableInertia: false, enableClickableLogo: false, disableKeyboardInput: true, navigationBarMode: navigationBarMode.minified, showLocateMeButton: true };
map = new Microsoft.Maps.Map(document.getElementById("MapDIV"), MapOptions);
LayerShapes = new Microsoft.Maps.Layer();
LayerShapes.setZIndex(101);
map.layers.insert(LayerShapes);
var svgTemplate = '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32"><foreignObject width="100%" height="100%"><div xmlns="http://www.w3.org/1999/xhtml"> <p>TEXT</p> {htmlContent} </div></foreignObject></svg>';
var customHtml = ' <image width="32" height="32" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABkAAAAcCAYAAACUJBTQAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3wQbECUudScMXAAAAB1pVFh0Q29tbWVudAAAAAAAQ3JlYXRlZCB3aXRoIEdJTVBkLmUHAAACGUlEQVRIx+3Wy2sTURTH8e/NTDIzaZMxadMWhyBUSheiiyo+QPpHuHIhdOfSP8GlbkXEhTv/gNau3LgRurEIUqlWU2ubh7evPEg6NjOZJHNdlIgLo11YRcj5A84Hfpx7zxFKKcUJlw7gOM6JAVLKIwTg4avbfxy4c/UJABH+Qg2QAfKfI98f48vc/CCuATJA/iEilFKq3/q98XTk2I0W5qp916/41SHhOM6xoIW5KlLK/t/K6oNbwlAdknELYSZpxTMkxrO4XoCUUv0O6gHlYkjHWxF+yyWTsKit57CGbbTMGSJWepTh05PIRof3mxLNjNP0Pdp+i9ziIyGl7BtFD1hdOqRdei5ijW2shkSvS8LAJTM2gh4JiWzvFNksFdAsA3s0Ram4TrtZJxnXCLwKWSF+CvWAt89czmffiEQ0gGYZzSuTX3tNx60Q1Pcxwyb67JUL7Jb38VsdojETz2ux8W6JqG6iJaOoGLTr98WP0fWAsZgQ849v8mnZYeriLNinwAup722RsW12cysYiRT62voGwymbbsQgMZREcMD1yzN4nkctrNEV4HbrTKeFKNeOJlFKiXtwV2ganJvKkF8rsvxiEd8P0FSTiXQa2wxJxEz2yl/QA2Mc2Qihq7NdqdE5rJAc2ufsZBbTiIIGXWXTVeCIa0glMQwh8vl7hMDHD5+Zmb7E16ZPtVrFilnsFLY42CngTDhEohbfALpF/s+4JwbyAAAAAElFTkSuQmCC"/> ';
var PinPos = new Microsoft.Maps.Location(Lat, Lng);
pin = new Microsoft.Maps.Pushpin(PinPos, { icon: svgTemplate.replace('{htmlContent}', customHtml), width: 32});
LayerShapes.add(pin);
I came across the same issue.
It happens because it tries to draw pushpin icons with unloaded images.
The general idea is to preload images in JS code before using it in pushpin icons.
I solved it by this way:
I preload images to array
I used canvas instead of 'svg'
var imageArray = [];
var imagesFiles = ['home_16.png', 'home_16_done.png', 'not existing.png'];
function preLoadImages(callback) {
var filesCount = imagesFiles.length;
for (var i = 0; i < imagesFiles.length; i++) {
var imageObj = new Image();
imageObj.src = '/images/' + imagesFiles[i];
imageObj.name = imagesFiles[i];
imageObj.onload = (function (img) {
imageArray.push({ name: img.target.name, object: img.target });
filesCount--;
if (filesCount === 0) {
if (callback)
callback(imageArray);
}});
imageObj.onerror = function () { //even image is not loaded
filesCount--;
if (filesCount === 0) {
if (callback)
callback(imageArray);
}};
}
}
....
pushpin = new Microsoft.Maps.Pushpin(location,
{ icon: createCanvasContent(imageArray[i], parameters[])
.....
function createCanvasContent(img, parameters){
var c = document.createElement('canvas');
c.width = img.width + 2;
c.height = img.height + 2;
var context = c.getContext('2d');
context.fillStyle = bgrcolor;
context.fillRect(0, 0, 18, 18);
c.style.opacity = '0.8';
//Draw image
context.drawImage(img, 1, 0, c.width - 2, c.height - 2);
return c.toDataURL();
}
You can use all features from canvas tag like
context.textAlign = "center";
context.strokeText(text, x, y);
context.fillStyle = 'white';
context.fillText(text, x, y);

Images not loading and not displaying on HTML canvas

I am working on a website that, when loaded, it displays the compiled image from an HTML canvas element. My problem is that I can't tell when an image is loaded to draw it to the canvas or how to force an image to be loaded before I proceed to the next step. I have been looking through previous posts about the subject and have tried a lot of different solutions but nothing is working for me.
I am using web2py so I do use some Python helpers which are run on the server side.
When I use this:
canvas = document.getElementById('canvas');
context = canvas.getContext('2d');
image1 = new Image();
image1.src = '{{=URL('static', 'images/image-wrap-border-land.png')}}'; //resolves to: '/SNotes/static/images/image-wrap-border-land.png'
image1.onload = function() {
context.drawImage(image1, 0, 0, canvas.width, canvas.height);
};
Since the image isn't loaded, the onload function doesn't do anything and it skips over it. I have also tried putting the onload function before I set the source but the image still isn't drawn.
I have tried:
//code from above
if(image1.completed) {
context.drawImage(image1, 0, 0, canvas.width, canvas.height);
}
But have faced the same problem.
I have also looked at the possibility that the image is producing an error as it is loaded. To catch this I wrote:
//code from above
image1.onerror = function() {
console.log('error');
}
But the image source is fine, and the image isn't producing an error, it is just taking a long time to load.
Is there a way to wait for an image to load before it is drawn to the canvas?
Edit to add specificity:
My HTML looks like this:
<div id="profile-container" class="container-fluid">
<h1>My Notes</h1>
<div id="empty-orders" class="column-wrapper disable" style="padding-top: 5px; margin: 0 38%;">
<h3>Create a Note and it will appear here</h3>
<button class="btn btn-lg btn-outline">{{=A('GET STARTED', _href=URL('default', 'canvas_board_print'))}}</button>
</div>
<div id="loading" class="column-wrapper">
<i class="fa fa-cog fa-spin fa-3x fa-fw"></i>
</div>
<div id="row1" class="row">
</div>
</div>
My javascript looks like this:
function showOrders(note_orders) {
var orders = note_orders;
//console.log(orders);
var row_num = 1;
var node_num = 1;
if(orders.length > 0) {
for (var i = 0; i != orders.length; i++) {
orders[i].border = JSON.parse(orders[i].border);
orders[i].image_retouching = JSON.parse(orders[i].image_retouching);
orders[i].shipping_info = JSON.parse(orders[i].shipping_info);
var new_node = $("<div>", {id: "node" + node_num, "class": "col-xs-3 node-wrapper"});
var new_canvas = $('<canvas>', {id: "canvas" + node_num, style: 'display: none'});
var new_image = $('<img>', {id: "note_prev" + node_num, 'class': 'img-responsive'});
$('#row' + row_num).append(new_node);
$('#node'+ node_num).append(new_canvas).append(new_image).append(processOrders(orders[i], node_num));
node_num++;
if (i != 0 && (i + 1) % 4 == 0) {
row_num++;
var new_row = $("<div>", {id: "row" + row_num, "class": "row"});
$(' #profile-container ').append(new_row);
}
}
$(' #loading ').addClass('disable');
} else {
$(' #loading ').addClass('disable');
$(' #empty-orders ').removeClass('disable');
}
}
function processOrders(curr_order, node_num) {
var canvas = document.getElementById('canvas' + node_num);
var context = canvas.getContext('2d');
var image1 = new Image();
image1.src = curr_order.image_url;
canvas.width = image1.naturalWidth;
canvas.height = image1.naturalHeight;
if(image1.complete) {
context.drawImage(image1, 0, 0, canvas.width, canvas.height);
if(curr_order.border.style == 'color_wrap') {
document.getElementById('note_prev' + node_num).style.border = "10px solid " + curr_order.border.color;
} else if(curr_order.border.style == 'image_wrap') {
var image_wrap = new Image();
if(canvas.width > canvas.height) {
image_wrap.src = '{{=URL('static', 'images/image-wrap-border-land.png')}}';
} else {
image_wrap.src = '{{=URL('static', 'images/image-wrap-border-port.png')}}';
}
console.log(image_wrap);
image_wrap.onload = function() {
context.drawImage(image_wrap, 0, 0, canvas.width, canvas.height);
};
image_wrap.onerror = function() {
console.log('errors');
}
}
}
document.getElementById('note_prev' + node_num).src = canvas.toDataURL('image/png');
var node_div = document.createElement('div');
//Other non-image informantion is appended to node_div
return node_div;
}
My problem is when drawing image_wrap, not image1 from my above example. Image1 from my code draws fine, but image_wrap won't appear when it is drawn to the canvas.
I FIXED IT!!!
I moved the creation of image_Wrap variable up with the creation of the image1 variable.
function processOrders(curr_order, node_num) {
var canvas = document.getElementById('canvas' + node_num);
var context = canvas.getContext('2d');
var image1 = new Image();
var image_wrap = new Image();
image1.src = curr_order.image_url;
canvas.width = image1.naturalWidth;
canvas.height = image1.naturalHeight;
if(image1.complete) {
context.drawImage(image
I thought this wouldn't be a good idea because I didn't want to create extra variables even if they weren't going to get used, but I guess it fixed it.
I was able to load the image using your code.
Can you confirm you are not getting an error while loading the image?
(Seeking clarification: posted as an answer to demo the running code snippet)
Update: added a hackish way to wait a second after loading image and before drawing it to canvas
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var image1 = new Image();
image1.src = 'http://lorempixel.com/400/200/sports/1/';
image1.onload = function() {
// you could draw the image right away
// ctx.drawImage(image1, 0, 0, canvas.width, canvas.height);
// OR
// You could wait for an extra second before drawing the image (hackish, I know)
setTimeout(function() {
ctx.drawImage(image1, 0, 0, canvas.width, canvas.height);
}, 1 * 1000); // 1 second (in milliseconds)
};
<h2>canvas</h2>
<canvas id='canvas'>
</canvas>
<hr/>
Set the onload function BEFORE you set the src. It's likely that the image is loading before the onload function is being set.
canvas = document.getElementById('canvas');
context = canvas.getContext('2d');
image1 = new Image();
image1.onload = function() {
context.drawImage(image1, 0, 0, canvas.width, canvas.height);
};
image1.src = '{{=URL('static', 'images/image-wrap-border-land.png')}}'; //resolves to: '/SNotes/static/images/image-wrap-border-land.png'
The first parameter for context.drawImage is the image you want to draw, the second two are the position on the context....
context.drawImage(img,sx,sy,swidth,sheight,x,y,width,height);
The remaining parameters allow you to scale the image.
Suggest you try..
image1.onload = function() { context.drawImage(image1,0,0); }

How to make a pdf with page breaks at a specific html tag?

I am trying to convert html into a pdf client side using jspdf.
The code I have currently is this: (most of this is for pagebreaks
$(document).ready(function() {
$("#runpdf").click(function(event) {
var partsec = $("main_body page1");
html2canvas(document.body, {
logging: true,
profile: true,
allowTaint: true,
letterRendering: true,
onrendered: function(canvas) {
var imageData = canvas.toDataURL("image/jpeg");
var image = new Image();
image = Canvas2Image.convertToJPEG(canvas);
var doc = new jsPDF();
doc.page=1;
//FIRST PAGE****************************************
doc.addImage(imageData, 'JPEG', -115, 5, 440 , 875);
var croppingYPosition = 1250;
count = (image.height) / 3300;
for (var i =1; !(i >= count); i++) {
doc.addPage();
var sourceX = 0;
var sourceY = croppingYPosition;
var sourceWidth = image.width;
var sourceHeight = 1100;
var destWidth = 1600;
var destHeight = 1090;
var destX = -450
var destY = 0;
var canvas1 = document.createElement('canvas');
canvas1.setAttribute('height', 1200);
canvas1.setAttribute('width', destWidth);
var ctx = canvas1.getContext("2d");
ctx.drawImage(image,
sourceX,
sourceY,
sourceWidth,
sourceHeight,
destX,
destY,
destWidth,
destHeight);
var image2 = new Image();
image2 = Canvas2Image.convertToJPEG(canvas1);
image2Data = image2.src;
doc.addImage(image2Data, 'JPEG', 12, 10);
croppingYPosition += 1230;
}
doc.save('test.pdf');
}
});
});
});
However, I would like to, rather than using arbitrary, hard-coded pixel values for setting pagebreaks, have it search for a specific html tag, id, class, or some other identifier to search for at which to make a page break. For example, The code looks down the html until it finds something of class .pagebreak at which point it takes everything above it and stretches it to fit a pdf page (I can do the stretchy part, I just need help with the actual page break) and then looks right after the page break and starts over. Hope this made sense. Thanks!

Categories

Resources