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
}
Related
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()
}
I am trying to animate using a sprite sheet and use it in my website, however I am knew to animation and I am having some trouble fixing it, currently nothing shows up on my webpage when I use this. I am using a sprite sheet which is called Speech.png, is 18176 x 256 and each frame is 256x256. Here is my code so far.
<script language="javascript">
// screen size variables
var SCREEN_WIDTH = window.innerWidth,
SCREEN_HEIGHT = window.innerHeight;
var canvas = document.createElement('canvas');
var c = canvas.getContext('2d');
canvas.width = SCREEN_WIDTH;
canvas.height = SCREEN_HEIGHT;
var xpos=0,
ypos=0,
index=0,
numFrames = 70,
frameSize= 255;
// Add our drawing canvas
document.body.appendChild(canvas);
//load the image
image = new Image();
image.src = "Speech.png";
image.onload = function() {
//we're ready for the loop
setInterval(loop, 1000 / 30);
}
function loop() {
//clear the canvas!
c.clearRect(0,0, SCREEN_HEIGHT,SCREEN_WIDTH);
/*our big long list of arguments below equates to:
1: our image source
2 - 5: the rectangle in the source image of what we want to draw
6 - 9: the rectangle of our canvas that we are drawing into
the area of the source image we are drawing from will change each time loop() is called.
the rectangle of our canvas that we are drawing into however, will not.
tricky!
*/
c.drawImage(image,xpos,ypos,frameSize,frameSize,0,0,frameSize, frameSize);
//each time around we add the frame size to our xpos, moving along the source image
xpos += frameSize;
//increase the index so we know which frame of our animation we are currently on
index += 1;
//if our index is higher than our total number of frames, we're at the end and better start over
if (index >= numFrames) {
xpos = 0;
ypos = 0;
index = 0;
//if we've gotten to the limit of our source image's width, we need to move down one row of frames
} else if (xpos + frameSize > image.width){
xpos =0;
ypos += frameSize;
}
}
</script>
I need to add an outer border to a JPG image (image has a solid background).
Using context.stroke() only adds an inner border and covers up the inner edges of the image, however I need to add an outer border to the image.
Example Image
If you want to add to an image, convert it to a canvas that is bigger than the original image by n pixels and draw the border.
The function creates a new image with a border amount is the border width in pixels, style is the colour/style you want the border. and image is the original image. Returns the new image with border
function borderImage(image,amount,style){
var paddedImage = document.createElement("canvas"); // create a new image
amount = Math.round(amount); // ensure that the amount is a int value
paddedImage.width = image.width + amount * 2; // set the size
paddedImage.height = image.height + amount * 2;
// get a context so you can draw on it
var ctx = paddedImage.getContext("2d");
ctx.strokeStyle = style; // set the colour;
ctx.lineWidth = amount;
// draw the border
ctx.strokeRect(amount / 2 , amount / 2, image.width + amount, image.height + amount);
// draw the image on top
ctx.drawImage(image, amount, amount);
return paddedImage ; // return the new image
}
To use
var image = new Image();
image.src = "http://i.stack.imgur.com/u2n6E.png";
image.onload = function(){
image = borderImage(this,8,"black");
document.body.appendChild(image);
}
function borderImage(image,amount,style){
var paddedImage = document.createElement("canvas"); // create a new image
amount = Math.round(amount); // ensure that the amount is a int value
paddedImage.width = image.width + amount * 2; // set the size
paddedImage.height = image.height + amount * 2;
// get a context so you can draw on it
var ctx = paddedImage.getContext("2d");
ctx.strokeStyle = style; // set the colour;
ctx.lineWidth = amount;
// draw the border
ctx.strokeRect(amount / 2 , amount / 2, image.width + amount, image.height + amount);
// draw the image on top
ctx.drawImage(image, amount, amount);
return paddedImage ; // return the new image
}
var image = new Image();
image.src = "http://i.stack.imgur.com/u2n6E.png";
image.onload = function(){
image = borderImage(this,8,"black");
document.body.appendChild(image);
}
You could try this
//assuming cvs is your canvas reference
var ctx = cvs.getContext('2d');
cvs.width = yourImage.width;
cvs.height= yourImage.height;
ctx.lineWidth = x; // This is your border thickness
ctx.strokeStyle = '#000000';
ctx.rect(0,0,cvs.width,cvs.height);
ctx.stroke();
ctx.drawImage(yourImage,x,x,cvs.width-2*x,cvs.height-2*x);
Your image size has been reduced a bit though to accomodate the borders. However there will be some lose in height and width (2 times the border width ).
If you want to preserve the aspect ratio of the original image after adding the border, you can change the last line to
ctx.drawImage(yourImage,x,x,cvs.width*(1- (2*x/cvs.width)),cvs.height*(1- (2*x/cvs.height)));
I am using Kinetic.js for this, but I figure this is not Kinetic specific. The problem is as follows:
I am loading an image in a canvas, and I then use Kinetic to rotate this image. How do I get the x and y of, for example, leftmost point of this image?
Try doing the calculations manually:
var theta = image.getRotation*Math.PI/180.;
// Find the middle rotating point
var midX = image.getX() + image.getWidth()/2;
var midY = image.getY() + image.getHeight()/2;
// Find all the corners relative to the center
var cornersX = [image.getX()-midX, image.getX()-midX, image.getX()+image.getWidth()-midX, image.getX()+image.getWidth()-midX];
var cornersY = [image.getY()-midY, image.getY()+image.getHeight()-midY, midY-image.getY(), image.getY()+image.getHeight()-midY];
// Find new the minimum corner X and Y by taking the minimum of the bounding box
var newX = 1e10;
var newY = 1e10;
for (var i=0; i<4; i=i+1) {
newX = min(newX, cornersX[i]*Math.cos(theta) - cornersY[i]*Math.sin(theta) + midX);
newY = min(newY, cornersX[i]*Math.sin(theta) + cornersY[i]*Math.cos(theta) + midY);
}
// new width and height
newWidth = midX - newX;
newHeight = midY - newY;
I have some image coordinates and I want to use them to put a small image on those with javascript. Can this be accomplished with javascript or I need to create the div and modify it's css attributes? By the way: the image can be placed anywhere on the page.
Here is a fully working code: Live Demo
CSS
.overlays{
position:absolute;
}
JS
function showImage() {
// myImage : ID of image on which to place new image
var image = document.getElementById('myImage');
console.log(image.width);
margin = 20;
l = image.offsetLeft;
t = image.offsetTop;
w = image.width;
h = image.height;
// Location inside the image
offX = parseInt(Math.random() * w);
offY = parseInt(Math.random() * h);
if(offX > margin) offX -= margin;
if(offY > margin) offY -= margin;
l += offX;
t += offY;
var newImage = document.createElement("img");
newImage.setAttribute('src', '1.jpg');
newImage.setAttribute('class', 'overlays');
newImage.style.left = l + "px";
newImage.style.top = t + "px";
document.body.appendChild(newImage);
}
Try this to place the new image on coordinates (X,Y) of a parent image:
var newImg = document.getElementById('newImg'), // your new (small) image
img = document.getElementById('parentImg'); // parent image
document.body.insertBefore(newImg, img); // Insert the new image before the image
document.body.insertBefore(img, newImg); // Then switch places (So the small image is after the big one in the DOM)
newImg.style.position = 'relative';
newImg.style.left = X + "px";
newImg.style.top = (Y - img.height)+"px"; // Substract the height of the source image to get the position relative to the top left.
The double insertBefore is to insert the new image after the bid one, by first inserting it before the big one, then moving the big one in front of it.
Then, it's just a matter of setting the new image's coordinates relative to the big image.
Working Example (Set to display the overlay after 2 seconds to show the difference)
css:
#yourId{
position: absolute;
}
js:
var img = document.getElementById('yourId'), // or any other selector
x = x, // your data
y = y; // your data
img.style.left = x+"px";
img.style.top = y+"px";