I'm using Paper.js for some canvas drawings. I'm trying to resize and position an Raster depending on canvas size but my code doesn't works properly.
var canvas = document.getElementById('canvas');
paper.setup(canvas);
var pitch = new paper.Raster('{{ STATIC_URL }}images/pitch.png');
var width = paper.view.size.width;
var height = paper.view.size.height;
console.log("screen dimensions: " + width + " " + height);
var midPoint = [width/2,height/2];
var paddingLeft = width/40;
var scale = (height/pitch.height)*(90/100);
console.log("scale: " + scale);
pitch.scale(scale);
console.log("pitch dimensions: " + pitch.height + " " + pitch.width);
pitch.position = [midPoint[0] + paddingLeft - (width-pitch.width*scale)/2 , midPoint[1]];
console.log("pitch position: " + pitch.position);
when I load the page for the first time, I get this logs:
screen dimensions: 472.5 340
scale: Infinity
pitch dimensions: 0 0
pitch position: { x: NaN, y: NaN }
but after refreshing the page, everything works fine.
screen dimensions: 472.5 340
scale: 0.6120000000000001
pitch dimensions: 500 759
pitch position: { x: 244.0665, y: 170 }
I think creating a new var for a Raster lags a bit at the first time. But i have no idea how to overcome this problem.
If you create a raster with a url as the input, you can use the onLoad handler to hold a function. In your case:
var canvas = document.getElementById('canvas');
paper.setup(canvas);
var pitch = new paper.Raster('{{ STATIC_URL }}images/pitch.png');
pitch.onLoad = function () {
var width = paper.view.size.width;
etc ...
}
Related
So I have an image in my canvas, and I am trying to make the image fit the canvas (I want image inside the canvas to be of the same size as the canvas). I use scaleToWidth() and scaleToHeight() to set the image size. But only one of them works. If I only use scaleToWidth() then it scales the width correctly, and if I only use scaleToHeight() then it scales the height correctly. In the code below, the height is being scaled correctly as it is written in the line below oImg.scaleToWidth(cDim.clientWidth). But I want both the width and height to be according to my values. How can I do that?
const canvas = new window.fabric.Canvas('c');
const cDim = document.getElementById("canvas-dimension");
console.log("Canvas Height: " + cDim.clientHeight);
console.log("Canvas Width: " + cDim.clientWidth)
let planURL = baseURL + "dream" + "/" + "plan";
window.fabric.Image.fromURL(planURL, (oImg) => {
//let scaleW = cDim.clientWidth / oImg.getWidth;
// let scaleH = cDim.clientHeight / oImg.getHeight;
// oImg.scaleX(scaleW)
oImg.scaleToWidth(cDim.clientWidth)
oImg.scaleToHeight(cDim.clientHeight)
canvas.setBackgroundImage(oImg);
canvas.renderAll();
},{ crossOrigin: 'anonymous'});
setCanvas(canvas)
Any help or workaround to this will be appreciated. Thank you!
SOLVED
So I used this approach instead of scaleToWidth and scaleToHeight, and it worked!
let scaleW = cDim.clientWidth / oImg.width;
let scaleH = cDim.clientHeight / oImg.height;
oImg.set({
opacity: 1,
scaleX: scaleW,
scaleY: scaleH,
});
I'm working on a project in which i'm capturing map screenshot (using dom-to-image library) and sending it to an external API which is returning back some coordinates(x,y,w,h) after processing the sent image. These co-ordinates(rectangles) i'm trying to draw on leaflet.
As the captured image size is bigger than width and height of captured area (don't know why), I need to do scaling the co-ordinates.
Now the problem is Leaflet rectangles are not drawing on accurate position which external API is returning.
However, I'm sure that the external API is returning correct coordinates(x,y,w,h) with respect to sent image's width & height.
Something wrong i'm doing on scaling coordinates.
Below is the code snippet i'm trying:
domtoimage.toPng(document.querySelector("#map"))
.then(function (dataUrl) {
var boundsOnly = map.getBounds();
let topLeft = boundsOnly.getNorthWest();
let topRight = boundsOnly.getNorthEast();
let bottomLeft = boundsOnly.getSouthWest();
let bottomRight = boundsOnly.getSouthEast();
var currBBOXpoints = { x1y1: map.latLngToLayerPoint(topLeft),
x2y2: map.latLngToLayerPoint(topRight),
x3y3: map.latLngToLayerPoint(bottomRight),
x4y4: map.latLngToLayerPoint(bottomLeft) };
var pW = currBBOXpoints.x2y2.x - currBBOXpoints.x1y1.x;
var pH = currBBOXpoints.x3y3.y - currBBOXpoints.x1y1.y;
currBBOXpoints.pW = pW; //calculated the width of captured area
currBBOXpoints.pH = pH; //calculated the height of captured area
var i = new Image();
i.onload = function () { //calculating captured image's actual width, height
$.ajax({
type: 'post',
url: '/externalapi',
data: JSON.stringify(dataUrl),
contentType: "application/json",
dataType: "json",
success: function (resultData) {
resultData["iW"] = i.width; //captured image width
resultData["iH"] = i.height; //captured image height
resultData["currBBOXpoints"] = currBBOXpoints; //captured area bounds
drawRects(resultData);
//NOTE: Captured image's width and height are bigger than width and height of captured area (don't know why)
}
});
};
i.src = dataUrl;
});
function drawRects(rectData) {
var scale = Math.max(rectData.currBBOXpoints.pW / rectData['iW'], rectData.currBBOXpoints.pH / rectData['iH']);
var shifted_x = rectData.currBBOXpoints.pW / 2 - rectData['iW'] / 2 * scale;
var shifted_y = rectData.currBBOXpoints.pH / 2 - rectData['iH'] / 2 * scale;
rectData.od.forEach(rc => {
var modifiedX = Number(rc['x']) * scale + shifted_x;
var modifiedY = Number(rc['y']) * scale + shifted_y;
var modifiedW = (modifiedX + rc['w'])
var modifiedH = (modifiedY + rc['h'])
let point3 = map.layerPointToLatLng(L.point(modifiedX, modifiedY));
let point4 = map.layerPointToLatLng(L.point(modifiedW, modifiedH));
var rectBounds = [[point3.lat, point3.lng], [point4.lat, point4.lng]];
var boundingBox = L.rectangle(rectBounds, { color: "yellow", weight: 1, name: "rect", fillOpacity: 0.10 });
map.addLayer(boundingBox);
});
}
I can't say why but when you set the scale to 0.9 after defining x and y it matches perfect.
var scale = Math.max(currBBOXpoints.pW / imagesize.width, currBBOXpoints.pH / imagesize.height);
var x = currBBOXpoints.pW / 2 - imagesize.width / 2 * scale;
var y = currBBOXpoints.pH / 2 - imagesize.height / 2 * scale;
scale = 0.9
Also you have to set in the fiddle the css width and height:
#mapWrapper{
padding: 10px;
margin: 10px;
width: 1744px;
height: 854px;
}
This is an XY problem (which you successfully identified: the size of the capture is not the size of the map container), so let's fix the root problem instead.
It looks like dom-to-image aligns the top-left corner of the capture, but also takes into account the size of any overflowing elements (such as partially-visible map tiles on the southeast corner) when calculating the size of the capture:
(This can be proven by playing a bit with the browser's box model inspector, comparing the position & size of the southeasternmost tile with the size of the capture)
Since the capture is aligned top-left, you can just clip the capture to the size of the map canvas.
You can use the width and height options of dom-to-image together with the getSize method of L.Map to force clipping the captured image to the dimensions of the map canvas, e.g.:
domtoimage.toPng(document.querySelector("#leaflet"), {
width: map.getSize().x,
height: map.getSize().y
})
See a working demo.
In the image below there is a canvas of 1000x1000, there is a rectangle object with the grey backgroundd of 900x300 at position 50,50 and a border black. Inside the rectangle is a fabric image object which is just loading In a square image of a pug.
The image object is the same size as the rectangle 900x300 positioned at 50,50 on the canvas (same as the rectangle).You can see the highlighted image object bounding box is the same size as the rectangle, how it should be, but the image itself isn't stretching to fill the size of the image object. What I want to see is the pug being the 900 width of the image object and the height based on the actual JPG ratio; so if the image itself is 300x300 and the width is 900 then the height of the image should also be 900 but obviously the bottom would be hidden because the object is only 300 high.
Does anyone know how I can make the JPG stretch to fit the image object?
var img02URL = 'http://fabricjs.com/lib/pug.jpg';
var canvas = new fabric.Canvas('mainc');
var clipRect1 = new fabric.Rect({
originX:'left',
originY:'top',
left:50,
top:50,
width:900,
height:300,
fill:'#DDD',
stroke:'black',
strokeWidth:2,
selectable:false
});
clipRect1.set({
clipFor:'test1'
});
canvas.add(clipRect1);
var image1 = new Image();
image1.onload = function(img){
var image = new fabric.Image(image1,{
width:900,
height:300,
left:50,
top:50,
clipName:'test1',
clipTo:function(ctx){
return _.bind(clipByName,image)(ctx);
}
});
canvas.add(image);
}
image1.src = img02URL;
var clipByName = function (ctx) {
this.setCoords();
var clipRect = findByClipName(this.clipName);
var scaleXTo1 = (1 / this.scaleX);
var scaleYTo1 = (1 / this.scaleY);
ctx.save();
var ctxLeft = -( this.width / 2 ) + clipRect.strokeWidth;
var ctxTop = -( this.height / 2 ) + clipRect.strokeWidth;
var ctxWidth = clipRect.width - clipRect.strokeWidth + 1;
var ctxHeight = clipRect.height - clipRect.strokeWidth + 1;
ctx.translate( ctxLeft, ctxTop );
ctx.rotate(degToRad(this.angle * -1));
ctx.scale(scaleXTo1, scaleYTo1);
ctx.beginPath();
ctx.rect(
clipRect.left - this.oCoords.tl.x,
clipRect.top - this.oCoords.tl.y,
ctxWidth,
ctxHeight
);
ctx.closePath();
ctx.restore();
}
function degToRad(degrees) {
return degrees * (Math.PI / 180);
}
function findByClipName(name) {
return _(canvas.getObjects()).where({
clipFor: name
}).first()
}
A spritesheet image object that contains sprites...
var spritesheet = new Image(); spritesheet.src ="foo.png";
I would like to be able to get a sub image from that spritesheet variable with the desired x, y, width, and height. And then assign a variable that is the sub image of the spritesheet.
How do I do that?
Using a div with backgroundPosition makes this easy since it will auto-clip for us without having to use overflow.
For example, here is the texture atlas from the popular Cookie Clicker idle game.
Using a negative offset for x and y we can select a sub-sprite from the texture atlas:
// Inputs -- change this based on your art
var tw = 48; // Texture Atlas Tile Width (pixels)
var th = 48; // Texture Atlas Tile Height (pixels)
var tx = 3; // tile index x (relative) (column)
var ty = 2; // tile index y (relative) (row)
var src = 'http://orteil.dashnet.org/cookieclicker/img/icons.png';
// Calculations -- common code to sub-image of texture atlas
var div = document.getElementById('clip');
var x = (tx*tw); // tile offset x position (absolute)
var y = (ty*th); // tile offset y position (absolute)
div.style.width = tw + 'px';
div.style.height = th + 'px';
div.style.backgroundImage = "url('" + src + "')";
div.style.backgroundPosition = '-' + x + 'px -' + y + 'px';
// You don't need the remaining parts. They are only to
// show the sub-sprite in relation to the texture atlas
div.style.border = "1px solid blue"; // only for demo purposes
// highlight where in the original texture the sub sprite is
var rect = document.getElementById('sprites').parentNode.getBoundingClientRect();
var hi = document.getElementById('highlight');
hi.style.zIndex = 999; // force to be on top
hi.style.position = "absolute";
hi.style.width = tw + 'px';
hi.style.height = th + 'px';
hi.style.left = (rect.left + x) + 'px';
hi.style.top = (rect.top + th + y) + 'px'; // skip sub-sprite height
hi.style.border = '1px solid red';
<div id="clip"></div>
<img id="sprites" src="http://orteil.dashnet.org/cookieclicker/img/icons.png">
<div id="highlight"></div>
Here's one way:
Create an in-memory canvas to clip the subsprite pixels from the spritesheet and draw those clipped pixels on that canvas
Create a new subsprite image from that canvas's dataURL.
Example code (not tested--may need tweeking):
var spritesheet = new Image();
spritesheet.onload=function(){
// specify desired x,y,width,height
// to be clipped from the spritesheet
var x=0;
var y=0;
var w=10;
var h=10;
// create an in-memory canvas
var canvas=document.createElement('canvas');
var ctx=canvas.getContext('2d');
// size the canvas to the desired sub-sprite size
canvas.width=w;
canvas.height=h;
// clip the sub-sprite from x,y,w,h on the spritesheet image
// and draw the clipped sub-sprite on the canvas at 0,0
ctx.drawImage(spritesheet, x,y,w,h, 0,0,w,y);
// convert the canvas to an image
var subsprite=new Image();
subsprite.onload=function(){ doCallback(subsprite); };
subsprite.src=canvas.toDataURL();
}
spritesheet.src ="foo.png";
function doCallback(img){
// do something with the new subsprite image
}
I came to know about Raphael.js recently and I was trying to do some hands on.
I would like to scale a text but it is not working.
After searching a lot in Google I have not find any idea.
Code is:
var paper = Raphael("paper1", 1000,1000);
var txt = paper.text(20,50,"Hello World").attr({
"font-family":"Arial","font-size":"30px", "font-weight": "normal",
fill: "#000000", stroke:"black", "stroke-width": "0px",
"text-anchor" : "start" , "font-style": "normal"});
flip is working by txt.scale(-1,1)
but txt.scale(2,1) is not scaling the text.
Is there a way to scale a text?
Note: Font size of the text needs to remain same i.e 30px in my case.
Is there a way to scale a text?
DEMO
I checked and it works like a charm
var r = Raphael('test');
var t = r.text(200, 100, "I am a text").attr({fill:'red', 'font-size':30});
$('#zoomin').click(function() {
t.scale(2);
});
$('#zoomout').click(function() {
t.scale(.5);
});
If we scale the text only then font size shall be changed.
We need to convert the text into an image and the we need to scale it.
To do that I have used a hidden canvas.
Following code works for me.
<canvas id='textCanvas' style="display:none"></canvas>
<script>
var paper = Raphael("paper1", 1000,1000);
var img = paper.image(getTxtImg("Hello World","italic","bold","15px","arial",
"#000000",130,35),20,28,300,80)
img.scale(2,1) //Double in width
img.scale(.5,1) //Half in width
function getTxtImg(txt,style,weight,fontsize,fontfamily,color,w,h)
{
var tCtx = document.getElementById('textCanvas').getContext('2d');
tCtx.canvas.width = w;
tCtx.canvas.height = h ;
var fontstr = "" + style + " " + weight + " " + fontsize + " " + fontfamily + " ";
tCtx.font = fontstr
tCtx.fillStyle = color
tCtx.fillText(txt, 0, h);
return tCtx.canvas.toDataURL();
}
</script>