Cropping an image equally from top to bottom - css

I'm trying to achieve a small animated intro to a website. Basically I want a centralised image to crop equally from top and bottom and then move up to the top of the screen.
So far I have the following: http://dk8.co/animation.html
This is using the following CSS:
#pic {
position:absolute;
top:0%;
width: 100%;
height: 200px;
left: 0px;
background:url(web/images/AM-title.jpg) no-repeat;
-moz-background-size:100% auto;
-webkit-background-size:100% auto;
-webkit-background-position:0% auto;
background-size:100% auto;
-o-background-size: 100% auto;
-webkit-animation-name: move;
-webkit-animation-duration: 2s;
-webkit-animation-iteration-count: 1;
-webkit-animation-timing-function: ease-in;
}
#-webkit-keyframes move {
0% {
top: 50%;
height: 400px;
}
50% {
top: 50%;
height: 300px;
}
}
However, the problem is that the "cropping" is occurring from just the bottom up rather than from top and bottom.
Is there any way of achieving an equal cropping effect on the top and the bottom?

The very simple solution is to assign 50% to the background-position property, or simply add 50% to the shorthand you have above:
background: url(web/images/AM-title.jpg) 50% 50% no-repeat;
Here is a working example: http://jsfiddle.net/sl1dr/L3sQW/

I wrote this five years ago for Prototype.js. You can scoop the code and see how it works.
Basically you move the position and background position of the element at the same time.

Related

CSS Animation - Move background position indefinitely

I'm trying to make an element's background move on a continuous loop from right to left without the snap back to the initial position and have it be smooth on all screen sizes. I've tried using repeat-x and doubling the image's width, and when it gets to the halfway point the animation loops, but you see it snap back to the first frame. So I came up with this hack (see below), but I'm hoping there's a better, more efficient way without having to use insane numbers and a smaller image.
Is there a way I can make the background repeat (repeat-x) but just move indefinitely?
Here's what I have at the moment:
.container:after {
animation: mist 300s infinite linear;
background: url("images/mist.webp") top left repeat-x;
background-size: cover;
content: "";
height: 100%;
left: 0;
position: absolute;
top: 0;
width: 100%;
}
#keyframes mist {
0% {
background-position: 0 0;
}
100% {
background-position: -20376px 0; /* image width x 6 */
}
}
Actually you need to use background-repeat: round, and set the position -100vw, so it will repeat the bg for all the space, and will loop for view width only,
check this snippet
.container:after {
animation: mist 6s infinite linear;
background: url("https://global-uploads.webflow.com/5ef5480befd392489dacf544/5f9f5e5943de7e69a1339242_5f44a7398c0cdf460857e744_img-image.jpeg") top left;
background-size: cover;
background-repeat: round;
content: "";
height: 100%;
left: 0;
position: absolute;
top: 0;
width: 100%;
}
#keyframes mist {
0% {
background-position: 0 0;
}
100% {
background-position: -100vw 0;
}
}
<div class="container"></div>

Long CSS animation easing (background-position)

I'm trying to animate a background image position smoothly with CSS over a longer period, let's say 60 seconds:
#movingbackground {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-image: url('https://upload.wikimedia.org/wikipedia/commons/thumb/6/68/Bigsurflowers.jpg/1280px-Bigsurflowers.jpg');
overflow: hidden;
background-position: left center;
animation: zoomin 60s ease-in infinite;
}
#-webkit-keyframes zoomin {
0% { background-position: 0% center; transform: scale(1.0); }
50% {background-position: 100% center; transform: scale(1.2); }
100% { background-position: 0% center; transform: scale(1.0); }
}
#keyframes zoomin {
0% { background-position: 0% center; transform: scale(1.0); }
50% {background-position: 100% center; transform: scale(1.2); }
100% { background-position: 0% center; transform: scale(1.0); }
}
<div id="movingbackground"></div>
The small movements in the beginning and end are "jumping" a few pixel every second instead of moving slowly (may depend on screen size).
The reason for that is probably that there is not enough movement to fill the required number of frames, especially when the animation is eased. As I think I have seen this effect working smoothly somewhere I wonder how to work around this.
Here's a Fiddle as well.
Animation of background-position makes browser to do layout, paint and composite.
Re-layout and re-paint are heavy on CPU and cause "jumping".
Instead of that, you might apply your background to pseudo-element (or use <img> in your HTML) and animate its transform property using 3d transformation.
It will make browser to use GPU for the animation and animation will run in composition phase pretty smoothly.
See the snippet below:
html,
body {
margin: 0;
padding: 0
}
#movingbackground {
position: relative;
width: 100vw;
height: 100vh;
overflow: hidden;
}
#movingbackground:before {
content: '';
position: absolute;
top: 0; left: 0; z-index: -1;
height: 100%;
width: 200%;
background: url(https://upload.wikimedia.org/wikipedia/commons/thumb/6/68/Bigsurflowers.jpg/1280px-Bigsurflowers.jpg) 0 50% / cover;
animation: zoomin 60s ease-in infinite;
}
#keyframes zoomin {
50% {
transform: translateX(-50%) scale(1.2)
}
}
<div id="movingbackground"></div>
I did some testing and came to the conclusion that it's probably impossible. (At least with transitions or animations)
The problem is the way browsers render images on a screen. The pixels of the image apparently get lined up with those of your screen.
So the picture always "jumps" exactly one pixel at a time.
That means, that the more pixels you have in your image, the more steps it will make. But when using ease-in it will always stutter in the beginning.
As I think I have seen this effect working smoothly somewhere
That was probably not realized with css.

background-size transition on hover causes chrome to "shake" background image

I am trying to achieve an effect I saw recently, where background image zooms on hover. I pretty much did it with example here: https://jsfiddle.net/qyh6nbwt/ but it seems to be very shaky (you will understand what I mean by hovering over it), I'm on osx running latest chrome version, have not checked it in other browsers yet.
Is there a way to make it smoother, so it doesn't "shake" on zoom in?
HTML
<div id="example">
test
</div>
CSS
#example {
background-image: url(http://www.jeroenkemperman.nl/wp-content/uploads/2014/06/Johns_Inc_Pizza_Spaghetti_wikipediacommons.jpg);
background-position: center center;
width: 250px;
height: 250px;
transition:all 1000ms ease;
background-size: 100% auto;
}
#example:hover {
background-size: 160% auto;
}
just use transform, scale.
so just instead of setting the bg image to 160% use
transform:scale(1.5);
some information about the transform css property you can find here
to use the transform scale in your case you will need a wrapper with overflow hidden so just the inner div gets bigger and cut of by the outer div.
see updated fiddle.
greetings timmi
Used transform scale instead of a background-size change transition: https://jsfiddle.net/qyh6nbwt/
transform: scale(2, 2);
So I made this my mission to figure this out, turns out it wasn't quite as simple of a fix as I thought.
It's a little dirty, but you need to frame your div within a div like this:
<div class="example">
<div></div>
<p>test</p>
</div>
Then from here, you can target the zooms more accurately, like this:
div.example {
height: 250px;
width: 250px;
overflow: hidden;
position: relative;
}
div.example > div {
position: absolute;
height: 100%;
width: 100%;
-moz-transition: all 1.5s;
-webkit-transition: all 1.5s;
transition: all 1.5s;
-moz-transform: scale(1,1);
-webkit-transform: scale(1,1);
transform: scale(1,1);
background-image: url('http://www.jeroenkemperman.nl/wp-content/uploads/2014/06/Johns_Inc_Pizza_Spaghetti_wikipediacommons.jpg');
-moz-background-size: cover;
-webkit-background-size: cover;
background-size: cover;
z-index: -1;
}
div.example:hover > div {
-moz-transform: scale(2,2);
-webkit-transform: scale(2,2);
transform: scale(2,2);
}
You can adjust the zoom and speed using the scale and transition properties.
Here is a working fiddle to demonstrate. Hope this helps, I checked in Chrome/Safari/Firefox and it seems to work pretty well.

CSS transform-origin issue

I'm working on a ghost floating here.
The issue I'm having is the transform-origin property. Right now the shadow (the ellipse on the bottom) seems to be expanding from the left to the right and then shrinking in that direction again. The behavior I wanted was for the shadow to expand and shrink from the middle - hence the transform-origin: 50% 50%;.
Here's the relevant code, although it helps to look at the Codepen:
.container {
position: absolute;
top: 50%;
left: 50%;
margin-left: -64.5px;
margin-top: -85.5px;
}
.shadow {
margin-left: 22px;
animation: shrink 3s ease-out infinite;
transform-origin: center center;
ellipse {
transform-origin: center center;
}
}
#keyframes shrink {
0% {
width: 20%;
}
50% {
width: 27%;
}
100% {
width: 20%;
}
}
If anyone has any ideas, thank you so much! Really struggling with this for some reason.
Okay I got a solution for you.
What I did was use margin and width for the animation to keep it centered. I also gave the p tag that is containing the svg shadow a set width of the ghost to keep the shadow centered.
Here is the css I edited,
.shadowFrame {
width: 130px;
}
.shadow {
animation: shrink 3s ease-out infinite;
transform-origin: center center;
ellipse {
transform-origin: center center;
}
}
#keyframes shrink {
0% {
width: 90%;
margin: 0 5%;
}
50% {
width: 60%;
margin: 0 20%;
}
100% {
width: 90%;
margin: 0 5%;
}
}
Here is the live link.

part of div transparent

I would like to create a menu that consists of three layers.
The menu is supposed to stretch over the entire screen width.
The first layer is an image that contains a gradient. Since the menu is stretched over the width the amount of change/width in color depends. It looks something like this:
The second layer looks just like the first layer, but lets say blue instead of red. So it contains that same gradient. I want to overlay parts of this layer with the first to highlight a selected menu item.
The third layer contains the menu items.
Here's a jsfiddle: http://jsfiddle.net/UrVq2/9/
and it's corresponding code:
HTML:
<div id="firstLayer"></div>
<div id="secondLayer"></div>
<div id="thirdLayer">Click me</div>
CSS:
#firstLayer {
background-image:url('http://s21.postimg.org/imynbhjo7/example.jpg');
background-size: 100% 100%;
width: 100%;
height: 100px;
min-width:900px;
position:absolute;
left:0;
top:0
}
#secondLayer {
background-image:url('http://s13.postimg.org/5o17i8wwn/example2.jpg');
background-size: 100% 100%;
width: 100%;
height: 100px;
min-width:900px;
position:absolute;
left:0;
top:0
}
#thirdLayer {
position:absolute;
top: 50px;
left: 50%;
}
When something in the menu is to highlight, and to get a match between the first layer gradient and the second layer gradient, I stretch both first and second layer over the entire width of the screen. Then I try to make the parts of the second layer that are not to highlight transparent. However, I fail doing so. Is there a way to achieve it, or should I take another approach?
e.g. I have tried following https://stackoverflow.com/a/8422890/1419386, however I cannot apply 1. and 3. suggestion, due to the gradient. 2. suggestion I don't believe I can apply, I want a sudden transparency at a point in the image and not some gradient into transparency.
(just splitting the gradient off from both layers does not work for me because it actually also difuses with the color beneath it (red or blue), so it is a little bit a simplified example).
There are 3 different posibilities that I can think of to solve your problem.
All of them are based on clipping instead of transparency, so the first thing that we need to do is to change the order of the divs:
HTML:
<div id="thirdLayer">hover me</div>
<div id="secondLayer"></div>
<div id="firstLayer"></div>
I have moved also the third layer in the front so that I can use the hover state without script, but this is not important.
The first posibility uses clip. Css:
#firstLayer {
background-image:linear-gradient(90deg,white,black), linear-gradient(90deg,red,red);
background-size: 100% 40%, 100% 100%;
background-repeat: no-repeat;
width: 100%;
height: 100px;
min-width:900px;
position:absolute;
left:0;
top:0;
clip: rect(10px,0px,80px,0px);
-webkit-transition: all 2s;
}
#secondLayer {
background-image:linear-gradient(90deg,white,black), linear-gradient(90deg,blue,blue);
background-size: 100% 40%, 100% 100%;
background-repeat: no-repeat;
width: 100%;
height: 100px;
min-width:900px;
position:absolute;
left:0;
top:0
}
#thirdLayer {
position:absolute;
top: 110px;
left: 50%;
}
#thirdLayer:hover ~ #firstLayer {
clip: rect(10px,800px,80px,400px);
}
Most of the CSS is standard stuff. I have replaced youyr images with gradients, so that the example does not depend on the availability of them. The key issue is using
clip: rect(10px,800px,80px,400px);
To show only the part of the div that you want. The main problem with this solution is that it is not posible to use percentages in that property, so it is of limited use if you want it to be flexible.
demo 1
The second posibility is to play with the background-size:
#firstLayer {
background-image:linear-gradient(90deg,white,black), linear-gradient(90deg,red,red);
background-size: 1000% 40%, 1000% 100%;
background-repeat: no-repeat;
backgrond-position: -10% 0%;
width: 10%;
height: 100px;
position:absolute;
left:-10%;
top:0;
-webkit-transition: all 3s;
transition: all 3s;
}
#secondLayer {
background-image:linear-gradient(90deg,white,black), linear-gradient(90deg,blue,blue);
background-size: 100% 40%, 100% 100%;
background-repeat: no-repeat;
width: 100%;
height: 100px;
position:absolute;
left:0;
top:0
}
#thirdLayer {
position:absolute;
top: 110px;
left: 50%;
}
#thirdLayer:hover ~ #firstLayer {
left: 47%;
background-position: 47% 0%, 47% 0%;
}
demo 2
Notice that to compensate that the width of the background is now 10%, the background size is now 1000%, so the porportion is the same:
There can be slight offsets in rendering, due to the different calculus, but the system is quiet good.
The third posibility is to use a clipping mask (with limited browser support)
#firstLayer {
background-image:linear-gradient(90deg,white,black), linear-gradient(90deg,red,red);
background-size: 100% 40%, 100% 100%;
background-repeat: no-repeat;
background-position: 0% 0%;
width: 100%;
height: 100px;
position:absolute;
left:0;
top:0px;
-webkit-transition: all 3s;
transition: all 3s;
}
#secondLayer {
background-image:linear-gradient(90deg,white,black), linear-gradient(90deg,blue,blue);
background-size: 100% 40%, 100% 100%;
background-repeat: no-repeat;
width: 100%;
height: 100px;
position:absolute;
left:0;
top:0;
}
#thirdLayer {
position:absolute;
top: 110px;
left: 50%;
}
#firstLayer {
-webkit-mask-position: -15% 0px;
-webkit-mask-size: 84px 100%;
-webkit-mask-image: linear-gradient(90deg, rgba(0, 0, 0, 1), rgba(0, 0, 0, 1));
-webkit-mask-repeat: no-repeat;
}
#thirdLayer:hover ~ #firstLayer {
-webkit-mask-position: 52% 0px;
}
We define a mask, and the only remaining issue is to set the position
demo3

Resources