CSS - Jumpy Scale Transition Issue - css

I have a square wrapper with a circle inside, like on this fiddle
https://jsfiddle.net/Lazarus97/3bntmd8g/1/
<div class="card">
<div class="circle">
<p>
SOME
<br>
Text Test
</p>
</div>
</div>
<style>
body {
display: flex;
min-width: 100vw;
min-height: 100vh;
justify-content: center;
align-items: center;
margin: 0;
padding: 0;
}
.card {
width: 200px;
height: 200px;
background: #eee;
overflow: hidden;
display: flex;
align-items: center;
justify-content: center;
text-align: center;
}
.circle {
background: red;
width: 150px;
height: 150px;
border-radius: 50%;
display: flex;
justify-content: center;
align-items: center;
color: #fff;
transform: translateY(80px) scale(1);
transition: transform .3s ease-in; /* SAME AS ITS CHILD */
}
.circle:hover {
transform: translateY(0px) scale(3);
}
.circle * {
transition: transform .3s ease-in; /* SAME AS ITS PARENT */
}
.circle:hover *{
transform: scale(0.333333333); /* I got this number like this --> 1 / scale(3) 1/3 = 0.333333333 */
}
</style>
but notice how the text inside the circle jumps as you hover the circle, it gets bigger then smaller again, even though I did the math correctly, and created the same transition easing motion.
it should remain the original size during the transition.
Does anybody knows why this happens and is there a way to fix this?
I tried to fix it with the different transition easing motions.

"...why this happens...":
The content of your .circle scales as the .circle scale. That means that you correctly calculated that at the end of the transition, if the circle is 3 times bigger, the content needs to be 3 times smaller to stay the original size. During the transition though, at 0.5 of the transition (0.15 seconds in your case) - the circle is 2 times bigger than it originally were, while the content is 1/3 smaller (that is 2/3=0.6̅), but ALSO 2 times bigger (so it's actually 4/3=1.3̅), as it scales with the circle.
"...is there a way to fix this...":
You either need to make those two objects independent, or find the equation to calculate how smaller does the content need to be at every point in time to be exactly the same size as originally if it scales with it's parent and then apply the results to an animation property with #keyframes at every %. But that is not perfect and would probably have stuttering effect on slower devices.

At first, when you scale the element you scale all its content, and you are doing two animations, both are initiated with :hover and both are ease-in which increase the effect, you can do it softly decreasing the max scale and turning to linear the second animation, that hides very much the effect but stills not being the best.
.circle:hover {
transform: translateY(0px) scale(2);
}
.circle * {
transition: transform .3s linear; /* SAME AS ITS PARENT */
}
You should decrease de min scale too.
.circle:hover *{
transform: scale(0.5);
}
In case you decide still using scale(3), you can use calc() to the best match.
.circle:hover *{
transform: scale(calc(1/3));
}

Related

CSS transition timing function not working as expected

I have a nav-links class on which I want to have horizontal scroll effect based on changing translateX. I change translateX using CSS variable from JavaScript. Irrespective of any timing function the horizontal movement seems choppy from start point to final point. But scale operation and vertical movements is smooth. Computed values of css variables are also correctly passed.
.nav-links {
position: relative;
left: 50vw;
display: flex;
gap: clamp(1rem, 2vw, 2rem);
margin-top: clamp(2rem, 2vw, 3rem);
padding: 0rem clamp(1rem, 2vw, 2rem);
transition: all 500ms cubic-bezier(0.13, 0.53, 0.38, 0.97);
}
.nav-links[data-show-menu="false"] {
transform: translate(var(--translateX), -70%) scale(0.9);
}
.nav-links[data-show-menu="true"] {
transform: translate(var(--translateX), 0%) scale(1);
}
translateX set in percentage terms as eg: --translateX :80%
Also tried to do same with animation, but it results in same issue.

Transform transitions not working when combined with width changes

I'm working on this mobile menu where you can expand and collapse different categories. When doing so, a sliding animation should be performed on the expanded submenu (when expanding) or on the top-level menu (when collapsing).
The structure of the HTML is the following:
<div class="slideOpenMainMenu">
<div class="sideMenuGeneral">
...Top-level menu...
</div>
<div class="sideMenuPanelMainChildren">
...Expanded submenu...
</div>
</div>
By adding and removing classes, I show the appropriate div while hiding the other from view. As you'll notice, I go out of my way to not use anything like display:none; since then I won't be able to animate the containers. Instead I use a combination of width, height, visibility and flex properties to hide and show the containers.
/* Menu parent container */
.slideOpenMainMenu {
position: fixed;
width: 100%;
box-sizing: border-box;
background: linear-gradient(90deg, #12416e 0%, #0d3050 100%);
display: flex;
justify-content: center;
}
/* Top-level menu - Initial state */
.sideMenuGeneral {
width: 100%;
max-width: 620px;
display: flex;
flex-flow: column;
padding: 20px 16px 0 16px;
box-sizing: border-box;
overflow: hidden;
}
/* item submenu - Initial state */
.sideMenuPanelMainChildren {
width: 0;
height: 0;
flex: 0 1 0;
max-height: 100vh;
overflow: hidden;
visibility: hidden;
-webkit-overflow-scrolling: touch;
}
/* Top-level menu - Expanded state */
.slideOpenMainMenu.item-expanded .sideMenuGeneral {
width: 0%;
padding: 0 !important;
visibility: hidden;
}
.slideOpenMainMenu.item-expanded .sideMenuPanelMainChildren {
flex: 1 1 auto;
flex-flow: column;
width: 100%;
height: auto;
overflow: scroll;
visibility: visible;
}
For the animation, I use transform:translateY and opacity properties to create the sliding effect I want.
/* Initial state */
.slideOpenMainMenu .sideMenuPanelMainChildren {
opacity: 0;
transform: translateX(30%);
transition: opacity 0.5s ease, transform 0.5s ease, visibility 0s ease;
}
.slideOpenMainMenu .sideMenuGeneral {
opacity: 1;
transform: translateX(0%);
transition: opacity 0.5s ease, transform 0.5s ease, visibility 0s ease;
}
/* Expanded state */
.slideOpenMainMenu.item-expanded .sideMenuPanelMainChildren {
opacity: 1;
transform: translateX(0%);
}
.slideOpenMainMenu.item-expanded .sideMenuGeneral {
opacity: 0;
height: 0px;
transform: translateX(-50%);
}
As you can see on this fiddle, the animation works well in Chrome and Firefox. Not so well on Webkit and Edge. From what I can tell, there seems to be some kind of conflict between the change in width and the transitions, because when I disable changes in width, you can see the animation play out. What could cause the change in behavior between platforms? Is there a way to correctly sequence the changes?
So while writing this question, I arrived at the answer (as it often happens). The issue seems to be rooted in that the sequence of CSS property changes is different on different browsers. by adding a tiny delay (0.01s) to the transform transition I got it to work, like so:
transition: opacity 0.3s ease, transform 0.3s ease 0.01s, visibility 0s ease;
Still, I find this a little odd and also interesting. If anyone has info on how these things are sequenced in the browser that'd be really great to learn.

CSS/HTML Animation Circle to Square. Which property/attribute determines the Shape?

In the included example found on codepen from CSS-Tricks , which
property in the css code determines the shape of the div?
I understand most of the css properties here. I am just wondering what
determines the shape, and if there is a way to describe/or make other
shapes. For example a star or triangle.
I am new to css and would like to learn the language, especially
animation tricks.
.element {
height: 250px;
width: 250px;
margin: 0 auto;
background-color: red;
animation-name: stretch;
animation-duration: 1.5s;
animation-timing-function: ease-out;
animation-delay: 0;
animation-direction: alternate;
animation-iteration-count: infinite;
animation-fill-mode: none;
animation-play-state: running;
}
#keyframes stretch {
0% {
transform: scale(.3);
background-color: red;
border-radius: 100%;
}
50% {
background-color: orange;
}
100% {
transform: scale(1.5);
background-color: yellow;
}
}
body,
html {
height: 100%;
}
body {
display: flex;
align-items: center;
justify-content: center;
}
<div class="element"></div>
The shape here is determined by border-radius, which can be used to round out the edges of an HTML element (turning a box into a rounded rectangle), or with enough radius (or border-radius: 50%), it can transform a square element into a circle. There's no similar way to produce other shapes (triangle, star, etc), though there are many outside-the-box and creative ways to do this, like using borders to make a CSS triangle.

CSS transition not working with transform: translate

How can I realize a smooth transition for my mobile menu?
The transform property is working, but I want it to happen slowly..
My Sass looks like this
.mobile-nav
display: none
position: relative
width: 100%
background: $white
padding: 30px 0 20px
transform: translateY(-100%)
#media (max-width: 775px)
.mobile-nav.is-o
display: block
transform: translateY(0%)
The main obstacle you're facing is that the display property is not animatable.
Like a light switch, display: none is off and display: block is on. There's no middle ground, no fade effects, no CSS transitions.
However, there are multiple other properties you can use for transitions. Among them:
height
opacity
z-index
background-color
Here's an example:
.mobile-nav-toggle {
font-size: 2em;
cursor: pointer;
}
.mobile-nav {
display: inline-block;
overflow: hidden;
width: 0;
height: 0;
opacity: 0;
transition: width 1s, height 1s, opacity 0s 1s, background-color 0s 2s;
}
.mobile-nav-toggle:hover + .mobile-nav {
width: 150px;
height: 150px;
opacity: 1;
background-color: lightgreen;
transition: 1s;
}
<div class="mobile-nav-toggle">☰</div>
<div class="mobile-nav">
<ul>
<li><a>Item</a></li>
<li><a>Item</a></li>
<li><a>Item</a></li>
</ul>
</div>
References:
Full list of animatable properties in CSS
Transitions on the display: property
I personally use opacity combined with visibility to achieve fade effect like I would like for whole element. Remember to manipulate z-index, so you "hidden" object won't be clickable when dissapeared.

Transform and Stacking Order

I am trying to understand what is really happening “3d” world of CSS.
I made a simple example
Particularly the code which bugs me the most is:
.back {
background-color: tomato;
transform: rotateY(180deg);
z-index: 1;
}
The thing which is not clear to me is why when you hover over .inner, its background color (gold) is not visible?? If you remove the transform property from .back or if you set the rotateY to 0deg then the gold background color of the .inner is clearly visible.
Why is the transform property of .back changing the stacking order?
Logically it makes sense that children(.front and .back) should appear in front of their parent(.inner).
Also, I would like to know what really happens when you set transform-style to flat? Does that make parent and all of its children collapse into single “unit” where element with highest stacking order takes priority/visibility?
in your code :
.outer {
display: block;
width: 200px;
height: 200px;
border: 2px solid gold;
perspective: 1000px;
padding: 10px;
margin: 0 auto;
}
.inner {
position: relative;
height: 100%;
width: 100%;
transition: transform 2s linear;
transform-style: preserve-3d;
background-color: gold;
backface-visibility: visible;
transform: rotateY(50deg);
}
.sides {
position: absolute;
width: 100%;
height: 100%;
left: 0;
top: 0;
color: white;
backface-visibility: hidden;
}
.front {
background-color: blue;
transform: translateZ(20px)
}
.back {
background-color: tomato;
transform: rotateY(180deg) translateZ(10px);
}
.inner:hover {
transform: rotateY(180deg)
}
<div class="outer">
<div class="inner">
<div class="sides front">Front Side</div>
<div class="sides back">Back Side</div>
</div>
</div>
you are using
transform: rotateY(180deg) translateZ(10px);
The transforms are applied right to left, so first it goes to the front 10px. But after that, it rotates 180deg. (around the transform-origin that is constant). That makes the previous 10px go towards the back instead of to the front.
if the order is the inverse
transform: translateZ(10px) rotateY(180deg);
now the rotation is done first, and so the translation is unafected by it and goes to the front.
and No, sorry, z-index is not a substitute for 3-d transforms, if you want to use 3d transforms, translation is the only way to go ....
In your first example, z-index is useless, as can be seen easily
codepen with z-index removed
This works because you are setting
backface-visibility: hidden;
So only the face that is facing front will be visible

Resources