homepage Welcome to WebmasterWorld Guest from 54.205.119.163
register, free tools, login, search, subscribe, help, library, announcements, recent posts, open posts,
Pubcon Platinum Sponsor 2014
Visit PubCon.com
Home / Forums Index / Code, Content, and Presentation / JavaScript and AJAX
Forum Library, Charter, Moderator: open

JavaScript and AJAX Forum

    
Skew Perspective of 2D Image w/ Javascript
Should be simple, but not sure how to do this.
rescueme




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

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

 

rainborick




msg:4428147
 3:17 pm on Mar 12, 2012 (gmt 0)

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.

Fotiman




msg:4428186
 5:02 pm on Mar 12, 2012 (gmt 0)

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).

rescueme




msg:4428196
 5:31 pm on Mar 12, 2012 (gmt 0)

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

rescueme




msg:4428204
 5:46 pm on Mar 12, 2012 (gmt 0)

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

Fotiman




msg:4428236
 6:37 pm on Mar 12, 2012 (gmt 0)

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]

Fotiman




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


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.

rescueme




msg:4428263
 7:17 pm on Mar 12, 2012 (gmt 0)

Cool. Got it to work now!

Global Options:
 top home search open messages active posts  
 

Home / Forums Index / Code, Content, and Presentation / JavaScript and AJAX
rss feed

All trademarks and copyrights held by respective owners. Member comments are owned by the poster.
Home ¦ Free Tools ¦ Terms of Service ¦ Privacy Policy ¦ Report Problem ¦ About ¦ Library ¦ Newsletter
WebmasterWorld is a Developer Shed Community owned by Jim Boykin.
© Webmaster World 1996-2014 all rights reserved