I'm working on a component that transitions an image from a start position + scale to an end position + scale (i.e. to fill up the screen). This is done with a CSS transform animation on translate and scale.
The challenge is that some of the images to transition from may be altered by component users using the object-fit property. However, it appears that the CSS translate does not maintain the object-fit property during translate.
Codepen example here: https://codepen.io/cathyxz/pen/mXgEMB
I know I could technically animate width and height, but I want to keep in line with properties that browsers can animate cheaply for performance reasons, i.e. nothing that affects layout, which leaves us with only position, rotation, scale, opacity.
References: https://www.html5rocks.com/en/tutorials/speed/high-performance-animations/
Is there anyway I can gradually animate my transition to "uncrop" images instead of stretching them?
object-fit is applied to the element before transformation.
Whatever the outcome of all CSS on the object, transform takes it and applies the transformation. In fact, the element is left in DOM untouched. That's why transform does not trigger a repaint (and is considered performant). Only its rendering (the composite layer) is transformed. Its rendering is stretched 4 times on X axis because of the transform. But this does not make the element 4 times wider and therefore object-fit cannot apply as you expect.
Can you gradually uncrop? Yes, but not with transform. In order to do it as cheap as possible (without triggering a repaint on subsequent flow) you need:
A parent placeholder (with position:relative) as tall as your image, to keep the space free in document flow
The element to be animated, with position:absolute. Because of this, even if you animate width, you do not trigger layout, because the element is outside of document flow.
.transform-placeholder {
height: 100px;
position: relative;
}
.transform-placeholder .object-fit {
width: 100px;
height: 100px;
animation: object-fit 2.1s infinite;
animation-timing-function: cubic-bezier(.4,0,.2,1);
}
#keyframes object-fit {
0% {
width: 100px;
}
50% {
width: 400px;
}
100% {
width: 100px;
}
}
<div class="transform-placeholder">
<div class="object-fit" style="background-image: url('https://picsum.photos/1600/400?image=857')">
</div>
</div>
<h2>Animation above does not trigger repaint on any DOM element that's outside the animated div.</h2>
Related
Context:
I'm working on the PDF viewer using react-pdf and one of the issues I've stumbled upon is zooming. Let's say we've rendered 3 pages (800x1100px), our initial scale factor is 1 and we've scrolled to the middle of the overflown container.
Each page is being rendered within a wrapper that takes into account current scale factor like this.
return (
<div ref={ref} css={styles.page} style={{
width: viewerScale * initialPageWidth,
height: viewerScale * initialPageHeight,
left: '50%',
transition: 'all .2s ease-in`,
transform: `translate(-50%, ${topOffset}px)`,
marginBottom: 12,
bottom: 0,
position: 'absolute',
top: 0
}}>
{pdfPage}
</div>
)
})
Wrappers reside within overflown div with the following styles:
height: 100%;
padding: 80px 0;
width: 100%;
overflow: auto;
position: absolute;
I'm not using scale prop provided by react-pdfs Page component as rerendering is quite slow once you have more than 100 pages. Instead I render page once in the highest resolution and rely on the combination of width & height making it sort of responsive.
Once scale factor changes each page is being re-rendered and animated to imitate scaling experience. That being said, I'm experiencing couple of issues:
Initial scrolling position is not preserved due to the change to the total height of overflow container.
Pages are not being scaled with respect to the scroll position which causes scroll jumps.
Question
How does one scale content while preserving scroll position in a Google-like fashion?
I've been trying to reverse-engineer their solution to some extent and I've managed to get ​pretty close by using transform: scale on the overflow container and changing its transform-origin dynamically based on the scroll position.
That being said, it causes flickering once the user scroll fast and causes a lot of repaints which are quite expensive. My understanding is that Google's viewer relies on a combination of container width and padding-bottom attributes in conjunction with absolutely positioned items but I'm absolutely clueless as to how they achieve their scaling effect.
Please let me know if you need me to provide any additional details and thanks in advance!
REFERENCE: http://www.templatewire.com/preview/landscaper/
I want to make a web page, and in that page, I want to have divs/sections each the size of the screen.
Now, I mean, the width and height of the monitor, and it won't resize again, and will stay the width and height of that monitor, regardless of the browser size, and regardless of how much content is inside it.
The link shows you what I mean, but I have a 1920x1080 browser window, you can see the top and bottom of the sections above and below it. I don't want the top and bottom of neighbouring sections to be seen if the monitor is very big, nor do I want the section to not be fully visible if the monitor's too small.
Example, say I had 5 sections like in the reference, and my browser window was 1920x1080, the overall height of that document would be 1920*5400.
(I want it to be the height of the screen minus the height on the nav bar.)
You can use Viewport units (the browser window size). 100vh is the height of the screen. If you got sections that bigger than the height of little screen you can use the min-height property and set it to 100vh.
Since you didn't place your code, this is generally example of use case:
section { min-height: 100vh;}
Read more here:
https://developer.mozilla.org/en-US/docs/Web/CSS/length
Good luck!
It appears you're looking for viewport percentage lenghts.
To give any element current viewport's height, in CSS, one should use:
your-selector {
height: 100vh;
display: block;
}
If the element is a <div> or any other element with a default value of block for display, it obviously doesn't need the second rule.
See it working:
your-selector {
height: 100vh;
display: block;
transition: background-color .3s linear;
}
/* let's add a hover, for testing */
your-selector:hover {
background-color: red;
}
body {
margin: 0;
min-height: 200vh;
}
<your-selector>Test</your-selector>
Note: you can also apply viewport percentage lengths to other properties, such as min-height, max-height, etc...
Note: although default viewport is browser window, that's can change. For example, 3d transforms turn any regular DOM element into a viewport for their children, affecting behavior of viewport percentage lengths, as well as behavior of position:fixed in any of their children.
I have a question about forming elements to form a circle, or align elements to form a circle, depending how you like it to be pronounce, now back to question:
There are couple of examples here on stackoverflow and on the internet regarding this question but any off these examples do not cover Bootstrap 3 responsive align elements to form a circle, I would like if someone can make an example out of mine working JSFiddle example (text needs to be a center of the circle, because I need to animate it), and make this using bootstrap grid system.
Is this possible, can you please explain to me how you do this so I can learn something out of this.
TL;DR; http://jsfiddle.net/k7yxtpc7/
Edit with (very long?) explanation:
So we start off with a bootstrap's hierarchy:
<div class="container-fluid">
<div class="row">
<div class="circle_container col-md-6 col-md-offset-3 col-sm-8 col-sm-offset-2 col-xs-12">
</div>
</div>
</div>
The planetary of images will be put inside .circle_container. Our aim is to make sure the whole circle will respond to .circle_container's width changes and adapt correctly. This way any change Boostrap makes to the container will be reflected on the circle itself, making it Bootstrap-compliant.
First we have to prepare .circle_container a bit. Since it's a whole circle the container must be square-ish. We must find a way to make .circle_container's height to be always equal to its width. I do this by putting a square img inside .circle_container, then scale the img's size according to the container's width:
<div class="circle_container ...">
<img class="transparent_square" src="http://i.stack.imgur.com/5Y4F4.jpg" width="2" height="2" />
</div>
.transparent_square{
width: 100%;
height: auto;
}
Note: I couldn't find a transparent square image on the web, so I had to make do with a white square. In your product a 2pxx2px transparent image is best.
Great, now we have a square container. But we've put a limiter on ourselves too. From now on, the img must be the only child of .circle_container that have a static (default) or relative position, because any further child will extend the container, destroying the square shape. Not a big deal though, since we'll position other children absolute anyway.
Next up is the central text bubble:
<div class="central_text text-center">
<h3>Special for you</h3>
<h5>Lorem ipsum</h5>
</div>
.central_text{
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
position: absolute;
}
The translate trick make use of the fact that percentile value in css transform use the element's pre-render width & height, while all other positioning rule use its parent's width & height instead. By giving the element left: 50%; top: 50% we put its top left corner at the center of its parent, then we translate it up and to the left by 50% of its own width and height, effectively centering the element within its parent. This is only 1 of several methods to center an element within a container, but it fits our situation best because the element is absolutely positioned.
Finally we reach the part where we create the circle. To sum up the trick here: we put the actual image inside a container, which has a pivot point at the center of the container, and position the image off to 1 side of the container equal to the radius of the circle. This way when we rotate the image's container, the image will be moved in a circle around the center of the container, like a drawing compass. After the image has reached our desired position, we rotate the image itself by the same degree in the other direction to compensate for the tilt in orientation, making the image upright again.
The container and image:
<div class="moon_container moon1"><img class="moon moon1" src="http://letscode.ghost.io/content/images/2015/09/stackoverflow.png"></div>
.moon_container{
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
width: 20%; /* This is the final width of the image */
}
I set the width for .moon_container as 20% of .circle_container's width. This will be the width of the images in our final circle. Increasing or decreasing this number simply change the size of the image to your desire.
Now to offset the image from its container:
.moon{
width: 100%;
height: auto;
/* The image can be relative positioned without breaking anything because its parent is absolute */
position: relative;
/* The radius of the circle. This is equal to 175%*20% = 35% of .circle_container's width */
left: 175%;
}
Note that CSS's left use an element's direct parent's width as base unit. If you changed .moon_container's width in the previous part, the actual distance of the images will change as well.
Finally, rotations (I use moon2 as the example here because moon1 doesn't need to rotate):
/* Container rotate 45deg clockwise... */
.moon_container.moon2{
/* 360/8 (the number of images) = 45deg */
transform: translate(-50%, -50%) rotate(45deg);
}
/* ... while the image rotate 45deg counter-clockwise */
.moon.moon2{
transform: rotate(-45deg);
}
Why transform: translate(-50%, -50%) rotate(45deg); and not transform: rotate(45deg);? Because we declared transform: translate(-50%, -50%); earlier for the .moon_container (the centering trick). If we only write transform: rotate(45deg); here, the CSS parser will override the previous rule with the new one, losing the translate part. So we have to append manually.
Repeat the process to all 8 images and we're done!
If you have undetermined number of images, simply use javascript to calculate this rotation part for each image.
I hope my explanation was useful for you. I've always been bad at explanation...
Edit 2: http://jsfiddle.net/k7yxtpc7/3/ Text change on hover version as per OP's request. There's only 1 thing to note in this part, that is
$("body").on({
mouseenter : function(event){
...
},
mouseleave : function(event){
...
}
}, ".moon");
It is good habit to bind all events on either 'body' or document, instead of binding them on the actual elements itself (the .moon). This way you:
Always use only 1 event listener for the hover event, instead of 8 (you can imagine how the number scale up on an actual product).
When you add more images later, you don't have to bind the event on the new .moon again.
Original Answer:
As the requirement is rather vague, I couldn't know if my solution would satisfy you. My solution is based on 3 assumptions:
The entire planetary of images are only based on view port width, similar to how Bootstrap handle its responsive design. If you want to take view port height into consideration maybe I can conjure up another version.
The images are scaled based on the Bootstrap container's width, in order to make sure there's enough space to display all images.
Typography uses Bootstrap's defaults.
The solution avoid using javascript at the cost of not being able to add/remove images on-the-fly. If a dynamic number of images is your intention, I will put calculations in.
Sexy animations compatible.
Unfortunately Bootstrap's center-block only center a block horizontally, I had to make use of the translate trick to center the pivot point.
.central_text{
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
position: absolute;
}
This is only an answer placeholder. I will write detailed explanation once we have a satisfactory solution.
I'm trying to create a website design where I have a 3 by 3 grid of images, and whenever I put the mouse over a particular image, that image gets resized slightly larger than the rest. This re-size shouldn't happen instantly though; it would be a sort of fluid animation where the height and width of the image are increasing by x pixels per frame (while maintaining aspect ratio) until reaching the desired size.
I'm pretty sure this can be done in JQuery but I can't seem to find the command for it. The closest I could find is the JQuery scale effect, but that shrinks the image into nothing instead of dynamically increasing its size to some prescribed maximum.
Now assuming I got the implementation for that correct, how would I get the script to resize the images individually when the mouse hovers over them? So far I've only been able to resize all the images at once, and I'm not sure how I could fix that through the script if I gave each of the 9 images separate ids.
CSS transitions are what you need. You can adjust the timing of the change and animate any CSS property with them.
The following code will expand the image by a factor of 1/5 in 1 second when the mouse hovers over the image.
img {
width: 200px;
height: 150px;
transition: width 1s;
-webkit-transition: width 1s;
transition: height 1s;
-webkit-transition: height 1s;
}
img:hover{
width: 240px;
height: 180px;
}
Here is a rough fiddle to get you started.
I'm having a hard time with a diagonal background image. This is not a "pattern", but a full image used in two types of layouts. (Image: http://i.imgur.com/mcWseu1.jpg)
On one template, the image should remain fixed on the page at 100%
height, positioned to the top right and scrolls with the page.
On the second template, the image should maintain the same aspect
ratio as it would in template one, but it should not be fixed.
Instead, it should scroll up with the rest of the page.
I've been able to achieve the desired result for template one, but I'm having a hard time with template two.
Is Javascript the only solution here? If so, any recommendations? Again, the challenges I can't fix:
Get the image to maintain the same aspect ratio as it would in template one (if it's 100% height to fit the window in template one, then it should size at 100% height in template two with the exception of being fixed) This is to maintain consistency between pages using separate templates.
Thanks for the help.
Edit: I have no code to reference for the actual challenge I'm facing. But here is the solution I've found for the first template:
CSS (applied to an img element):
.abovefold {
width: auto;
height: 100%;
position: fixed;
top: 0;
z-index: -1;
}
I believe you're looking for the CSS background-attachment attribute. To fix a background, simply set it as such:
.example {
background-image: url('http://i.imgur.com/mcWseu1.jpg');
background-attachment: fixed;
}
http://www.w3.org/community/webed/wiki/CSS_shorthand_reference#Background
I've demonstrated this in a fidde: http://jsfiddle.net/GHDbM/
When it comes to dimensioning the background, you'll want to set the background-size attribute:
.example {
background-size: auto 100%; /* Adjust to element height */
}
The auto in the above example is for width and the 100% is for height.
http://www.w3.org/TR/css3-background/#the-background-size
Another fiddle for this: http://jsfiddle.net/sk2RY/