Smooth out rapid javascript-driven position changes with CSS transitions? - css

I think this is a long shot, but worth asking. I've got something similar to the left menu in many mobile apps (e.g. Facebook), that opens smoothly (in my case, using CSS transitions) when you press the 'open' button, but I've also implemented a drag-out feature where you can swipe from the left of the screen to drag the menu out. In this scenario, it's driven by javascript and is too rapid (seemingly) for the CSS transitions to know what to do. It gets confused, stutters, goes backwards etc. So I've simply shut off the transition while the user is dragging the menu out. This works very well on iOS where ti's perfectly smooth, and while Android and Blackberry both try their best, it would be great if it could be smoother.
Here is my CSS for the open/closed menu states
#view_wrap {
position: absolute;
top: 0px;
right: -100%;
left: 100%;
bottom: 0px;
-webkit-transition: all .2s 0s;
-webkit-transform: translate(-100%);
background-color: #fff;
}
#menu.open + #view_wrap {
-webkit-transform: translate(-3.125em);
box-shadow: 0px 0px .3125em rgba(0,0,0,.5);
overflow: hidden;
}
#view_wrap.animating {
-webkit-transition: none;
}
and the drag functionality just changes the translate value with each touchmove
Do you know of any tricks to get such rapid changes to be applied more smoothly with or without CSS transition?

I managed to get this to work, not entirely sure it will work in your situation but I have a slideshow which slides images horizontally both through next & back buttons and through dragging with a finger.
I was in a similar situation to you in that the sliding with the buttons was powered by 3d transitions so very smooth on any device that supported it however the dragging was done with just plane old javascript and stuttered a lot.
I have significantly more code than this but just extracted the bits to do with the transition for clarity.
On the element being dragged I began by adding a transition with a small duration on the touchstart event using jquery, 100ms seemed to work nicely for me.
// inside the touchstart event
$('#my-element').on('touchstart', function(e) {
$(this).css('transition','100ms');
});
While the touchmove event was firing, where I was previously doing a calculation to work out the left hand position and setting the left position of the element, I replaced this by applying a 3d transition and updating the X coordinate as the user moved their finger.
NOTE: translate3d apparently is 'hardware accelerated' on iOS devises where 'translate' isn't so its better to do a 3d translate with Y & Z set to 0.
// Inside the touchmove event, -10px will change as the user drags
$('#my-element').on('touchmove', function(e) {
var newLeft = /** A calculation of the new left hand position **/
$(this).css('transform','translate3d('+newLeft +'px,0px,0px);
});
Finally, on the touchend event I removed the CSS transition and re-set the left hand position of the element to the correct position.
// Inside the touchend event
$('#my-element').on('touchend', function(e) {
var endLeft = /** A calculation of the new left hand position **/
$('#sliding-element').css('transition','');
$('#sliding-element').css('transform','');
$('#sliding-element').css('left', endLeft /** Whatever the new left hand position is **/);
});
Hope this helps

Related

High render usage with animations on mouse move

I found some interesting behavior when testing the performance of some CSS animations. For example, I can animate these 1,000 elements and on my PC they are barely using any resources.
It's honestly impressive that it preforms that well, but if I'm moving the mouse, it's a completely different story.
The only difference is that I am constantly moving my mouse. I want to know why this happens, and if there is anything I can do to improve it. None of these elements have any mouse interaction, no JavaScript listeners, no CSS hover effects or anything like that. I am confused why moving the mouse would then have such a major effect. This high resource usage also happens even when the elements aren't visible and are scrolled past. The window is focused for all benchmarks.
The animation that I am talking about are using transforms and opacity. When using other properties like width, height, margin I get high resource usage no matter what.
Here are several things that I have tried to change this with no noticeable difference in performance.
pointer-events: none, unsurprisingly no effect. I didn't think it worked like that.
will-change
display: inline-block vs position: absolute layout
transform vs opacity animations
translate3d vs translate
Elements outside of the viewport (scrolled past)
I'm guessing this has to do with a GPU and as soon as the mouse moves, it requires the CPU to do something. This would explain the lack of difference on properties that require to CPU to calculate, and properties that can use the GPU are effected so dramatically.
I'm noticing this using Brave and Edge for sure. I don't under the Firefox tool enough to confidently say the same for them, although it appears to happen as well.
How can I make this always run as in the first case? I am never using any mouse interaction, so these elements could careless what it happening with the mouse from my perspective.
Here is a code snippet that should be able to produce the effect. I also have here on a GitHub pages site.
https://jack-stoller.github.io/so_demo.html
<style>
body {
padding-bottom: 150vw;
}
div {
position: absolute;
height: 50px;
width: 50px;
pointer-events: none;
will-change: transform;
background-color: red;
animation: Animation 2s ease infinite;
}
#keyframes Animation {
from {
transform: scale(0);
}
to {
transform: scale(1);
}
}
</style>
<body></body>
<script>
const size = 50 + 8 + 8;
const count = Math.floor(window.innerWidth / size);
[...Array(1000)].forEach((_, i) => {
let div = document.createElement('div');
div.style.top = Math.floor(i / count) * size + 'px';
div.style.left = Math.floor(i % count) * size + 'px';
document.body.appendChild(div);
});
</script>

CSS: Combination of Visibility and Opacity

In my project, I have a button that I want to display and hide under certain conditions. When the button is hidden, it should still take up space (meaning display: none; is not what I want) but it should not be visible and the user should not be able to interact with it. Changing the visibility does everything I want, but then it appears and disappears instantly, and I would like to have a smooth transition. When using the opacity, I can set the transition duration, but then the cursor still changes to a pointer when hovering over the button. By accident, I discovered that I can simply combine these two things to get the desired result: When I click on the button in the example, it fades out slowly, but once it is gone I cannot interact with it anymore. However, I don't understand why it works that way, since the visibility changes instantly, and I also don't know if it works like this in all major browsers. Could somebody answer these two things for me, please?
function hide() {
document.querySelector("button").style.visibility = "hidden";
document.querySelector("button").style.opacity = "0";
}
button {
transition-duration: 1s;
}
<button onclick="hide()">This is a test.</button>
You can use opacity and pointer-events. Also please don't use javascript for css stuff. In my example I add a class instead.
const button = document.querySelector('.js-disable')
button.addEventListener('click', function() {
button.classList.add('button--is-disabled')
})
.button {
background-color: #ededed;
padding: .5rem 1rem;
border: none;
}
.button--is-disabled {
opacity: 0;
pointer-events: none;
transition: opacity 2s;
}
<button class="button js-disable">Click me</button>
The question was basically: why does this work when visibility is changed instantly.
This is in fact not correct. visibility is interpolated and as you have the transition set on everything, the visibility will transition.
Obviously it can not have continuous values, but it 'chooses' a value of visible or hidden, which are the two endpoints you have specified, in steps. The place(s) at which it changes are determined by the timing function.
So, your code works absolutely fine. The element fades away with the opacity and at some point (near the end in the case of the ease timing function) it will switch from visible to hidden.
See for example MDN:
Visibility values are interpolated between visible and not-visible.

css animation: transform origin not appearing to work correctly

I am working on this 3 panel card opening animation.. Here is an example of what I have so far.. example. This is the class that I'm applying for the second animation and transform-origin just doesn't seem to do what I think it should.. But I'm pretty new at this animation stuff.
.two-right {
transform: rotateY(170deg);
transform-origin: 100% 0;
}
As you can see the problem is that I cant get the second animation to open correctly.. On the left side it works just fine but on the right the card isn't opening correctly on the right side of the card.. Right now I'm just poking around and haven't been able to find anything. Any help would be appreciated.
Okay I found the answer to my own question.. The reason is that the flip is based on it's parent.. That's why it is rotating on the wrong axis but after it's scaled and the animation is complete it ends up being positions in the correct location.
Instead of trying to do the animations I put a timeout in the javascript to let it scale and then add the flip animation later. Here is the completed code.
Complete example
.two-right {
/* transform: rotateY(170deg); */
transform-origin: 100% 0;
}
.two-flip {
transform: rotateY(160deg);
}
I first add the .two-right class and then the .two-flip half a second later.

CSS3 Transitions, Keyframes, Animations

I have been playing around with transitions and animations, and I wanted to make the most of using hover with them.
I was wondering if it is possible to make animation happen after being hovered over, not necessarily while hovering over it. For example, if I wanted a picture to slide in and change opacity after the related paragraph is hovered, how would I get it to stay without keeping the mouse of the section?
My last question is if something similar to keyframes can be used with transitions. I prefer transitions because they transition back to the original state instead of snapping back.
I made a jsfiddle with some basic code examples, and I am wondering how to expand upon them. Hopefully the examples help clarify what I am trying to explain.
jsfiddle
Says I need code to link jsfiddle
They are very simple, the first is just an animation (left to right) that I want to remain after hovering once.
The second is a transition (left to right), and I am wondering if I can make an animation similar to the animation that follows it (left to right to left)
I update the fiddle here
However, "left to right to left" can not accomplish through transition.
Because transition only has start status and end status, it can not handle the middle status.
Look here. https://developer.mozilla.org/en-US/docs/Web/CSS/transition
You can accomplish "left to right to left" through keyframes. For example.
#keyframes slideRight {
0% {
margin-left: 0em;
}
50% {
margin-left: 2em;
}
100% {
margin-left: 0em
}
}

Fixed element disappears in Chrome

When scrolling on a website I've built, using the CSS property position: fixed works as expected to keep a navigation bar at the very top of the page.
In Chrome, however, if you use the links in the navigation bar it sometimes disappears. Usually, the item you've clicked on is still visible, but not always. Sometimes the entire thing disappears. Moving the mouse around brings back part of the element, and scrolling with the scroll wheel or arrow keys just one click brings the element back. You can see it happening (intermittently) on https://nikeplusphp.charanj.it - you might have to click on a few of the navigation the links a few times to see it happen.
I've also tried playing with the z-index and the visibility/display type but with no luck.
I came across this question but the fix didn't work for me at all. Seems to be a webkit issue as IE and Firefox work just fine.
Is this a known issue or is there a fix to keep fixed elements visible?
Update:
Only effects elements that have top: 0;, I tried bottom: 0; and that works as expected.
Add -webkit-transform: translateZ(0) to the position: fixed element. This forces Chrome to use hardware acceleration to continuously paint the fixed element and avoid this bizarre behavior.
I created a Chrome bug for this https://bugs.chromium.org/p/chromium/issues/detail?id=288747. Please star it so this can get some attention.
This fixes it for me:
html, body {height:100%;overflow:auto}
I was having the same issue with Chrome, it seems to be a bug that occurs when there is too much going on inside the page, I was able to fix it by adding the following transform code to the fixed position element, (transform: translateZ(0);-webkit-transform: translateZ(0);) that forces the browser to use hardware acceleration to access the device’s graphical processing unit (GPU) to make pixels fly. Web applications, on the other hand, run in the context of the browser, which lets the software do most (if not all) of the rendering, resulting in less horsepower for transitions. But the Web has been catching up, and most browser vendors now provide graphical hardware acceleration by means of particular CSS rules.
Using -webkit-transform: translate3d(0,0,0); will kick the GPU into action for the CSS transitions, making them smoother (higher FPS).
Note: translate3d(0,0,0) does nothing in terms of what you see. it moves the object by 0px in x,y and z axis. It's only a technique to force the hardware acceleration.
#element {
position: fixed;
background: white;
border-bottom: 2px solid #eaeaea;
width: 100%;
left: 0;
top: 0;
z-index: 9994;
height: 80px;
/* MAGIC HAPPENS HERE */
transform: translateZ(0);
-webkit-transform: translateZ(0);
}
The options above were not working for me until I mixed two of the solutions provided.
By adding the following to the fixed element, it worked. Basically z-index was also needed for me:
-webkit-transform: translateZ(0);
z-index: 1000;
This is a webkit issue that has yet to be resolved, oddly making the jump with JavaScript, rather than using the # url value, doesn't cause the problem. To overcome the issue, I supplied a JavaScript version that takes the anchor value and finds the absolute position of the element with that ID and jump to that:
var elements = document.getElementsByTagName('a');
for(var i = 1; i < elements.length; i++) {
elements[i].onclick = function() {
var hash = this.hash.substr(1),
elementTop = document.getElementById(hash).offsetTop;
window.scrollTo(0, elementTop + 125);
window.location.hash = '';
return false;
}
}
I could refine this further and make it is that only it only looks for links beginning with a #, rather than ever a tag it finds.
If it don't work after adding
-webkit-transform: translateZ(0)
than also add
user-scalable=no
on viewport meta
source here
it worked for me
I encountered the same issue in a different case. It was because of usage of same id in multiple place. For example i used #full 2 divs.
It seems that mozilla and I.E. supports usage of same id in multiple cases. But chrome doesnot. It reacted with fixed element disappering in my case.
Just removing the id's solved the problem.
None of them worked for me except calling the modal via javascript
Click me to open MyModal
<script>
function show_modal()
{
MyModal.style.display = 'block';
}
</script>
other than this, none of the solutions above solved my problem.
This Worked for me . Add Overflow property to your top most container which can be Div or Form etc.
div, form
{
overflow:visible;
}
The same issue happened to me. For the main page of the website. I made a fixed header and Used an image for the front poster. Everything worked fine. But the moment I changed the opacity of the poster image my header with position: fixed; got disappeared. It was present there in the chrome developer tools. But was totally transparent on the website.
I tried every solution from StackOverflow, W3shools, Geeke4geeks. But if one thing was fixed another thing got messed up.
So I just opened photoshop and edited the image manually. And then posted it on my website.
It worked.
But still, it won't be effective for divs under the fixed elements.
If none of the answers above worked for you, make sure you aren't a dummy like me and have overflow: hidden; set on the fixed element :(
What if none of above worked at all? simple case of sticky header + mobile side menu pushing content.
I try to find a way to avoid fixed element (sticky header) being translated but in this case nothing is a good alternative.
So as there is no workaround on scope so far there is a JS alternative that I opted for to recalculate absolute position of fixed element. See here: https://stackoverflow.com/a/21487975/2012407
In my case, I just added min-height: 100vh; to fixed element, may be that will be useful for somebody

Resources