I have an element of given dimensions (say, 100x300 px) living in a container of the same height and variable width that I want to transform using rotateX around -webkit-transform-origin: top center; while picking the -webkit-perspective of the container so that it appears that the bottom line of the image stays where it is but only expands to fill the entire container.
Wow, that sounds confusing. Here's a picture:
So basically, I want to create a trapezoid with a fixed upper width and a variable lower width. I can't however quite figure out the math behind the relations... Javascript welcome. Following example works IF the body is 600px wide: http://jsfiddle.net/24qrQ/
Now the task is to change the perspective and rotation continuously with the body width. Any Ideas?
Okay, after a glass of wine the maths came back to me:
First, let's look at the perspective / rotation ratio. Viewed from the side, it looks like this:
The red element is rotated around its upper edge, if we project its lower edge to the lower edge of the container, the intersection between the projection line and the line perpendicular to the container at its upper edge is the required viewpoint. We get this by simple trigonometry (notice phi here is in radians, not in degree).
If we apply this, the lower edge of the element will always appear on the lower edge of the container. Now the free parameter is rotation. This seems to have the relation
rad = pi/2 - element.width / container.width
for sufficiently large widths, however I can't quite wrap my head around the actual relationship. Here is a fiddle: http://jsfiddle.net/24qrQ/6/
Basically, you are trying to figure out how to put an object in 3D space, so it lines up with a 2D viewport. That's always a tricky thing.
I don't know what the math is, and most other probably don't either. This is hardly a common problem. But here's how I would go about figuring it out.
The only variable here is width. And the 2 values that would need to change based on the width is -webkit-perspective on the container and -webkit-transformon the inner element. So I would manually edit the values for a few different widths and record the 3D values that you had to enter to make things look right. (I'd use the web inspector to edit the values in realtime so you get immediate feedback)
One you have a few data points, plot them out on a graph and then try to figure out how they change. I have a hunch it's a parabolic curve, but it may but hyperbolic or sinusoidal too, my 3D math isn't good enough to know for sure.
Then you can try figure out an equation where when you input the widths you've sampled, you get back the manual 3D values you set previously. Then use JS to read the width of the container and set the CSS values to make it look right.
I've done that with 3 widths 300, 450, 600:
http://jsfiddle.net/24qrQ/3/
Some trends are obvious. As width increases, perspective goes up at an increasing reate, and rotation goes down at an increasing rate.
Figuring out the exact formula, is now up to you.
As a simpler alternative, if figuring out a formula becomes too difficult, you could manually curate a handful of widths and 3D values that look nice and store them in JS somewhere. Then you could just linearly interpolate between them. It wouldn't be exact, but it might be close enough.
It would also be less fun!
Related
I modified a very complicated animated grass example found here and checked a few other examples. I want to make something very similar to this but less complicated code. safari is on its knees trying to render this... also I had to use css transforms to get it to hang from the top of the browser window as desired.
http://yak.is/hairy/
if someone could just explain the basics. I'm lost trying to comprehend the code I hacked.
here's another one I tried:
http://yak.is/hairy/2.html
the shape isn't very hair-like at all, but I do like the simplicity and the cursor input.
basically:
need to draw a realistic-looking amount of hair/fur across the top of the screen (updating canvas width to window width and drawing the correct density of lines) and give them a natural-looking automatic "sway" in the wind.
each line will bend a little bit from cursor input.
I have big horizontal strip image in photoshop which is made of lots of smaller elements. The background is transparent and the strip goes from smaller elements (left) to bigger elements (right). My goal is to make this strip interactive to mouse events.
Each element is some kind of polygonal image which is trimmed left and right and then exported as a png. It is then imported into a canvas.
The problem is that I can put them side by side but since they are not rectangles I need a way to calculate the offset made up by the transparent pixels on each side of each element to make them stick together correctly... I am using KineticJs to get a precise hitarea for each element... So maybe there is a way to do it automatically with kineticjs,or there is some kind of operation I could do using each image data?
My problem illustrated:
Any ideas?
Also I am doing this simply because I would prefer precise mouseOver bounding box on each item (rather than a simple rectangle) and would rather avoid the solution to calculate each offset manually... But maybe that's not worth it?!
Ok, so you have yourself a custom shape you want to use, here is a tutorial for that: http://www.html5canvastutorials.com/kineticjs/html5-canvas-kineticjs-shape-tutorial/ , the simplest thing you can do, and even that seems fairly long, is to calculate the bounding lines for that shape. (Two somewhat vertical lines, and two somewhat horizontal lines). Then you test if the right vertical line of shape one crosses with the left vertical line of shape two, if they do, then set the coordinates of the images to be the same coordinate.
http://www.mathopenref.com/coordintersection.html
line1 = ax + b ..... line2 = cx+d //see possible tests
if(...intersection test...){ // or just test if some coordinate is left of some other coordinate
shape2.setX(shape1.getX()+shape1.getWidth()); //account for image width, so they don't overlap
shape2.setY(shape1.getY()) // no need to account for height
}
UPDATE: This is a very rough solution to the workings of the problem. The next step would be to do more fine tuning dependent on each image.
http://jsfiddle.net/9jkr7/15/
If you want precise areas, use an image map. With some clever finagling and a blank image gif you should be able to have the background you want whenever you hover over any particular area of the image map (might require javascript).
The other option I can think of would be to use SVG itself or one of the many libraries in existance to build interactive vector graphics into your page.
You could also write a function that calculates the left most, top most, right most, and bottom most pixel by looking at all of the pixels in the image data. Here's a tutorial on that:
http://www.html5canvastutorials.com/advanced/html5-canvas-get-image-data-tutorial/
Do you have any idea how it could be possible not to scale a HTML canvas element while scaling the whole website (ctrl+"+" in most browsers) but to make its dimensions bigger? I have got an application where you can view large images, zoom and pan them in the canvas element. Now I think it would be cool to scale the website with ctrl+"+" to have more space for viewing the image. As it is by default the canvas scales, too and you gain nothing.
You can use technique described in this article http://novemberborn.net/2007/12/javascriptpage-zoom-ff3-128.
The main idea is to place any two elements and set for the first element css value in pixels like top\left\width etc and for the second element percentage value. When you scale the page the percentage value stays unchanged, but the value in pixels changes depending on zoom factor.
Based on these changes you can calculate the scale factor and multiple it with canvas dimensions to scale it.
How to calculate scale factor you can find in the demo link from article in the script block.
Something like Zoomooz.js could work - http://janne.aukia.com/zoomooz/
I'm using JavaScript to inflate a span of left-aligned text until it fills its container, or until it reaches variable thresholds in either height or width. This process results in a widely varying font-size.
My problem is that, with certain characters, there is a small amount of padding on the left... and while normally it's not noticeable, with very large font sizes it can reach 10 pixels or more.
I whipped up a page to demonstrate the effect on various characters:
http://jsfiddle.net/kBu7S/
The text span exists in a design where every other element going down the page is aligned pixel for pixel to the left edge, so it's very visually distracting with the larger text sizes.
Can anyone think of a way to calculate the amount of padding, so that I can bump the relative position of the span? Or maybe there's even a CSS solution? (letter-spacing and word-spacing have no effect.)
I'll admit that even a dirty solution seems pretty unlikely... thanks for your consideration though.
The padding is often called the "shoulder" of the type, and it's intrinsic to the design of the typeface. There's no simple way to counter-act it, because it's there for optical reasons, to give the type the right spacing. After all, if you type IIIIIII, you need to have some spacing between the capital I's, and that only comes from this padding. Other characters, like WWWWWW, need none.
Your best bet is to do an experiment with your typeface, and measure the actual padding on each character, then build a table mapping characters to their paddings. Then you can adjust the character's position. This is a lot of work, but I don't know what else you can do.
This worked on the fiddle:
#text:first-letter { margin-left:-3px}
You could expand that to a class...
.fontCorrection:first-letter { margin-left:-3px }
...and apply it as appropriate.
Well, you could build a script to generate an image using the same typeface with the same size (black on white background) and read the bitmap to count the distance from the left edge to the first black pixel.
It's kind of overkill, but it's the only thing I can think of.
I'm making a canvas-based game engine and am wondering if anyone has any good info on how to achieve an overhead view perspective. What I'm looking for is somewhere halfway between the traditional birds eye view and the old SNES mode7 view. Just a slight angle to give the illusion of 3D.
I'm trying to figure out what is going to be the best way to deal with the perspective skewing. I'm not doing rotations so 3D matrix stuff would be going overboard, but I need to be able to deal with rendering the map layers at a consistent angle and it'd be nice if the angle was adjustable. I also need to deal with the depth warp. Basically, the bottom row of pixels should be 1:1 pixel width and height, then for each row it'd get, for example, 5% smaller or something like that. What I'd like is to be able to supply a large canvas as a texture and then supply a camera angle between 0 and 90 where 0 is perfectly horizontal and 90 is birds eye view.
Anyone have any related tutorials or sample code? I've searched online a bit, but everything I've found seems to either be unsuitable for use in this particular application or overly complex, doing all sorts of crazy 3D skewing and rotation stuff. All I want is to take the normal tiled grid and lean it back a bit, no rotations or complicated stuff like that.
Here's an example of what I want;
Here's an example. http://img801.imageshack.us/img801/2176/perspectivesample.jpg
The bottom pixel row is 1:1 pixel ratio, and each row above that progressively gets shorter horizontally and vertically. The source texture of the top center region is normally about half the height of the bottom center region, but it has been shrunk vertically and horizontally to fit the perspective.
What I'm thinking might work best is to render the current viewport state to another canvas in flat, birds eye view, with approximately 50% extra space on the top and sides, then slice an upside triangular region from that and draw that to the actual visible canvas.
Only problem is, I suck at math when it comes to calculating angles and such.
if i understand you right, you just want a simple trapeze transformation. if so, maybe this or this link helps you out. for images that aren't centered it would just be an additional rhomboid tranformation, wich is easily possible with canvas, as far as i know.
What you're talking about is something that can be done simply with any 3D api. However since you've decided to try to stick to 2D canvas, you have to do everything in the 2D world which means working with rectangles, rotation, scaling, skewing, etc. Also know as affine transformations as mentioned the other answer.
What you want to do is possible, but since you want to use 2D you have to think in terms of 2D functions.
Generate your initial image.
Add a slice from the bottom of the original image to the bottom of the canvas, very slightly positioned to the left so the center of the image matches up with the center of the current canvas.
Very slightly increase the scale of the entire image
Repeat until you get to the top of the image.
The Pseudo code would look like this...
imgA = document.getElementById('source');
// grab image slices from bottom to top of image
for (var ix=height-slice_height;ix>=0;ix-=slice_height)
{
// move a section of the source image to the target canvas
ctx.drawImage(imgA, 0,ix,width,slice_height,
0-half_slice_width_increase,width,slice_height);
// stretch the whole canvas
ctx.scale(scale_ratio, 1);
}
This will take lots of tweaking, but that is the general solution.
scale_ratio will be a number slightly larger, but very close to 1.
ctx is the standard canvas 2D context
half_slice_width_increase is the 1/2 the amount the canvas will grow when scaled by the scale ratio. This keeps the scaled image centered.
To look correct you would want to transform the background tiles first before you add the icon overlays.