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)
Related
I have a drawn image in canvas with context.drawImage() and I want to select that image to move it with drag and drop in the same canvas (same as MS Paint selection tool). How can I code that in javascript?
function crop(xStart,yStart,xLast,yLast){
canvas.width = xLast - xStart;
canvas.height = yLast - yStart;
context.clearRect(0, 0, canvas.width,canvas.height); drawFromAux(xStart,yStart,xLast,yLast,0,0);
return canvas.toDataURL();
}
// img is my original image
function select(xStart,yStart,xLast,yLast){
selection.src = crop(xStart,yStart,xLast,yLast);
selection.draggable = "true";
context.clearRect(0, 0, canvas.width,canvas.height);
canvas.width = img.width;
canvas.height = img.height;
context.clearRect(0, 0, canvas.width,canvas.height);
context.drawImage(img, 0, 0);
context.clearRect(xStart, yStart, xLast - xStart,yLast - yStart);
context.drawImage(selection,0,0);
}
Using Canvas.js and Pointer.js that should not be that hard.
Things to do:
create image object containing with x, y and raw image
render it to a canvas
listen for mouse moves and check if mouse button is pressed
simple point collision detection between mouse and image on canvas to be able to "select it" and drag it
Loading Pointer.js and Canvas.js:
<script type="text/javascript" src="https://gustavgenberg.github.io/handy-front-end/Canvas.js"></script>
<script type="text/javascript" src="https://gustavgenberg.github.io/handy-front-end/Pointer.js"></script>
1) Creating an image object is not very hard:
const image = {
image: new Image(),
x: canvas.width / 2 - image.width / 2, // centered in canvas
y: canvas.height / 2 - image.height / 2 // centered in canvas
};
image.image.src = ' <url> ';
2) Render that image to the canvas (using Canvas.js)
const canvas = new Canvas('my-canvas', 500, 500).start();
canvas.on('draw', function ( renderer ) {
renderer.drawImage(image.image, image.x, image.y);
});
3) Listening for mouse moves and moving the image (using Pointer.js)
const pointer = new Pointer( canvas.element );
let moveImage = false;
pointer.on('move', function ( event ) {
if( moveImage ) {
image.x += (event.x - pointer.getMoveHistory(-2).x);
image.y += (event.y - pointer.getMoveHistory(-2).y);
}
});
4) Pointer collision detection (using Pointer.js)
pointer.on('down', function () {
moveImage = pointer.touches({ x: image.x, y: image.y, width: image.image.width, height: image.image.height });
});
pointer.on('up', function () {
moveImage = false;
});
JSFiddle: https://jsfiddle.net/GustavGenberg/3h39b9h1/
Hope this helps you :) !
I'm trying to get my script to render progressively higher-resolution images and substitute them into my canvas as they become available. Right now, nothing gets rendered to the canvas until the script has reached the highest resolution it plans to render at.
Here's where I'm currently at (jsfiddle). The basic structure is:
$(function() {
window.onresize = function() {
resizeCanvas();
};
resizeCanvas();
});
function resizeCanvas() {
// set canvas css to fill body
canvas.width = 18;
canvas.height = canvas.width * aspectRatio;
ctx = canvas.getContext("2d");
drawMandlebrot(...);
while (2 * canvas.width < $(window).width()) {
canvas.width *= 2;
canvas.height *= 2;
drawMandlebrot(...);
}
}
function drawMandlebrot(...) {
var preCanvas = document.createElement('canvas');
preCanvas.width = canvas.width;
preCanvas.height = canvas.height;
var prectx = preCanvas.getContext('2d');
// render stuff to prectx
ctx.putImageData(prectx.getImageData(0,0,preCanvas.width,preCanvas.height), 0, 0);
}
Why do the smaller resolution versions not get rendered to the canvas?
Unrelated to my main problem, how would I go about making sure that the rendering is interruptible by something like window.onresize? And how might I go about optimizing my rendering routine to perform faster?
page 1:
//Calls the function from page 2 and the callback image is set as the source of the control.
previewImage(current, function(img) {
jQuery(".mapItem").attr("src",img.src);
});
page 2:
//The functions callback returns an image which we use in page 1 (above)
var canvas = document.createElement('canvas');
context = canvas.getContext('2d');
context.drawImage(this.m_Images[i],0,0,canvas.width,canvas.height);
var t = new Image();
t.src = canvas.toDataURL();
callback(t);
The issue:
I have 2 JavaScript pages, the first one has an image control and the second one has a function that returns a callback as an image.
My control in page 1 (.mapItem) has a height and width of 75.2px (fixed). The image that is coming from the callback however will have a different size each time e.g one day it can be 200px * 300px and one day it can be 150px * 200px etc
How can I clip or CUT the image of the callback? I want the image (t) to zero (0) as x and y starting points and then clip the image where ever the .mapItem control height and width is.
I need this to be proportional ratio. So I can't just add the following code:
context.drawImage(this.m_Images[i],0,0,72.5,72.5); because this will ruin the image as we dont even know if it is square shaped.
Thanks in advance :)
You can determine the proportions of callback image and then apply them to the page 1 image thus:
Let's assume that the callback image is 300x200px. The ratio of the image's height-to-width can be expressed as...
var ratio = callbackImage.height / callbackImage.width;
...or, in this case...
var ratio = 200 / 300; // 0.666...
We know the width of the page 1 canvas is 72.5 so we can apply the ratio to that value to determine the proportional height of the callback Image like so...
var canvasWidthHeight = 72.5;
var imageHeight = canvasWidthHeight * ratio; // 48.333...
To center the callback Image on the page 1 canvas calculate the y offset like so..
var y = (canvasWidthHeight - imageHeight) / 2;
...and now you can use the canvas drawImage method with these values...
context.drawImage(
this.m_Images[i],
0, y,
canvasWidthHeight, imageHeight
);
If the callback image was higher than it was wide you'd apply the ratio the page 1 canvas dimensions to work out the x offset rather than the y offest. If the callback image was square then its ratio would be 1.0 and you could simply paint it into the square page 1 canvas at
context.drawImage(
this.m_Images[i],
0, 0,
canvasWidthHeight, canvasWidthHeight
);
All together the code might look something like this...
var image = this.m_Images[i];
var canvas = {
width: 72.5,
height: 72.5
};
var wh = 0;
var ratio = image.height / image.width;
if (image.width > image.height) { // landscape
wh = canvas.width * ratio;
context.drawImage(
image,
0, (canvas.height - wh) / 2,
canvas.width, wh
);
} else if (image.width < image.height) { // portrait
ratio = image.width / image.height;
wh = canvas.height * ratio;
context.drawImage(
image,
(canvas.width - wh) / 2, 0,
wh, canvas.height
);
} else { // square
context.drawImage(
image,
0, 0,
canvas.width, canvas.height
);
}
Hope that helps. ;)
EDIT: You may need to ensure that the new Image() has fully loaded before initiating the callback. You can do this with the following snippet...
// callback preparation code as before...
var t = new Image();
t.addEventListener('load', function() {
callback(this);
});
t.src = canvas.toDataURL();
I was wondering how I can make my canvas resize along with my browser window? As it is right now, using innerWidth and innerHeight, it will only size to the initial browser width and height. How do I make it expand with the browser?
Edit: Is there a way to do this without clearing the canvas?
JS
canvas.onmousemove = draw;
var ctx = canvas.getContext('2d');
var video = document.createElement("video");
video.setAttribute("src", "some_video.mp4");
video.autoplay = true;
var img2 = new Image;
img2.src = "some_image.png";
ctx.canvas.width = window.innerWidth;
ctx.canvas.height = window.innerHeight;
function draw(e){
var rect = canvas.getBoundingClientRect();
var x = e.clientX-rect.left-img2.width/2;
var y = e.clientY-rect.top-img2.height/2;
ctx.drawImage(video, x, y, 830, 644);
ctx.drawImage(img2, x, y, img2.width, img2.height);
}
Use it on resize
$(window).resize(function(){
// Place your code here which sets the width and height of canvas.
});
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.