HTML Canvas drawing an image is not working - javascript

I have this angularJS application and I am generating an image from the canvas. I have everything else working, I can even draw images from SVGs that are in the html, but trying to pull an image from a relative URL is failing to display.
I know that the image is being loaded and that it has actually found the right image because of some console logging.
Here is my options object:
// Define our options
self.options = {
team: null,
itemsPerRow: 3,
targets: {
containerId: 'totals',
svgContainerClassName: 'total-item',
svgClassName: 'svg-document',
svgTitleClassName: 'total-title'
},
itemPadding: {
top: 50,
right: 50,
bottom: 100,
left: 50
},
canvasPadding: {
top: 300,
right: 100,
bottom: 200,
left: 100
},
sizes: {
first: 1100,
all: 300
},
footer: {
logo: '/assets/images/k-logo-orange-black.png'
}
};
You can see the footer logo.
The function for displaying that is this:
// Private function to draw the footer
var drawFooter = function (canvas, ctx) {
var y = canvas.height - 50;
// Draw our image
var img = new Image();
img.onload = function () {
console.log(img.width);
console.log(img.height);
console.log(canvas.width - self.options.canvasPadding.right - img.width);
console.log(y - img.height);
ctx.drawImage(img, canvas.width - self.options.canvasPadding.right - img.width, y - img.height);
};
img.src = self.options.footer.logo;
};
So, the console logs are outputting the dimensions of the image and the canvas locations are well within the bounds of my canvas, but the image just isn't showing.
Does anyone know why?

I think you have two things going on here. first, if you are placing this image inside an SVG element, in Javascript SVG elements are created using a different namespace, using commands such as (with a few exceptions):
var svg = document.createElementNS('http://www.w3.org/2000/svg','svg');
svg.setAttributeNS('http://www.w3.org/2000/svg','xlink','http://www.w3.org/1999/xlink');
see w3 docs here
Second, I do not see where you have given the x attribute its variable, which is necessary, found on MDN here, (which also reference w3 docs here)

This question could never have been answered. I was using promises to draw other images and then doing a $q.all() once they had been resolved. I didn't return anything with the drawFooter so the image has not drawn, so I just changed the function to this:
// Private function to draw the footer
var drawFooter = function (canvas, ctx) {
// Create a deferred promise
var deferred = $q.defer();
// Get our y location
var y = canvas.height - 50;
// Draw the copyright
ctx.font = '20px lato-regular';
ctx.fillStyle = 'black';
ctx.textAlign = 'left';
ctx.fillText(self.options.footer.copyright, self.options.canvasPadding.left, y);
// Draw main text
ctx.font = '60px lato-regular';
ctx.fillStyle = 'black';
ctx.textAlign = 'center';
ctx.fillText(self.options.footer.mainText, canvas.width / 2, y);
// Create a new image
var img = new Image();
// When the image loads
img.onload = function () {
// Get our coordinates
var imageWidth = img.width / 2,
imageHeight = img.height / 2,
imageX = canvas.width - self.options.canvasPadding.right - imageWidth,
imageY = y - imageHeight;
// Draw the image
ctx.drawImage(img, imageX, imageY, imageWidth, imageHeight);
// Resolve our promise
deferred.resolve();
};
// Set the SRC to the logo
img.src = self.options.footer.logo;
// Return our promise
return deferred.promise;
};
and then I made sure it was being added to my pormises array:
// Finally add our footer to our promises array
promises.push(drawFooter(canvas, ctx));
When I did that, it all worked fine.

Related

How to draw a photo then a shape on top of the photo using a canvas in Ionic?

There are numerous examples out there showing how to draw things onto a canvas, however, my problem is slightly different - I want to load a photo into memory, draw a shape onto exact coordinates over the photo, THEN draw/scale the photo onto a canvas. Not sure where to start with this. Are there any relevant libraries out there I can use with ionic that will allow you to do this?
Edit 1 ~ I now have this mostly working:
private properties:
#ViewChild('mainCanvas') canvasEl: ElementRef;
private _CANVAS: any;
private _CONTEXT: any;
ionViewDidEnter():
this._CANVAS = this.canvasEl.nativeElement;
this._CONTEXT = this._CANVAS.getContext('2d');
updateCanvas():
var img = new Image();
const ctx = this._CONTEXT;
const canvas = this._CANVAS;
ctx.clearRect(0, 0, this._CANVAS.width, this._CANVAS.height);
ctx.fillStyle = "#ff0000";
img.onload = (() => {
img.width = img.width;
img.height = img.height;
canvas.width = img.width;
canvas.height = img.height;
ctx.drawImage(img, 0, 0);
ctx.lineWidth = 8;
ctx.strokeStyle = "#FF0000";
ctx.strokeRect(100, 100, 400, 400);
ctx.scale(0.5, 0.5); // this does nothing
});
img.src = (<any>window).Ionic.WebView.convertFileSrc(path);
This draws the photo then the rectangle onto the canvas, however, the resulting image is too large to fit onto the screen, so I need to scale the canvas after all drawing is complete. I tried this with ctx.scale but the canvas remains the same size regardless of which values I specify.
You cannot draw straight onto a photo, but what you can do is create an offscreen canvas that is the same size as the photo, draw the photo to it, and then draw your shapes on top.
The result can then be drawn to your main canvas e.g.
// Empty image for example purposes
const img = new Image(100, 100);
// Creating a canvas for example purposes
const mainCanvas = document.createElement('canvas');
const mainCtx = mainCanvas.getContext('2d');
// Create an offscreen buffer
const bufferCanvas = document.createElement('canvas');
const bufferCtx = bufferCanvas.getContext('2d');
// Scale the buffer canvas to match our image
bufferCanvas.width = img.width;
bufferCanvas.height = img.height;
if (bufferCtx && mainCtx) {
// Draw image to canvas
bufferCtx.drawImage(img, 0, 0);
// Draw a rectangle in the center
bufferCtx.fillRect(img.width / 2 - 5, img.height / 2 - 5, 10, 10);
// Draw the buffer to the main canvas
mainCtx.drawImage(bufferCanvas, 0, 0);
}

Can I center canvas image

I'm drawing a large canvas image as a background, the image is larger that the window size. I'm wondering if theres a way for me to center the image to fit on full screen. If so, how? this is what I'm doing:
var canvas = document.getElementById('canvas'),
context = canvas.getContext('2d');
window.addEventListener('resize', resizeCanvas, false);
function resizeCanvas() {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
drawStuff();
}
resizeCanvas();
function drawStuff() {
var imageObj = new Image();
imageObj.onload = function() {
context.drawImage(imageObj, 69, 50);
};
imageObj.src = '/resources/img/bg.png';
}
Here is an optional way of centering an image to canvas not using transform (also see note below):
imageObj.onload = function() {
var x = (canvas.width - this.width ) * 0.5, // this = image loaded
y = (canvas.height - this.height) * 0.5;
ctx.drawImage(this, x, y);
};
Since the image is larger than the canvas x and y will be negative in this case, which is perfectly fine. If the image was smaller it would work just as fine too. If you do the drawing outside the load handler you would of course need to use imageObj instead of this.
NOTE: The way you have set up your resize handler is not the best way to handle image repositioning - you should only load the image once, then reuse that object. As resizing typically creates a number of events it would trigger an equal number of image reloads.
For this to work properly you could do something like this instead:
var canvas = document.getElementById('canvas'),
context = canvas.getContext('2d'),
imageObj = new Image(); // declare globally
imageObj.onload = function() {
// now set up handler when image is actually loaded
// - else drawImage will fail (width, height is not available and no data)
window.addEventListener('resize', resizeCanvas, false);
// initial call to draw image first time
resizeCanvas();
};
imageObj.src = '/resources/img/bg.png';
function resizeCanvas() {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
drawStuff();
}
function drawStuff() {
var x = (canvas.width - imageObj.width ) * 0.5,
y = (canvas.height - imageObj.height) * 0.5;
ctx.drawImage(imageObj, x, y);
}
It's not perfect as the resize event queue will still be large and may lag - there are solutions for this too, for example this one (with minor modifications).
Here's how to center the image on the canvas. Any vertical or horizontal overflow will be off-canvas:
imageObj.onload = function() {
ctx.translate(canvas.width/2,canvas.height/2);
ctx.drawImage(imageObj,-imageObj.width/2,-imageObj.height/2);
ctx.translate(-canvas.width/2,-canvas.height/2);
};
Good luck with your project!
If you are adding image after uploading then you can use this, it works for me :)
var image_center_width = (canvas.width - img.width) / 2;
var image_center_height = (canvas.height - img.height) / 2;
img.set({
left : image_center_width,
top : image_center_height,
});
canvas.add(img)

How do I get the image data on specific objects?

In a canvas I created a 2d context. In that context... with a function... I'm able to create some 'circle objects'. Now, what I want, is to get the ImageData of a single circle object instead of the image data of the whole context.
In the code below, you can see my wish commented out.
var c = document.getElementById('canvas');
var ctx = c.getContext('2d');
var circle = function (X,Y) {
var that = this;
that.X = X;
that.Y = Y;
that.clicked = function(e) {
//
//
//!!!!!!!!!!!!!!
// Code below works fine, on context level
imgData = ctx.getImageData(e.pageX, e.pageY, 1, 1);
//
// Code below is at the level of the circle, that's what I want, but isn't working
imgData = that.getImageData(e.pageX, e.pageY, 1, 1);
//!!!!!!!!!!!!!!
//
//
alert(imgData.data[3]);
}
that.draw = function () {
ctx.save();
ctx.translate(that.X, that.Y);
ctx.fillStyle = '#33cc33';
ctx.beginPath();
ctx.arc(0, 0, 50, 0, 2 * Math.PI);
ctx.fill();
ctx.stroke();
ctx.restore();
}
}
var circles = new Array();
circles.push(new circle(50,50));
document.addEventListener('click',function() {
circles.forEach(function(circ,index){
circ.clicked();
});
})
So, how do I get the image data on specific objects?
edit:
I understand that I need to draw the circle first, I do that later in my code, but what if I've got a background rect in the context, when I click next to the circle, it will get the imageData of the background rect, when I want to return the 0 value of the alpha rgba.
To this you need to log all your drawings as a "shadow canvas". The most common way is to create shape objects and store them in for example an array:
Draw the shape on canvas
Log its type, position, dimension, colors and orientation and store as an object and push that object to the array
When you need to get an isolated shape or object as an image:
Get mouse position (if you want to click on the object to select it)
Iterate the array of objects to see which object is "hit"
Create a temporary canvas of the dimension of that shape
Draw in the shape into the temporary canvas
Extract the data as an image (ctx.getImageData(x, y, w, h) or canvas.toDataURL())
When you need to resize your canvas you simply iterate all the objects and redraw them. You can even serialize your data for storage using this method.
An example of an object can be:
function Rectangle(x, y, w, h, fill, stroke) {
this.x = x;
this.y = y;
this.width = w;
this.height = h;
this.fill = fill;
this.stroke = stroke;
}
You can extend this object to render it self to canvas as well as giving you a bitmap of itself isolated from the other shapes. Add this to the above code:
Rectangle.prototype.render = function(ctx) {
if (this.fill) { /// fill only if fill is defined
ctx.fillStyle = this.fill;
ctx.fillRect(this.x, this.y, this.width, this.height);
}
if (this.stroke) { /// stroke only if stroke is defined
ctx.strokeStyle = this.stroke;
ctx.strokeRect(this.x, this.y, this.width, this.height);
}
}
Rectangle.prototype.toBitmap = function() {
var tcanvas = document.createElement('canvas'), /// create temp canvas
tctx = tcanvas.getContext('2d'); /// temp context
tcanvas.width = this.width; /// set width = shape width
tcanvas.height = this.height;
tctx.translate(-this.x, -this.y); /// make sure shape is drawn at origin
this.render(tcxt); /// render itself to temp context
return tcanvas.toDataURL(); /// return image (or use getImageData)
}
You simply draw your shapes, create the object based on the positions etc:
var rect = new Rectangle(x, y, w, h, fillColor, strokeColor);
myShapeArray.push(rect);
When you need to render the shapes:
for(var i = 0, shape; shape = myShapeArray[i++];)
shape.render(ctx);
And when you need to get its bitmap (you retrieved its index in advance with the mouse click):
var image = myShapeArray[index].toBitmap();
And of course: you can make similar objects for circles, lines etc.
Hope this helps!
Remember that Canvas is a bitmap graphics tool. Anything you draw into a single context becomes part and parcel of the same object. You can't get separate image data for each "object" you used to draw on that canvas... it's painted ... flattened ... into those pixel positions for that bitmap as soon as you hit draw().
The only way you could do something like what you are looking for would be to create separate canvas contexts that you overlay on top of each other. This would be better handled by utilizing a library such as KineticJS (http://www.html5canvastutorials.com/kineticjs/html5-canvas-events-tutorials-introduction-with-kineticjs/). The only other option would be to use an object oriented drawing tool such as SVG, (through Raphael.js, for example: http://raphaeljs.com) which does preserve separate objects in the the graphics space.
For reference about getImageData, see http://www.html5canvastutorials.com/advanced/html5-canvas-get-image-data-tutorial/
You can use trigonometry instead of trying to locate your colors with getImageData.
For example, if you have a circle defined like this:
var centerX=150;
var centerY=150;
var radius=20;
var circleColor="red";
Then you can test if any x,y is inside that circle like this:
// returns true if x,y is inside the red circle
isXYinCircle(140,140,centerX,centerY,radius);
function isXYinCircle(x,y,cx,cy,r){
var dx=x-cx;
var dy=y-cy;
return(dx*dx+dy*dy<=r*r);
}
If the x,y is inside that red circle then you know the color at x,y is "red"
If you have multiple overlapping circles you can test each circle in increasing z-index order. The last circle that reports x,y inside will be the color at x,y.
It is because that is not a CanvasGraphicsContext. Try:
that.draw();
imgData = ctx.getImageData(e.pageX, e.pageY, 1, 1);
At first, I create my 2 canvas elements. 1 to display, 1 to calculate the pixeldata.
var c = document.getElementById('canvas');
var c2 = document.getElementById('canvas2');
var ctx = c.getContext('2d');
var ctx2 = c2.getContext('2d');
var width = window.innerWidth,
height = window.innerHeight;
c.width = ctx.width = c2.width = ctx2.width = width;
c.height = ctx.height = c2.height = ctx2.height = height;
Than I make my function to create an image
function Afbeelding(src, X, Y, W, H) {
var that = this;
that.X = X;
that.Y = Y;
that.W = W;
that.H = H;
that.onClick = function () { };
that.image = new Image(that.W, that.H);
that.image.src = src;
that.draw = function (context) {
context = (typeof context != 'undefined') ? context : ctx;
context.save();
context.translate(that.X, that.Y);
context.drawImage(that.image, 0, 0, that.W, that.H);
context.restore();
}
When a document.click event is fired, the next function (inside the Afbeelding function) will be called:
that.clicked = function (e) {
if ((e.pageX > that.X - (that.W / 2) && e.pageX < that.X + (that.W / 2)) && (e.pageY > that.Y - (that.H / 2) && e.pageY < that.Y + (that.H / 2))) {
if (that.isNotTransparent(e)) {
that.onClick();
}
}
}
This function (also inside the Afbeelding function) is used to check the pixel for transparancy.
that.isNotTransparent = function (e) {
var result = false;
ctx2.clearRect(0, 0, width, height);
that.draw(ctx2);
var imgData = ctx2.getImageData(e.pageX, e.pageY, 1, 1);
ctx2.clearRect(0, 0, width, height);
if (imgData.data[3] > 0) {
result = true;
}
return result;
}
}
And all below is to lauch the things up above.
var items = new Array();
var afb = new Afbeelding();
afb.draw();
afb.onClick = function () {
alert('clicked');
}
items.push(afb);
document.addEventListener('mousedown', function (e) {
items.forEach(function (item, index) {
item.clicked(e);
});
});

Loss of quality while adding drawing image on HTML 5 canvas from binary string

I am trying to draw an image from binary string on to the canvas.
var reader = new FileReader();
//reader.readAsDataURL(file);
reader.readAsBinaryString(file);
reader.onload = function(event){
var d = $(thisObj.CreateIndoorFormDivControlName).dialog();
var canvas =document.getElementById('canvasfloorLayout');
var cxt=canvas.getContext("2d");
var img=new Image();
img.onload = function() {
cxt.drawImage(img, 0, 0,canvas.width,canvas.height);
}
img.src = "data:image/jpeg;base64,"+window.btoa(reader.result);
I am using the above code but the problem is the image size is getting reduced to the canvas size and quality is dropping like anything. I have been tried with
cxt.drawImage(img, 0, 0,img.width,img.height);
But the image gets cropped.
I donot want to use reader.readAsDataURL as I need to post the binary to the server. My requirement is to show the full image and draw lines on it .
canvas.width = img.width;
canvas.height = img.height;
ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
...if you draw the image at the canvas' native resolution, then when the image gets scaled, it's obviously going to lose quality.
...if you draw the image at the image's native resolution, but the canvas hasn't changed size, then you're going to end up with a partial image, or a partially-filled canvas.
So if you want neither of those, then set the dimensions of the canvas to match the dimensions of the image, and you'll have a canvas-drawn image which matches the resolution of the data-image.
EDIT
Adding an example of a proxy, between the two.
var img = new Image(),
canvas = document.createElement("canvas"),
context = canvas.getContext("2d"),
// ......etc
dimensions = {
max_height : 800,
max_width : 600,
width : 800, // this will change
height : 600, // this will change
largest_property : function () {
return this.height > this.width ? "height" : "width";
},
read_dimensions : function (img) {
this.width = img.width;
this.height = img.height;
return this;
},
scaling_factor : function (original, computed) {
return computed / original;
},
scale_to_fit : function () {
var x_factor = this.scaling_factor(this.width, this.max_width),
y_factor = this.scaling_factor(this.height, this.max_height),
largest_factor = Math.min(x_factor, y_factor);
this.width *= largest_factor;
this.height *= largest_factor;
}
};
dimensions.read_dimensions(img).scale_to_fit();
canvas.width = dimensions.width;
canvas.height = dimensions.height;
context.drawImage(img, 0, 0, dimensions.width, dimensions.height);

blend two images on a javascript canvas

How do you blend two arrays of pixel data to create one image? with the option of using different blending modes?
Pixastic is a special framework for advanced use of canvas, here are blending examples: http://www.pixastic.com/lib/docs/actions/blend/
If you would like do this alone, you can extract pixel data from 2 images, blend it with a mathematical equation, and put into a canvas. Here is information how to get and put pixel data from/to canvas:
http://ajaxian.com/archives/canvas-image-data-optimization-tip
Update:
Simple example with alpha blending of 2 images in proportion 50-50.
(Images borrowed from http://www.pixastic.com/sample/Butterfly.jpg and http://www.pixastic.com/sample/Flower.jpg )
<img src="Butterfly.jpg" id="img1">
<img src="Flower.jpg" id="img2">
<p>Blended image<br><canvas id="canvas"></canvas></p>
<script>
window.onload = function () {
var img1 = document.getElementById('img1');
var img2 = document.getElementById('img2');
var canvas = document.getElementById("canvas");
var context = canvas.getContext("2d");
var width = img1.width;
var height = img1.height;
canvas.width = width;
canvas.height = height;
var pixels = 4 * width * height;
context.drawImage(img1, 0, 0);
var image1 = context.getImageData(0, 0, width, height);
var imageData1 = image1.data;
context.drawImage(img2, 0, 0);
var image2 = context.getImageData(0, 0, width, height);
var imageData2 = image2.data;
while (pixels--) {
imageData1[pixels] = imageData1[pixels] * 0.5 + imageData2[pixels] * 0.5;
}
image1.data = imageData1;
context.putImageData(image1, 0, 0);
};
</script>
I have created a separate, lightweight, open-source library for perform Photoshop-style blend modes from one HTML Canvas context to another: context-blender. Here's the sample usage:
// Might be an 'offscreen' canvas
var over = someCanvas.getContext('2d');
var under = anotherCanvas.getContext('2d');
over.blendOnto( under, 'screen', {destX:30,destY:15} );
See the README for more information.
I am tasked with recreating this java applet using JavaScript (must be tablet friendly, and work in all modern browsers > IE8).
I am creating images using: var image1 = new Image(); and then setting source: img.src = "some path";
So, from pepkin88 I see that the following function will blend two images by combining their pixel array data, overriding previous data from the first image with the new blended data, and finally putting the new data on the canvas resulting in a blended image:
window.onload = function () {
var img1 = document.getElementById('img1');
var img2 = document.getElementById('img2');
var canvas = document.getElementById("canvas");
var context = canvas.getContext("2d");
var width = img1.width;
var height = img1.height;
canvas.width = width;
canvas.height = height;
var pixels = 4 * width * height;
context.drawImage(img1, 0, 0);
var image1 = context.getImageData(0, 0, width, height);
var imageData1 = image1.data;
context.drawImage(img2, 0, 0);
var image2 = context.getImageData(0, 0, width, height);
var imageData2 = image2.data;
while (pixels--) {
imageData1[pixels] = imageData1[pixels] * 0.5 + imageData2[pixels] * 0.5;
}
image1.data = imageData1;
context.putImageData(image1, 0, 0); };
HOWEVER, if you viewed the java applet that I'm responsible for recreating, you see that blending happens in real-time continuously as you drag the image around with the pointer the images are constantly blending based on their overlapped regions..
SO, I'm looking to modify the code to account for this, and I continually have the x, y, positions of images drawn (based on top left corner), and the w, h of all images stays static:
the following snippets don't include everything I'm doing, just what I sense is important for you to know
//Rectangle Class from Java converted to JS
function Rectangle(x, y, width, height, src) {
this.x = x;
this.y = y;
this.w = width;
this.h = height;
this.img = new Image();
this.img.src = src;
}
//Stores instance in rect array
rect[0] = new Rectangle(1, (height - 111)/2, 150, 105, "images/mMain.png");
//Draw method that's called
Rectangle.prototype.draw = function(ctx) {
//this.checkBound();
ctx.drawImage(this.img, this.x, this.y, this.w, this.h);
prepareMix(this.img, this.x, this.y, this.w, this.h);
}
So, I'm working on a prepareMix function that receives image info and uses it to get and store image data:
function prepareMix(src, x, y, w, h) {
pixels = 4 * w * h;
var image = mtx.getImageData(x, y, w, h);
var imgData = image.data;
}
Made a list of what to do:
Sense the overlapping
Get and Store the overlapping image data
Mix the overlapping region data arrays
Replace the overlapping image data with the blended data
Put the new data on the canvas
1. Sense the Overlapping:
Plan: Store image positions and compare positions data to know whether or not overlapping is occurring.
IF overlapping is TRUE, which two images is it true for? Distinguish these images that're overlapping from other images so that methods can be called on them.
js, css, html, and images in zip here BOX

Categories