Welcome to WebmasterWorld Guest from 54.166.152.121

Forum Moderators: open

Message Too Old, No Replies

Skew Perspective of 2D Image w/ Javascript

Should be simple, but not sure how to do this.

     
12:44 am on Mar 12, 2012 (gmt 0)

5+ Year Member



Hello,

I want to do something fairly simple, I think... I want to take an image and skew it so that it looks 3-dimensional.

You can see an example of what I am trying to accomplish here: [world.org...]

Any ideas?

I saw something similar, but much more complicated than I need here:

[ozoneasylum.com...]

I figure with what I need to do, it should only be a few lines of a code. I'd appreciate any suggestions.

Thanks,

- Jeff
3:17 pm on Mar 12, 2012 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member



You can skew images with HTML 5/CSS3 transform, but there are compatibility issues with older browsers. If you want to do this dynamically, the best way is probably with ImageMagick with a server script in PHP or Perl.
5:02 pm on Mar 12, 2012 (gmt 0)

WebmasterWorld Senior Member fotiman is a WebmasterWorld Top Contributor of All Time 5+ Year Member Top Contributors Of The Month



There are no native browser methods for skewing images, so you'll need to use the HTML5 canvas to accomplish it.
Edit: Note, there *may* be a way to do it with CSS3, but I've not figured out how. :)

I put together a small jsFiddle that I think does what you want. I wasn't sure how much skew you're looking for, but looked to be roughly 20%.

[jsfiddle.net...]

This is about as few lines as I think you'd want to get it down to. Note, this example uses JavaScript, and would replace every image with a canvas. You could modify if it operate on specific images.

Note, I didn't do any cross browser testing on this (works in Chrome though).
5:31 pm on Mar 12, 2012 (gmt 0)

5+ Year Member



Hi Fotiman,

It looks like that is exactly what I was after. :-) I was indeed looking for an HTML5 solution, and we'll just do a browser test with javascript and serve something different for older browsers that can't handle this.

Best wishes,

- Jeff
5:46 pm on Mar 12, 2012 (gmt 0)

5+ Year Member



Hi again, Fotiman,

I am not used to using jsFiddle. If you have a few minutes, do you mind simplifying the code even further so basically I can copy/paste the javascript straight into my web page and it will draw that single skewed image wherever I put the code?

- Jeff
6:37 pm on Mar 12, 2012 (gmt 0)

WebmasterWorld Senior Member fotiman is a WebmasterWorld Top Contributor of All Time 5+ Year Member Top Contributors Of The Month



The jsFiddle example is using jQuery. Here's the code snippet you're inquiring about:

// Find each img, and replace it with a canvas
$('img').each(function (index, el) {
var c, // new canvas which will replace this img element
ctx, // context of new canvas
i, // loop counter
tmpCtx, // temp context for doing work
h, // height of the image / new canvas
w, // width of the image / new canvas
dh, // destination height (used in translation)
dw, // destination width (used in translation)
dy, // destination y
leftTop,// left top corner position
leftBot;// left bottom corner position

// Get the height/width of the image
h = el.height;
w = el.width;

// Create the canvas and context that will replace the image
c = $("<canvas height='" + h + "' width='" + w + "'><\/canvas>");
ctx = c.get(0).getContext('2d');

// Create a temporary work area
tmpCtx = document.createElement('canvas').getContext('2d');

// Draw the image on the temp work area
for (i = 0; i < h; i++) {
dw = Math.abs((w * (h - i) + w * i) / h);
tmpCtx.drawImage(el,
0, i, w, 1, // sx, sy, sw, sh
0, i, dw, 1); // dx, dy, dw, dh
}

// Calculate the left corners to be 20% of the height
leftTop = parseInt(h * .2, 10);
leftBot = parseInt(h * .8, 10) - leftTop;

ctx.save();

// Draw the image on our real canvas
for (i = 0; i < w; i++) {
dy = (leftTop * (w - i)) / w;
dh = (leftBot * (w - i) + h * i) / w;
ctx.drawImage(tmpCtx.canvas,
i, 0, 1, h,
i, dy, 1, dh);
}

ctx.restore();

// Replace the image with the canvas version
$(el).replaceWith(c);
});


The piece that is targeting which images to change is this:

$('img').each(function (index, el) {

$('img') is jQuery terminology that will get every 'img' element. Then for each it performs the function that follows.

I would NOT recommend dropping ANY JavaScript into your web page wherever you drop the code. That suggests mixing your content layer (your HTML markup) with your behavior layer (JavaScript), which is bad practice and harder to maintain. Instead, let your content stand on its own and use progressive enhancement to add extra functionality.

Here's a modified example. Note, however, that it still relies on jQuery. If you're not using jQuery, then this will still need modification:

// Find each img, and replace it with a canvas
function skew(el) {
var c, // new canvas which will replace this img element
ctx, // context of new canvas
i, // loop counter
tmpCtx, // temp context for doing work
h, // height of the image / new canvas
w, // width of the image / new canvas
dh, // destination height (used in translation)
dw, // destination width (used in translation)
dy, // destination y
leftTop,// left top corner position
leftBot;// left bottom corner position

// Get the height/width of the image
h = el.height;
w = el.width;

// Create the canvas and context that will replace the image
c = $("<canvas height='" + h + "' width='" + w + "'><\/canvas>");
ctx = c.get(0).getContext('2d');

// Create a temporary work area
tmpCtx = document.createElement('canvas').getContext('2d');

// Draw the image on the temp work area
for (i = 0; i < h; i++) {
dw = Math.abs((w * (h - i) + w * i) / h);
tmpCtx.drawImage(el,
0, i, w, 1, // sx, sy, sw, sh
0, i, dw, 1); // dx, dy, dw, dh
}

// Calculate the left corners to be 20% of the height
leftTop = parseInt(h * .2, 10);
leftBot = parseInt(h * .8, 10) - leftTop;

ctx.save();

// Draw the image on our real canvas
for (i = 0; i < w; i++) {
dy = (leftTop * (w - i)) / w;
dh = (leftBot * (w - i) + h * i) / w;
ctx.drawImage(tmpCtx.canvas,
i, 0, 1, h,
i, dy, 1, dh);
}

ctx.restore();

// Replace the image with the canvas version
$(el).replaceWith(c);
}

var i,
imagesToSkew = document.getElementsByTagName('img'); // This is where you define which elements to replace

for (i = 0; i < imagesToSkew.length; i++) {
if (imagesToSkew[i].className && imagesToSkew[i].className == 'perspective') {
skew(imagesToSkew[i]);
}
}

In this example, I've separated the function that skews the images from the code that selects the elements. Then I'm calling that function only on the elements I want to change. In this example, I'm looking for img elements that have a 'perspective' class assigned to them. For example:

<img src="..." class="perspective">

Then I call the skew method on only those that have this class. Alternative, you could give each image a unique id and call document.getElementById(id) to get each image you want replaced (though that would be slightly harder to maintain). Then just put this script before your closing </body> tag and you're good to go.

If you're not using jQuery, then you'll need to replace the few bits from the example. The result would be this:


// Find each img, and replace it with a canvas
function skew(el) {
var c, // new canvas which will replace this img element
ctx, // context of new canvas
i, // loop counter
tmpCtx, // temp context for doing work
h, // height of the image / new canvas
w, // width of the image / new canvas
dh, // destination height (used in translation)
dw, // destination width (used in translation)
dy, // destination y
leftTop,// left top corner position
leftBot;// left bottom corner position

// Get the height/width of the image
h = el.height;
w = el.width;

// Create the canvas and context that will replace the image
//c = $("<canvas height='" + h + "' width='" + w + "'><\/canvas>");
c = document.createElement('canvas');
c.height = h;
c.width = w;
ctx = c.getContext('2d');

// Create a temporary work area
tmpCtx = document.createElement('canvas').getContext('2d');

// Draw the image on the temp work area
for (i = 0; i < h; i++) {
dw = Math.abs((w * (h - i) + w * i) / h);
tmpCtx.drawImage(el,
0, i, w, 1, // sx, sy, sw, sh
0, i, dw, 1); // dx, dy, dw, dh
}

// Calculate the left corners to be 20% of the height
leftTop = parseInt(h * .2, 10);
leftBot = parseInt(h * .8, 10) - leftTop;

ctx.save();

// Draw the image on our real canvas
for (i = 0; i < w; i++) {
dy = (leftTop * (w - i)) / w;
dh = (leftBot * (w - i) + h * i) / w;
ctx.drawImage(tmpCtx.canvas,
i, 0, 1, h,
i, dy, 1, dh);
}

ctx.restore();

// Replace the image with the canvas version
el.parentNode.replaceChild(c, el);
//$(el).replaceWith(c);
}

var i,
imagesToSkew = document.getElementsByTagName('img'); // This is where you define which elements to replace

for (i = 0; i < imagesToSkew.length; i++) {
if (imagesToSkew[i].className && imagesToSkew[i].className == 'perspective') {
skew(imagesToSkew[i]);
}
}


Then all you need to do is add class="perspective" to the images that you want to appear this way.

[edited by: Fotiman at 6:45 pm (utc) on Mar 12, 2012]

6:39 pm on Mar 12, 2012 (gmt 0)

WebmasterWorld Senior Member fotiman is a WebmasterWorld Top Contributor of All Time 5+ Year Member Top Contributors Of The Month




I would NOT recommend dropping ANY JavaScript into your web page wherever you drop the code.

I meant to say, I would NOT recommend dropping ANY JavaScript into your web page to have it draw the image wherever you drop the code.
7:17 pm on Mar 12, 2012 (gmt 0)

5+ Year Member



Cool. Got it to work now!
 

Featured Threads

Hot Threads This Week

Hot Threads This Month