When using the .toImage() method in KineticJS, I always seem to get a much larger image than is really necessary, with the piece I need taking up only the top left corner of the data image. My stage is scaled based on window size and a pre-defined initial size (on window resize, resize stage function called which sets the scale and the size of the container). Should I be setting some sort of crop when I use toImage() to compensate for this? Looking at the image, it seems that the overall image is about twice the size it needs to be, and the piece I need is about half the size I need, when the scale is at around 0.5 (the stage is about half size because I use Chrome and leave the developer bar open at the bottom for debugging).
Here's what I'm using now:
desc.toImage({
width: sideW / cvsObj.scale,
height: sideH / cvsObj.scale,
callback: function(img) {
desc.hide();
sideImg.transitionTo({x : sideW / 2, width : 0, duration : 0.25, callback : function() {
// add image to emulate content
var params = {name : 'descimg', width : sideW, height : sideH, image : img, x : sideW / 2, y : 0};
var image = new Kinetic.Image(params);
side.add(image);
image.setWidth(1);
sideImg.hide();
image.transitionTo({x : 0, width : sideW, duration : 0.25, callback : function() {
side.add(desc);
desc.show();
image.hide();
cvsObj.page.draw();
}});
}});
}
});
There have been improvements to KineticJs over time and functions work in a 'better' way nowadays. Try the new KineticJS 4.3:
http://d3lp1msu2r81bx.cloudfront.net/kjs/js/lib/kinetic-v4.3.0.min.js
I posted this question two and a half months ago, and received no replies until today. I've kept up-to-date with KineticJS - even adding my own bug reports and code suggestions. However, recently I revisited this particular section of code and came up with something a bit better - the image I'm getting back is now properly cropped and can be inserted as is into my canvas layers. Here's the code:
// grp = kinetic group, iw = image width, ih = image height, rimg = returned image array for a deferred function, cvsobj.scale is a global stage scale variable (i scale the stage to fit the window)
getImage : function(grp, iw, ih, rimg) {
var dfr = $.Deferred();
var gp = grp.getAbsolutePosition();
grp.toImage({
width: iw * cvsObj.scale,
height: ih * cvsObj.scale,
x : gp.x,
y : gp.y,
callback: function(img) {
rimg.push(img);
dfr.resolve(rimg);
}
});
return dfr.promise();
}
This is an extended prototype function - part of a sub-section functionality for converting an entire layer or section of a layer into an image specifically for manipulating in an animation sequence. Hopefully this helps someone else.
Related
I'm trying to set up a simple "image to PDF" function with jsPDF but I found myself stopped by a strange behavior : the position and size of the picture aren't the one given in parameters.
function exportToPdf(src_){
var pdfSize = [100, 100];
var pdfDoc = new jsPDF({
"unit" : "px",
"format" : pdfSize
});
var tempImg = new Image();
tempImg.onload = function(event_){
pdfDoc.addImage(
tempImg,
"PNG",
0,
0,
pdfSize[0],
pdfSize[1]
);
pdfDoc.save("test.pdf");
}
tempImg.src = src_;
}
Simple enough : I define the size of the PDF in pixels, load the Image, add it to the PDF at the position [0, 0] and with the size of the PDF and DL it.
What I expected versus the result I have on CodePen
Seeing this, I tried to tweak the position and, instead of [0, 0], I gave
pdfDoc.addImage(
tempImg,
"PNG",
pdfSize[0]/2,
pdfSize[1]/2,
pdfSize[0],
pdfSize[1]
);
I expected to have the image start at the middle of the PDF and overflow on the right and bottom sides, with only the top-left quarter visible.
The result
I have this problem on a basic HTML page, on CodePen BUT the exact same code works perfectly on the test page of parall.ax.
I feel like there is a scaling issue but I don't understand why. Does anyone have an idea ?
Well, after more tests, I think I found the answer to my own question :
The positions and sizes must be given in "pt".
English isn't my native language so I might be wrong but it looks like the documentation isn't up to date.
addImage indicate that x, y, width and height must be "in units declared at inception of PDF document" but there is a clear scaling problem if the unit isn't "pt"
fillRect doesn't indicate a unit for its x/y but its width/height are asked in pixel. Again, using pixels result in a scaling problem while using "pt" doesn't.
I'm working on a Paper.js application that puts a raster (an image) in the view. Then it zooms to fit the image so that all of it is visible at one time. It's mostly working, but the image ends up offset, like this:
When it should look more like this:
Here's the code that makes the view, adds the image, and makes the call to zoom to fit:
// Set up HTMLImage
var image = new Image(this.props.image.width, this.props.image.height);
image.src = 'data:image/png;base64,' + this.props.image.imageData;
//var canvas = React.findDOMNode(this.refs.canvas); // React 0.13 +
var canvas = this.refs.canvas.getDOMNode();
// Scale width based on scaled height; canvas height has been set to the height of the document (this.props.height)
var scalingFactor = canvas.height/image.height;
canvas.width = image.width * scalingFactor;
// Initialize Paper.js on the canvas
paper.setup(canvas);
// Add image to Paper.js canvas
var raster = new paper.Raster(image, new paper.Point(0,0));
// Fit image to page so whole thing is displayed
var delta = scalingFactor < 1 ? -1 : 1; // Arbitrary delta (for determining zoom in/out) based on scaling factor
var returnedValues = panAndZoom.changeZoom(paper.view.zoom, delta, paper.view.center, paper.view.center, scalingFactor);
paper.view.zoom = returnedValues[0];
And here is the panAndZoom.changeZoom method:
SimplePanAndZoom.prototype.changeZoom = function(oldZoom, delta, centerPoint, offsetPoint, zoomFactor) {
var newZoom = oldZoom;
if (delta < 0) {
newZoom = oldZoom * zoomFactor;
}
if (delta > 0) {
newZoom = oldZoom / zoomFactor;
}
var a = null;
if(!centerPoint.equals(offsetPoint)) {
var scalingFactor = oldZoom / newZoom;
var difference = offsetPoint.subtract(centerPoint);
a = offsetPoint.subtract(difference.multiply(scalingFactor)).subtract(centerPoint);
}
return [newZoom, a];
};
Any idea why it zooms to fit but loses the centering?
TL;DR
Use either, but not both:
After the zoom:
paper.view.setCenter(0,0);
When pasting the image:
var raster = new paper.Raster(image, new paper.Point(canvas.width/2, canvas.height/2));
The long answer
As a disclaimer, I must point out I have no knowledge of paper.js and their documentation looks seemingly terrible. This answer is born from fiddling.
I more or less replicated your code and after some tinkering, I managed to fix the issue by using this:
paper.view.zoom = returnedValues[0];
paper.view.setCenter(0,0);
If you wonder why I'm not pointing at the documentation for setCenter, it's because I couldn't find any. I discovered that method by inspecting paper.view.center.
Although I wasn't satisfied as I could not understand why this would work. As such, I kept looking at your code and noticed this:
// Add image to Paper.js canvas
var raster = new paper.Raster(image, new paper.Point(0,0));
The documentation for raster tells us the following:
Raster
Creates a new raster item from the passed argument, and places it in the active layer. object can either be a DOM Image, a Canvas, or a string describing the URL to load the image from, or the ID of a DOM element to get the image from (either a DOM Image or a Canvas).
Parameters:
source: HTMLImageElement / HTMLCanvasElement / String — the source of the raster — optional
position: Point — the center position at which the raster item is placed — optional
My understanding is that the image is pasted on the canvas with its center at position 0,0, also known as the top left corner. Then the content of the canvas is resized, but based on its own center position. This pulls the image closer to the center of the canvas, but there is still a discrepancy.
Setting the center to 0,0 simply synchronizes the center points of both the image and the canvas.
There is also an alternative way, which is to paste the image at the current center of the canvas, which seems more proper:
// Add image to Paper.js canvas
var raster = new paper.Raster(image, new paper.Point(canvas.width/2, canvas.height/2));
Here is the rudimentary JSFiddle to experiment.
I want to make responsive canvas with its context.
I have already made the canvas responsive; below you can see my resize function.
$(window).on('resize', function(event) {
event.preventDefault();
d.myScreenSize = {
x: $('.mainCanvasWrapper').width(),
y: $.ratio($('.mainCanvasWrapper').width())
};
console.log('d.myScreenSize ', d.myScreenSize);
var url = canvas.toDataURL();
var image = new Image();
ctx.canvas.width = d.myScreenSize.x;
ctx.canvas.height = d.myScreenSize.y;
image.onload = function() {
ctx.drawImage(image, 0, 0, $('#paper').width(), $('#paper').height());
//the drawing is being blurry (blurred). Please, have a look at the screenshot i have posted.
}
image.src = url;
if (d.drawer == 'true') {
socket.emit('windowResize', d.myScreenSize);
};
});
I have tried many solutions and also I don't want to use any library
can anyone suggest me better solution ?
You are scaling bitmap data which means you will have loss in quality when resizing it. If you went from a small size to a large size then the image will be blurry due to the interpolation that takes place.
What you would want to do is to store your drawings as vectors:
Keep the drawn points in arrays "internally"
When needed, redraw all the points to the canvas
When resized, scale the points accordingly, then redraw as above.
My suggestion when it comes to rescaling the points is to keep and use the original points as a basis every time as this will give you a more accurate scaling.
There are plenty of examples here on SO on how to store drawn points as well as redraw them. One that could be used for basis is for example: HTML canvas art, generate coordinates data from sketch.
Fiddle!!
I have set this fiddle up to show what parameters are going into the setViewBox() function. The thing is, the way it is working makes no sense to me.
Why does setViewBox(0, 0, 625, 625) result in a larger box / further zoomed in than setViewBox(0, 0, 1250, 1250)?
Also, why does setViewBox(-100, 0, 625, 625); adjust the image to the right (I would expect it to go left because of the negative x value)?
This is what the docs say about setViewBox(x, y, w, h):
parameters:
x - new x position, default is 0
y - new y position, default is 0
w - new width of the canvas
h - new height of the canvas
Also, I am trying to figure out the relationship between the first(x) / third(w) and the second(y) / fourth(h) parameters such that the box zooms in and out from the middle, instead of expanding from the top left corner, if anyone has any suggestions.
The answer is that the parameters passed to setViewBox are basically telling the size of the window you're looking through at the object. If the window is smaller, you see less of the object, or the object zoomed in. If you move the window left, you'll see the object move right, relative to the window. The key is to think of it as a window you're looking through, not the size of the object itself.
this is an example showing how to calculate the setViewBox values, you have to include jquery (to get SVG cocntainer X and Y ) :
var original_width = 777;
var original_height = 667;
var zoom_width = map_width*100/original_width/100;
var zoom_height = map_height*100/original_height/100;
if(zoom_width<=zoom_height)
zoom = zoom_width;
else
zoom = zoom_height;
rsr.setViewBox($("#"+map_name).offset().left, $("#"+map_name).offset().top, (map_width/zoom), (map_height/zoom));
I am working on an image cropping tool at the moment, the image crop tool comes from jCrop, what I am trying to is make the image the user takes the crop from smaller than the original uploaded. Basically if uploads a landscape image I need to make the image 304px wide without alterting the aspect ratio of the shot.
For instance if the user uploads a portrait shot, I need make the image 237px without altering the aspect ratio of the shot.
Is this possible? I have access to original images sizes in my code, but I cannot work out make sure I am not altering the aspect ratio?
Yes, it's possible if the source image is already wide enough. If it's not wide enough there isn't enough data to crop and if you wanted to maintain aspect ratio, you'd need to scale the image up before the crop.
Something like this should get you started:
CropWidth=237;
AspectRatio= SourceWidth/SourceHeight;
CropHeight = CropWidth*AspectRatio;
To properly maintain aspect ratio you should use GCD:
function gcd(a, b) {
return (b == 0) ? a : gcd(b, a % b);
}
You should then do something like this:
function calcRatio(objectWidth, objectHeight) {
var ratio = gcd(objectWidth, objectHeight),
xRatio = objectWidth / ratio,
yRatio = objectHeight / ratio;
return { "x": xRatio, "y": yRatio };
}
This will get you the ratio of some object. You can use this information to figure out how large the resulting image should be.
function findSize (targetHeight, xRatio, yRatio) {
var widthCalc = Math.round((xRatio * targetHeight) / yRatio);
return { "width" : widthCalc, "height" : targetHeight };
}
var test = calcRatio(1280,720),
dimensions = findSize(400, test.x, test.y);
//400 is what we want the new height of the image to be
Those are your dimensions. If you don't have any extra "space" around your images that you need to account for then your work is done. Otherwise you need to handle a couple cases.