CSS: Combination of Visibility and Opacity - css

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.

Related

Increase div height while button is pressed, then move up on release

I have a piano keyboard rendered in React and I want to replicate a trailing note effect that's also present in Synthesia. That is, while a key is pressed, a trailing note should be rendered above the key, it's height continuously increasing while that key is pressed, and once released, the trailing note should move up at the same speed that its height was increasing.
I managed to apply the move-up part of the notes:
componentDidMount(){
setTimeout(this.applyMoveUp,1)
}
applyMoveUp =()=>{
console.log("apply move up")
this.ref.current.classList.toggle('trailingNote-moveUp')
}
and this is the css:
.trailingNote {
background:black;
width:20px;
transition: transform 5s;
transition-timing-function: linear;
}
.trailingNote-moveUp{
transform: translateY(-700px);
}
This renders the trailing note on the screen and applies the trailingNote-moveUp css class to it so it starts moving up as soon as it's on the screen, but its height does not increase.
I tried using css keyframes as well (https://codepen.io/Sederfo/pen/NWyEjvK) but I'm not sure how to tell CSS to continue the first transformation until the key is released and how to code all of this in React since I am new to this framework.
How can this effect be achieved in React?

Trying to animate a show/hide definition list with hidden attribute (hidden attribute causes animation to not work)

I'm building an accessible accordion using a definition list. I have this working well, where the <dt> contains a <button> which when clicked toggles an aria-expanded from true/false and toggles a hidden attribute on the corresponding <dd>. There's other things going on for accessibility but that's the basics for the list and the show/hide.
The show/hide display of the dd is then controlled via a hidden attribute selector (in this case, this is coming from the bootstrap stylesheet):
[hidden] {
display: none!important;
}
The show/hide functionality right now is a hard show/hide and I'm trying to add a nice animation via css transitions (this needs to work in IE11 where transitions are supported.)
I built a simple POC for the animation at http://jsfiddle.net/jokvqy6u/ which just toggles a show and hide class. This was just something I could quickly throw together and send out to our marketing team to illustrate the animation to get feedback on.
I thought I'd be able to just easily add hidden and :not(hidden) selectors to the POC and it would work just fine, then I could retrofit into the real accordion, but the animation doesn't seem to work with hidden attributes on the html. You can see a demo of that at http://jsfiddle.net/6zmdhqrn/2/ where the 2nd item animates because it does not have a hidden attribute. Items 1 and 3 have hidden attributes and do not even open up.
Does anyone know why this is happening and how I can get my transitions working with hidden attributes?
EDIT I have it sort of half working at http://jsfiddle.net/6zmdhqrn/3/ by overriding the [hidden] selector coming from bootstrap with some important statements. I still get a hard show, but the hide slides up. I suspect it has to do with the element being display:none where the animations have no dimensions/info to start animating from? Still looking for info/tips on how to get the opening animation.
You guessed right, the problem here is display isn’t an animatable CSS property.
You need to base your animation on an animatable property, such as opacity for example.
Here’s a quick demo:
const el = document.querySelector('div');
document.querySelector('button').addEventListener('click', () => {
if (el.classList.contains('visible'))
el.classList.remove('visible');
else
el.classList.add('visible');
})
div {
opacity: 0;
transition: opacity 0.3s ease;
}
.visible {
opacity: 1;
}
<div class="visible">some content!</div>
<button>toggle visibility</button>
As you may have noticed, setting the opacity to 0 doesn’t hide the element completely (i.e. it still takes up space). In case this is something undesirable, you may consider animating a different property, such as width or height.

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

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

Changing background without sliding effect

I'm working on iphone like slider. It is <input type="range"> tag with button for sliding. When I'm moving the button to the end lock picture on the button must be changed to unlock picture. For this I'm changing background of the button from picture with lock icon to picture with unlock icon (both in one sprite pic). Like this:
$('.btn').css({'background-position': '-62px -104px'});
But for some reason picture not just changes, it's like sliding to the right and disappears and from left side coming the new picture instead. Strange animated effect. And I can't find the reason for this strange behaviour.
Can anybody explain to me why s it and what can I do?
I'm not sure, but maybe it will help: slider uses Zepto js instead of regular jQuery.
It sounds like you have css transitions active on the .btn element. That would cause the background image to transition over with a sliding effect.
Try adding the following css to the element:
.btn {
-webkit-transition: none;
-moz-transition: none;
-o-transition: none;
transition: none;
}
Beware that this may have unintended consequences if any other part of your code relies on transitions being active.
See here for more information http://www.w3.org/TR/css3-transitions/#transition-shorthand-property
Try for example this:
$('.btn').css({'background-position': 'old position'}).hide();
$('.btn').css({'background-position': 'new position'}).show();

Animating non-animatable properties with CSS3 transitions

In my app I'm animating the opacity of elements on the page with something like:
.s {
transition-property: opacity;
transition-duration: 250ms;
}
(with vendor-specific versions, of course). And then
.s.hidden {
opacity: 0;
}
So the animation starts when the hidden class is assigned. Problem is, mouse events are still detected on elements with opacity zero, which I don't want, so I need to either set visibility to hidden or display to none after the transition is finished. I would hope to be able to do something like:
.s {
transition-property: opacity, visibility;
transition-duration: 250ms;
transition-delay: 0, 250ms;
}
and then
.s.hidden {
opacity: 0;
visibility: hidden;
}
to use the CSS transition machinery to do this painlessly. As far as I can tell, that doesn't work because visibility is a non-animatable property. But other transition frameworks such as d3 do handle non-animatable properties, in the obvious way by simply setting the value when the transition starts, or when it ends.
The best I've been able to come up with is to use the transitionend event (and its browser-specific variants such as oTransitionEnd) to catch the end of the transition and set visibility at that point, but I'm wondering if there's any easier way, preferably sticking purely to CSS. Or, as the title of my question implies, are non-animatable properties just that?
visibility is an animatable property, see the spec.
Which means your .hidden class will work as you have described. Demo here: http://jsfiddle.net/ianlunn/xef3s/
Edit: the spec isn't perfectly clear:
visibility: if one of the values is ‘visible’, interpolated as a
discrete step where values of the timing function between 0 and 1 map
to ‘visible’ and other values of the timing function (which occur only
at the start/end of the transition or as a result of ‘cubic-bezier()’
functions with Y values outside of [0, 1]) map to the closer endpoint;
if neither value is ‘visible’ then not interpolable.
But this is what I believe it means:
visibility doesn't smoothly animate between a range of visible and hidden in the way that opacity animates between 1 - 0. It simply switches between visible and hidden at the start and end states of the transition.
Providing the transition is either going to or from visibility, then a transition will occur. If trying to transition between visibility: hidden and visibility: collapse for example, those values are "not interpolable" and the transition would not occur.
So in my example, opacity causes the element to fade out and then at the end of the transition, visibility snaps to hidden.
As a good alternative to display/visibility toggle, opacity:0 with pointer-events:none could be used.

Resources