So I made this site that shows the planets and their moons from a birds eye view. Now, I know Pluto is not a planet, but I included it because it has lots of moons and it is extremely interesting because of this fact: Pluto and its largest moon Charon orbit EACH OTHER around a point well outside the SURFACE of Pluto. So they do this dance where they have the same orbital period, they are always the same distance away from each other, and always on opposite sides of their orbits (so the distance between them is always maximized). Ok, enough science lecture, onto my problem.
Take a look at the site here: http://distantfuturejosh.com/endomoons/ . You will have to press the right arrow a few times to land on Pluto, then once you do, press the "lock" icon (which constrains the scale) and zoom in for a better view of Pluto and Charon NOT in the right configuration. If you click any where on screen they will begin to orbit for your viewing pleasure.
I can throw Pluto to the opposite side of his orbit by doing a little absolute positioning and margining, but the problem with that is the amount of margin necessary will change as the user scales. I could fix that probably with an if statement but I'd rather not write code that checks every time if the planet (dwarf planet) is Pluto or not.
I would like to solve this with a CSS transform. The problem is, when I apply transform: rotate(180deg); (with appropriate prefixes) it doesn't take. Inspect element doesn't show that it's being overridden (its not crossed out) but it just doesn't budge. I don't know what's causing it to fail and therefore I don't know how to fix it.
If I understood the question correctly, you want to move Pluto to the opposite side of its orbit. I would suggest getting rid of the calculated margin-top and margin-left and using percentages instead, as it scales better. Try the following after removing the calculated margins:
#Pluto {
margin-top: 100%;
transform: translate(-50%, -50%); /* add vendor prefixed ones as well */
}
Update: below is a working demo of a very simplified version of your site. I'm using em for lengths. This way you could zoom in/out by simply changing the font-size in JS (or even in your browser). I hear your concern about rounding, I'm not a 100% sure, but I think browsers that round lengths are going to round your 2244.608px anyway.
Working example: http://jsbin.com/aqiMUxI/5
Related
Background
I'm working on a web app built in HTML (not WebGL/Canvas) which includes 2d viewport controls for panning and zooming the page content. Something like Figma, perhaps, but rendered entirely with DOM, which is a hard technical requirement.
To achieve the viewport functionality I've made extensive use of CSS transform to power all offsets and animations in order to reduce the work required to render changes to compositing, as much as possible. The "canvas" of my app contains many discrete items which can be moved and resized by the user, similar to any typical OS window manager. These 'widgets' may contain their own scrollable content.
For example, after panning 50px,25px and zooming to 1.5x, the DOM and transform values might look like this for a particular "canvas" which has a widget at (20, 100):
<div id="canvas" style="transform: scale(1.5, 1.5) translate(50px, 25px)">
<div id="widget-1" style="transform: translate(20px, 100px)" />
</div>
After a lot of experimentation I've discovered that the most efficient way to render these items across multiple browsers is to promote each individual 'widget' to its own layer by applying will-change: transform to the outermost element. This results in a pretty reasonable framerate, even with a lot of content in the frame while panning and zooming.
Webkit Misbehavior
However, there's one catch - on Webkit-based browsers, when zooming (which is applied via scale transform in CSS on the root canvas element), the contents of the widgets are not re-rasterized to accommodate the new scale value. At a zoom greater than 1x, this produces noticeable blurriness. Images with text, in particular, are basically unreadable.
Above, one image widget and another DOM text widget at 1x (native) scale.
And now, at 2x scale (you won't be able to tell the difference inline in this post, but you can see it at full resolution). Notice that the image is just as illegible as before, and the text is blurry.
For a live reproduction of this problem, see this CodeSandbox (leave "Animation" unchecked).
Side note: this only happens on Chrome, Safari, and Edge - so it seems like an artifact of Webkit's rendering behavior. Firefox actually scales everything quite nicely, and with a faster framerate to boot.
However, the performance of this approach is desirable. After trying some other configurations of layering, I decided the best approach would be to try to force the browser to re-rasterize the widgets once a zoom change animation was completed.
The Hack
The intended goal is to allow the old rasterized textures to persist during the zoom animation to make it as smooth as possible (so the blurriness seen above will be present while the viewport scales up/down), but to trigger a re-rasterization at the final scale once the animation is complete - re-draw all the widgets, taking current scale into account so that their contents are sharp and legible even at 2x zoom.
To correct this problem, I have implemented what feels like a "hack": after the end of each zoom animation, I'm toggling the will-change on all the widgets from transform to initial for 1 frame, and then back again:
const rerasterize = () => {
requestAnimationFrame(() => {
element.style.willChange = 'initial';
requestAnimationFrame(() => {
element.style.willChange = 'transform';
});
});
};
This actually works. Now when I zoom in to 2x, the image text is legible and the DOM text is nice and sharp:
(In my app, you can even see the moment when the zoom animation "settles" and text "pops" into high-resolution.)
However, if I understand correctly, the code above is actually forcing the browser to dispose of the composite layer for each widget for 1 frame, and then recreate it. While the performance seems acceptable in practice, I would much prefer to just ask the browser to invalidate the layer which it has already constructed.
The Question
So, with all that context aside, the question is simply: is there a way to manually trigger an invalidation of a composite layer without trashing it? A magic CSS incantation, perhaps?
I'm also open to alternative approaches with respect to layer grouping which might improve behavior without harming render performance.
Other Stuff I Tried
One thing I noticed when creating the reproduction CodeSandbox is that if I add a transition property to the "canvas" element (which is being transformed to achieve the viewport changes), even if widgets are composited in different layers, it appears to fix the blurriness. You can see this by checking "Animation" in the demo. However, my animations are currently done via JS, so adding a secondary CSS transition on top of this doesn't seem like a great plan.
I tried ripping out JS animations entirely and relying solely on transition, but surprisingly this did not seem to help. Panning and zooming felt noticeably choppier (some of this might come down to native easing feeling less natural than JS spring-based easing), but more concerningly the GPU memory usage and dropped frames were notably worse than without transition - which leads me to believe that transition might be causing a lot more work than I really want on the GPU for my use case (perhaps invalidating layers frequently during animations, when I would prefer them to remain intact until the transition ends).
i was looking for a way to display plain text ( 2d or 3d ) in a threeJS Scene , and apply some css effects to them , i looked around for a while and i came across this . probably what i wanted ..
now the problem is that when i aplly a animate.css ( well knows css animation library ) class to html element , the position of my CSS3D Object is reseted and fliped ( can't explain why )
'http://jsfiddle.net/kd9Tc/4/'
note :
-i think this code example illustrates pretty well what i am trying to do , apply css text effects in webGL Scene ( i need all the camera movement and tweening ) .
i tried THREE.TextGeometry first , but i wanted the text to be in a language other than english and converting the font properly let to nowhere ( displayd ' ?????? ' in all my tests ) and beside that , i've been using css transforms for a while now and i'm pretty comfortable with them , and working with them is much easier for me .
overall the main reason i'm using threeJS is camera movement capability .
i'm glad that i used threeJS beacuse it open's the door to many other features as well , but yet , i'm not sure i;m using ther right tool here :-?
thanks
When you apply a CSS transform on an element drawn by ThreeJS' CSS3DRenderer, it overrides an initial translation and rotation originally set by the renderer itself.
After looking at your fiddle and then looking at the CSS3DRenderer in GitHub I can assess that the renderer does some toying around with the CSS translation and rotation of the elements within, before drawing them, in order to show what you'd expect out of the box (versus what's logical taking into account the way TheeJS handles its coordinates).
Here are the specific lines which I'm referring to. (ThreeJS GitHub)
In order to apply CSS transforms effectively across CSS3DRenderer be sure to not override the translation and rotation transforms. Simply put, take the following into account as the relative origin to every transform:
translate3d(-50%, -50%, 0) rotateX(180deg) rotateY(180deg)
For example this:
-webkit-transform: none;
transform: none;
Would become this:
-webkit-transform: translate3d(-50%, -50%, 0) rotateX(180deg) rotateY(180deg);
transform: translate3d(-50%, -50%, 0) rotateX(180deg) rotateY(180deg);
Here's a modified version of your fiddle which now works somewhat as expected.
(I didn't take the time to correct the X-axis translates, but you should get the gist of what I'm saying.)
Another way of working around this problem would be to somehow extend the CSS3DRenderer so that it corrects the rotate transforms on the fly instead of only at instantiation.
Why is the renderer doing this?
My best guess is that when the renderer was coded, they were expecting transforms to be applied via JavaScript and not CSS.
If you're truly more comfortable with CSS transforms I'd say go right ahead with what you are using. On the other hand if you feel a little more adventurous I'd suggest using TweenJS for animations with ThreeJS. They play quite well together. I use them on my website (link on my profile).
P.S.
#KianP Kudos on the fiddle you've put up. I can honestly say you are working on some pretty interesting stuff and you're using a set of tools that I wouldn't have thought of using together (Animate.css and ThreeJS) but seem to work quite well (Taking into account you correct the positioning as explained above).
fellow earthians.
I, relatively sane of body and mind, hereby give up understanding CSS positioning by myself.
The online resources about CSS go to great length to explain that the "color" attribute lets you set the "color" of stuff. Unmöglish.
Then, that if you want to put something to the left of something else (crazy idea, right?), all you have to do is to set it to float to the left provided you set the "relative" flag on its parent block which has to have a grand-father node with the "absolute" flag set to true so that it's positionned relatively to an other container that may-or-not contain anything, have a position, a size, or not, depending on the browser, the size of other stuff, and possibly the phases of the moon. (CSS experts are advised not to take the previous paragraph seriously. I'm pretty sure someone will point out that my rant is not valid, or w3c-compliant - and that it only applies to the swedish beta version of IE6)
Joking apart, I'm looking for any resource that explains the root causes of all the crazyness behind layout in CSS. In essence, something that would be to CSS what Crockford's articles are to Javascript.
In this spirit, let me point out that I'm not looking for css libraries or grid frameworks like blueprint, or for CSS extension languages like lesscss. I've been using those to ease my sufferings, but I'm afraid it would be like telling someone to "just use jQuery" when they say they can't wrap their mind around prototype inheritence in JS.
If all you can point me to is http://shop.oreilly.com/product/9781565926226.do , I guess I'll consider myself doomed.
Thanks in advance.
EDIT : I probably should not have talked about "positioning" (thanks to all who've explained again that 'position:relative' does not mean 'relative to your container' and that 'position:absolute' means relative to something. I've never been so close to making a monty python script out of a SO questions). I think I meant layout in general (positioning + floats + baselines + all the nonsense required to put stuff on a straight line).
Also please excuse the ranting tone, I'm trying to pour some humour into frustration. I would use zen techniques to calm down if I could, but this only reminds me of
this.
It seems most others have not quite understood the gist of your post. I'll break it down for you:
CSS positiong is complex because it was designed by many different groups of people over a long period of time, with various versions, and legacy compatibility issues.
The first attempts were to keep things simple. Just provide basic styling. Colors, fonts, sizes, magins, etc.. They added floats to provide the basic "cutout" functionality where text wraps around an image. Float was not intended to be used as a major layout feature as it currently is used.
But people were not happy with that. They wanted columns, and grids, boxes, and shadows, and rounded corners, and all kinds of other stuff, which was added in various stages. All while trying to maintain compatibility with previous bad implementations.
HTML has suffered from two opposing factions warring it out. One side wanted simple (compared to existing SGML anyways) solutions, another side wanted rich applications. So CSS has this sort of schitzophrenic nature to it sometimes.
What's more, features were extended to do things they weren't initially intended to do. This made the existing implementations all very buggy.
So what does that mean for you, a mere human? It means you are stuck dealing with everyone elses dirty laundry. It means you have to deal with decade old implementation bugs. It means you either have to write different CSS for different browsers, or you have to limit yourself to a common "well supported" featureset, which means you can't take full advantage of what the latest CSS can do (nor can you use the features there were designed to add some sanity to the standard).
In my opinion, there is no better book for a "mere human" to undrstand CSS than this:
http://www.amazon.com/Eric-Meyer-CSS-Mastering-Language/dp/073571245X
It's simple, concise, and gives you real world examples in a glossy "easy on the eyes" format, and lacking most of the nasty technical jargon. It is 10 years old, and doesn't cover any of the new stuff, but it should allow you to "grok" the way things work.
Positioning is easy to understand:
relative positioning -- Render the page exactly as your normally would. Once done, anything with relative positioning gets moved, relative to where it initially was. Nothing else is affected.
absolute positioning -- Removes the item from the page flow. Other things render as if this weren't there, i.e. they fill in the space that this item took up. They are now positioned absolutely to the nearest element with position: relative OR position: absolute set. In many cases this means they are positioned absolute to the body tag.
You then position things with top, right, bottom and left in CSS.
If something has absolute positioning set:
positioned relative to the top left of the page when using top and left. Positioned relative to the bottom right of the page when using bottom and right.
its width/height can be controlled with a combination of top / bottom or left / right, e.g.: top: 100px; bottom: 100px will make an item that is 100% - 200px of its parent's height (unless you specify a height too in which case top and height are used and bottom is ignored).
There's more to the positioning that just the position property. You need to understand how floats work as well.
http://coding.smashingmagazine.com/2009/10/19/the-mystery-of-css-float-property/
http://coding.smashingmagazine.com/2007/05/01/css-float-theory-things-you-should-know/
These two articles should get you going.
Read a bit on display properties as well, since they're likely to be one of the problematic areas in any given html/css.
this link is mainly about z-index but IMO it does a pretty good job of explaining how things are positioned on a page
http://coding.smashingmagazine.com/2009/09/15/the-z-index-css-property-a-comprehensive-look/
This link is more focused on positioning but it is important to understand the z axis in order to understand the rest of the positioning puzzle
http://kilianvalkhof.com/2008/css-xhtml/understanding-css-positioning-part-1/
Have you checked out this great book? http://shop.oreilly.com/product/9781565926226.do
just kidding.
I don't think you need an entire resource devoted to this one question. It's rather simple once it clicks.
Think of CSS positioning as a way to position items either relatively to themsevels (wherever they fall on the page) or absolutely from on an X/Y coordinate.
You can position something relative and it will either move up or to the right with a positive number, or down and to the left with a negative number.
If you position an element absolutely it will remove itself from the layout altogether (the other elements will not recognize it as being on the screen) and then do one of two things. It will either:
1 - position itself from the top left of the page and either go up/down right/left as I mentioned before depending on whether the numbers are +/-.
2- if the PARENT element is either positioned absolute or relative it will position itself from the top left "corner" of the parent element, NOT the browser window.
Think of z-index as layers in photoshop. With 0 being the bottom (and newer browsers recognize negative z index for even more fun). and 100 as the top later (newer browsers recognize an infinite amount of numbers). The z-index only works with position: relative and absolute.
So if I position something absolute, and it happens to fall underneath another element, I can give it z-index: 100 and it will position itself on top. Keep in mind that the element itself is a rectangle, and the height/width of the element may inhibit you from clicking on the element beneath. You can't do angles,circles, etc. with pure CSS.
Does that help?
These two courses from code academy should explain CSS positioning well:
First, Second.
I am trying to make a 3d transition/transformation so that when a link is click a bar run the length of my website rotates to reveal different options. However it currently looks very untidy, in that each face of the cube has gaps around it, and you can see each face of the cube, regardless of whether it not view (i.e when it is animating you can see text you should be able to see, it also gives the impression that bar grows in width when animating.
Is there anyway I can tidy this up?
I have made a fiddle which can be found here
I’m not sure about the gaps, but applying -webkit-backface-visibility: hidden to .face should sort out the visible text issue.
There‘s a good cube example at the end of this blog post which might help with the gaps — maybe you need to use translateX to get the faces into the right position?
I'm using CSS 3D transformations to zoom a div, for example:
-webkit-transform: scale3d(2,2,1);
The scaling itself works fine in any WebKit browser. However, when using this on Safari (mobile or Windows), the content of the div is not re-rendered. The result is that the content gets blurred after scaling.
This effect only occurs when using 3D transformations. Everything works fine when using
-webkit-transform: scale(2);.
In order to exploit hardware acceleration on iPhone/iPad, it would be nice to use the 3D transformations.
Does anybody know how to tell Safari to re-render a div with the new scale?
The reason why the text is blurry is because Webkit is treating the text as an image, I guess it's the price of being hardware accelerated. I'm assuming you are using transitions or animation keyframes in your ui, otherwise the performance gains are negligible and you should switch to non-3d transforms.
You can either:
• Add an eventlistener for transitionend and then replace the 3d transform for a standard transform, such as...
element.addEventListener("transitionend", function() {
element.style.webkitTransform = 'scale(2,2)'
},false);
• Since Webkit treats stuff as an image, it's better to start big and scale down. So, write your css in your "end state" and scale it down for your normal state...
#div {
width: 200px; /*double of what you really need*/
height: 200px; /*double of what you really need*/
webkit-transform: scale3d(0.5, 0.5, 1);
}
#div:hover {
webkit-transform: scale3d(1, 1, 1);
}
And you get a crispy text on hover. I made a demo here (works on iOS too):
http://duopixel.com/stack/scale.html
I found when trying to force a redraw of a div in safari for other reasons (recalculate text-overflow on hover), that is simple:
selector {
/* your rules here */
}
selector:hover {
/* your rules here */
}
selector:hover:after {
content:"";
}
I did something on hover that changes the padding to accommodate some buttons, but in safari/chrome it doesn't recalculate the content correctly, adding the :after pseudo-class did the trick.
I'm trying to do the same thing. I think what's happening here is that Safari is just scaling pixels. That is, it does all its "normal" browser rendering and then scales the pixels of the result.
Example: Place a relatively high quality image (say 1000x1000 pixels) in a small div (200x200 pixels) and set the image to 100% width and height. When you 3D transform the div to scale 5 times, the result will be blurry in Safari and crisp in Chrome. Use a 2D transform and the image will appear crisp in both.
A workaround is to convert to a 2D transform after you are done with the 3D. However, I've found there is a slight delay when converting between transforms so this isn't working too well for me.
I couldn't find a fix for making zoom-ins not blur in Safari (desktop v7.0.2 and the one included in iOS 6.1.3 and 7.0.6) but I did at some point, notice that I got a sharp png when I set the scale to 5. I don't know why, since that version of my code is lost in all the subsequent changes I made. All other scale factors were blurry.
Since the iPhone and iPad are target devices for this project I ended up abandoning scale transform and animating image height instead. I'm disappointed that the Safari team decided to implement transforms in a way that make them an unviable option in so many cases.
Hmmm... I'm getting the same issue trying to scale up google maps images (hidpi) with Chrome 53.
The only solution I've found so far is to hide the image (or a div that contains the images) and then show it again. Can be with opacity=0 or visibility=hidden, but it actually has to be invisible for at least a frame or two.
This, BTW, isn't even a 3d transform. Just 2D stuff.
Hmmm... I'm getting the same issue trying to scale up google maps images (hidpi) with Chrome 53.
One solution is to hide (opacity, visibility) the image for a few frames (or a container wrapping the image/images/whatever it is)... the better solution which I found in another post on SO was this (issued on the containing DIV):
e.style.transform = 'translateZ(0) scale(1.0, 1.0)'
BTW, my stuff was just ordinary 2d stuff, the translateZ seems to make the difference though, even though I never touched anything 3d.