Are CSS transform values in Safari immutable? - css

Safari seems to take the first valid transform value and cements that into place. For example, the following code when run in Chrome or Firefox will result in a div that scales vertically 300% when hovered.
In Safari, however, it remains at 100% scale because that is the first valid transform value.
#bar {
width: 100%;
height: 50px;
background-color: #36c898;
transform: scaleY(100%);
transition: 0.2s;
}
#bar:hover {
transform: scaleY(300%);
transition: 0.2s;
}
<div id="bar"></div>
Here is a more thorough CodePen with another example.

Related

Is a new transition always applied before the new value?

Suppose I'm using transition, to smoothly change an element's position on hover. I also change the value of transition itself to achieve a different animation in each direction.
It seems like when I move the mouse over the elements, the new transition value is used for the "forward" transition, and when I un-hover, the old value is used for the "reverse" transition.
I couldn't find much documentation about this. Is the order guaranteed?
div {
background: red;
width: 40px;
height: 40px;
position: absolute;
top: 30px;
left: 0px;
transition: left 1s linear, top 1s ease-in-out;
}
:hover div {
top: 150px;
left: 400px;
transition: left 1s linear, top 1s linear;
}
div:nth-child(2) { transition-delay: 0.1s; }
div:nth-child(3) { transition-delay: 0.2s; }
div:nth-child(4) { transition-delay: 0.3s; }
Hover on me!
<div></div>
<div></div>
<div></div>
<div></div>
[...] all 3 properties change their values when I hover on the element, and it's not intuitively clear in which order the changes are applied, at least coming from background experience with a framework such as Core Animation where "model" and "presentation" layers are separate things and the parameters of an animation are set up before the animation starts running. I think your answer makes sense though...
I think the key difference here is that thinking about transition in terms of "animations starting while the element is/isn't :hovered" is the wrong way to think about it.
You are right, the paradigm you're used to (an MVC paradigm) doesn't really apply to CSS. At least not at the level where you as a CSS "writer" are affected. The relevant spec for this, by the way, is CSS Transitions
In CSS, changes to CSS properties apply immediately. Transitions allow you to apply a change to a value over some duration. In your case, you have four divs who are all set to be 30px from the top and 0px from the left edges of the screen.
On hover, thanks to your :hover div selector, new styles apply. Normally they'd apply instantaneously, but because you gave them a transition, it happens over a duration. You can see each one move individually thanks to the transition-delay you gave some of them, as well. To make it even easier to see, I changed the color of each div to be unique. It should be pretty clear which ones move first.
As soon as you remove your mouse, the :hover pseudo-class no longer applies, and so the styles under div are re-applied. Again, they would be instantaneously applied, but the transition you set (along with the transition-delay on 3 of the 4 divs) changes that to occur over a longer duration. So, just as when the :hover` styles apply, the red div moves first, then the others after an increasing 0.1s delay each.
div {
background: red;
width: 40px;
height: 40px;
position: absolute;
top: 30px;
left: 0px;
transition: left 1s linear, top 1s ease-in-out;
}
:hover div {
top: 150px;
left: 400px;
transition: left 1s linear, top 1s linear;
}
div:nth-child(2) { transition-delay: 0.1s; background: blue; }
div:nth-child(3) { transition-delay: 0.2s; background: green; }
div:nth-child(4) { transition-delay: 0.3s; background: yellow; }
Hover on me!
<div></div>
<div></div>
<div></div>
<div></div>

Rotate frame but not image

I want to rotate a frame but not the image inside it. Here is a JSFiddle that does the rotation but the image still moves. How can I keep the image stationary but move the frame only.
https://jsfiddle.net/q6n2w4qm/2/
HTML:
<body>
<div class="center">
<div class="hexagon">
<div class="hexagon-in1">
<div class="hexagon-in2">
</div></div>
</div>
</div>
</body>
CSS:
.center{
width: 200px;
margin: auto;
margin-top: -50px;
}
.hexagon{
width: 200px;
height: 400px;
overflow: hidden;
visibility: hidden;
transform: rotate(120deg);
cursor: pointer;
-moz-transition: all 1s ease-in-out;
-webkit-transition: all 1s ease-in-out;
-o-transition: all 1s ease-in-out;
transition: all 1s ease-in-out;
}
.hexagon-in1{
overflow: hidden;
width: 100%;
height: 100%;
transform: rotate(-60deg);
}
.hexagon-in2{
width: 100%;
height: 100%;
visibility: visible;
transform: rotate(-60deg);
background: url('http://lorempixel.com/g/250/350/city');
repeat: no-repeat;
position: relative;
}
.hexagon:hover{
-ms-transform: rotate(150deg); /* IE 9 */
-webkit-transform: rotate(150deg); /* Chrome, Safari, Opera */
transform: rotate(150deg);
}
1) PNG pseudo-mask overlay
I created a simple HTML/CSS solution, but is only possible with the following three criteria:
The background color behind the image is a solid color
There is enough margin on all sides of the image
You have Photoshop or some comparable image editing software
Working Example
body {
background-color:#222222;
}
.hex-hack {
position:relative;
top:0;
left:0;
}
.base-image {
position:relative;
top:0;
left:0;
z-index:1;
margin: 84px;
}
.hex-overlay {
position:absolute;
width:568px;
height:568px;
top:0px;
left:0px;
z-index:3;
-moz-transition: all 1s ease-in-out;
-webkit-transition: all 1s ease-in-out;
-o-transition: all 1s ease-in-out;
transition: all 1s ease-in-out;
}
.hex-overlay:hover {
-ms-transform: rotate(30deg); /* IE 9 */
-webkit-transform: rotate(30deg); /* Chrome, Safari, Opera */
transform: rotate(30deg);
}
<body>
<div class="hex-hack">
<img class="base-image" src="https://lorempixel.com/output/technics-q-g-400-400-2.jpg" alt="" />
<img class="hex-overlay" src="https://i.imgur.com/zYa31Tw.png" alt="" />
</div>
</body>
Note that the margin of the top image is half of the difference between the widths of the bottom image and the top image to keep it centered.
How I Made the Hexagon Mask in Photoshop
I started with a 400 by 400 image on a 800 by 800 canvas and created a perfect circle around the image so that each corner pixel of the image touched the circle. I cropped the canvas down to the width of the circle (568px). The purpose of this is to guarantee that the image is completely covered as the hexagon mask rotates.
Next, I had to create a 350 by 400 hexagon with no fill, rotate it 30 degrees, and center it in the middle of the canvas. Then I selected the hexagon's pixels (ctrl + click the hexagon layer), inverted the selection (shift + ctrl + I), and filled a new layer with the #222222 background color. I hid every other layer and saved it as a png.
2) CSS clip-path and animate
Another possible solution for you to consider is to use CSS to animate an image's clipping path via the clip-path and animate properties. This might be an easier approach, however, the clip-path property is relatively new and doesn't have the greatest browser support - especially with IE, Edge, and Opera. Here are a couple resources to check out:
CSS Masking - Excellent article on the clip-path property (includes animation demo)
Clippy - Great tool for creating CSS clip-paths
3) SVG animation and clipPath
Finally, this is a very browser-friendly solution, but you'll need some software (like Illustrator) to create an SVG from an image. This is also something I have no actual experience with, but I'm positive it can be achieved with a little research, and some trial and error. Here are some resources to get you started.
SVG clipping/masking techniques
Animating SVGs with CSS

Transform scale working on Chrome but not on Firefox

Once I start animating, on Chrome I get a ripple effect. My circle transform scales up. On Firefox, that exact same animation is ignored for some reason.
$("#animate").click(function() {
$("#square").toggleClass("animate");
$("#fab").toggleClass("ripple");
});
#keyframes ripple {
from {
transform: scale(0)
}
to {
transform: scale(20)
}
}
#square {
position: relative;
width: 300px;
height: 300px;
overflow: hidden;
border: 1px solid red;
transition: background 0.1s linear 0.6s, transform 1s;
transform: rotate(0deg);
}
#fab {
position: absolute;
width: 56px;
height: 56px;
border-radius: 50%;
background: #4FB5AB;
top: 122px;
right: 0;
transform: scale(1);
transition: transform 1s;
}
.ripple {
animation: ripple 1s 0.5s;
transform: scale(20) !important;
/*Duration - delay */
transition: transform 0s 1s !important;
}
.animate {
transform: rotate(90deg) !important;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="square">
<div id="fab"></div>
</div>
<br />
<button id="animate">animate</button>
CodePen Demo
Before I start explaining the problem with your code, here is a word of caution - Do not use transitions and animations together. They generally end up causing problems like the one faced here.
When an animation is specified on an element, it will take complete control over the properties that are being animated unless there is a rule with !important setting. If !important setting is used then that rule takes precedence over the animation. (but unfortunately Chrome and Firefox seem to be handling this case differently).
As per W3C Spec:
CSS Animations affect computed property values. During the execution of an animation, the computed value for a property is controlled by the animation. This overrides the value specified in the normal styling system. Animations override all normal rules, but are overriden by !important rules.
emphasis is mine
In your code, there were two problems and they are as follows:
Within .ripple selector, you were specifying the transition-duration as 0s, which means, there is no transition at all and that the change of transform is an instant one. As explained in the W3C Spec, Firefox seems to be (correctly) giving the control to the rule with !important setting (that is, the transform and transition within .ripple selector) and so it transitions the state change immediately after the specified 1s delay+. Chrome lets animation take control and thus produces the effect you are looking for.
Firefox seems to animate the element quicker than Chrome does and so while a duration of 1s is enough for the animation in Chrome, FF needs it to be 2s to be slower and show the effect.
+ - You can further verify this by removing the !important settings on the rules. Once !important is removed, the animation would take control.
$("#animate").click(function() {
$("#square").toggleClass("animate");
$("#fab").toggleClass("ripple");
});
#keyframes ripple {
from {
transform: scale(0)
}
to {
transform: scale(20)
}
}
#square {
position: relative;
width: 300px;
height: 300px;
overflow: hidden;
border: 1px solid red;
transition: background 0.1s linear 0.6s, transform 1s;
transform: rotate(0deg);
}
#fab {
position: absolute;
width: 56px;
height: 56px;
border-radius: 50%;
background: #4FB5AB;
top: 122px;
right: 0;
transform: scale(1);
transition: transform 1s;
}
#fab.ripple {
animation: ripple 2s 1s;
transform: scale(20);
/*Duration - delay */
transition: transform 1s 1s;
}
#square.animate {
transform: rotate(90deg);
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="square">
<div id="fab"></div>
</div>
<br />
<button id="animate">animate</button>
Finally, please do not use !important unless it is mandatory. Instead just make the selector more specific. In the snippet, I have made it more specific by using the #id.class format.

Image shifting/jumping after CSS transition effect with scale transform in Firefox

I have a problem in latest Firefox browser version 34 (system: Windows 7, screen width: 1600px). I made effect with zooming images (in some container) after hover on it. I am using transform: scale(1.1) with transition: transform 0.3s ease-in-out. But when I hover on image, and after image zoom in.. it make some strange 1px-shifting. Some rendering browser bug, but I hope that existing some fix for it.
Most important CSS definition and part of HTML code:
figure {
display: block;
overflow: hidden;
position: relative;
backface-visibility: hidden;
}
figure img {
width: 100%;
transform: scale(1);
transition: transform 0.3s ease-in-out;
}
figure:hover img {
transform: scale(1.1);
}
<figure>
<img class="img-responsive" src="http://lorempixel.com/600/400/fashion/7">
</figure>
Sample with bug is online here: http://templates.silversite.pl/test/jumpingimg/
I saw also that somebody can fix it, but I do not know how, e.g. box "Our recent work" on http://demo.qodeinteractive.com/bridge/
I had a similar problem on my project. All images were position: absolute; and the transform look like that:
figure img{
transform: translate( -50%, 50%) scale(1);
position: absolute;
top: 50%;
left: 50%;
}
figure img:hover{
transform: translate( -50%, 50%) scale(1.1);
}
I replace every scale with scale3d and that solved my problem.
The final styles look like that:
figure img{
transform: translate( -50%, 50%) scale3d(1, 1, 1);
position: absolute;
top: 50%;
left: 50%;
}
figure img:hover{
transform: translate( -50%, 50%) scale3d(1.1, 1.1, 1);
}
Hope that's will fix your problem
On the link that you provided, http://demo.qodeinteractive.com/bridge/ , if you actually go here: http://demo.qodeinteractive.com/bridge/portfolio/gallery-style-condensed/two-columns-grid/ , you can see that, once looking at dev tools, that they apply a margin of "1px" on left/right side
.projects_holder.hover_text.no_space article .image img {
margin: 0 1px;
}
If you disable that style, you'll see the image move as you're describing when hovering on the image.
Therefore, your CSS for the image should be:
figure img {
width: 100%;
transform: scale(1);
transition: transform 0.3s ease-in-out;
display: block; /* (or inline-block) */
margin: 0 1px;
}
I have just run into this same problem now. The solutions here didn't fix the issue, so I'm posting what I did to get this to work.
Like OP I had a container with oveflow hidden and was the same size as the image inside it. The image would scale on hover to create a 'zoom' effect - but when initially starting and ending the transition, the image was "jumping"/growing a tiny bit on the bottom and right-hand side. This made it jumpy and not smooth.
I had calculated the dimensions of my components based off of percentages, which caused them to be non-integers (Chrome). I have a feeling Scale & Scale3d round the pixel values when scaling, which caused this jump. I gave a parent container display:table, which caused all children to have their width/heights be rounded to be an integer value. This fixed the issue for me, and the images now scale smoothly!
7,5 years later it's still an issue and the now solution is will-change css property. Only IE won't get this, but others seems to be doing fine - no more px jumping (edit: on non retina screens).
figure {
display: block;
overflow: hidden;
position: relative;
backface-visibility: hidden;
}
figure img {
width: 100%;
transform: scale(1);
transition: transform 0.3s ease-in-out;
}
figure:hover img {
transform: scale(1.1);
will-change: transform;
}
I just run over the same issue and for me it looks like that the browser corrects the decimal pixel after the scaling is done. Or some how the height and the width doesn't get scaled equals and that gets corrected in the end.
So I think the solution is to use an image with a 1 x 1 ration factor.
So for me the code of the question works fine when I use a the lorempixel with a width and height of 400px.
Let me know if that solves the issue?!
figure {
display: block;
overflow: hidden;
position: relative;
backface-visibility: hidden;
}
figure img {
width: 100%;
transform: scale(1);
transition: transform 0.3s ease-in-out;
}
figure:hover img {
transform: scale(1.1);
}
<figure>
<img class="img-responsive" src="http://lorempixel.com/400/400/fashion/7">
</figure>

css transition background image fade

I'm using css transitions to cause a fade-in and fade-out effect on a background-image property. The property gets changed via jquery when the user scrolls.
It initially did not work on any browser. I found that setting an completley empty/transparent PNG file on the original element made chrome work, but the other browsers still don't.
Here's an example of the code:
nav {
background:url(/img/empty.png);
background-origin:border-box;
background-position:top;
background-repeat:repeat;
background-size:50px 50px;
transition: background 1s ease-in-out;
-moz-transition: background 1s ease-in-out;
-webkit-transition: background 1s ease-in-out;
}
.contrast {
background:#3a3a3a url(/img/xnav.jpg);
background-origin:border-box;
background-position:top;
background-repeat:repeat;
background-size:50px 50px;
}
The contrast class gets applied to the nav element via jquery. It only seems to fade out on most browsers, but not fade in. It works properly in chrome.
Q1: Is there a cleaner way to do this? Adding a transparent PNG as a background element to the nav element seems like a hack.
Q2: This still doesn't work on firefox, IE or Safari. Can anyone suggest a clean fix?
You can "fake" the background-image opacity with pseudo-element on your:
nav{
position:relative;
}
nav::before{
content: "";
background: url(/img/xnav.jpg);
background-origin:border-box;
background-position:top;
background-repeat:repeat;
background-size:50px 50px;
opacity: 0;
top: 0;
left: 0;
bottom: 0;
right: 0;
position: absolute;
transition: opacity 1s ease-in-out;
}
.contrast{ // applied on nav::before
opacity: 1;
}
Thanks to Nicolas Gallagher for this.

Resources