I am using Vue.js and transitions.
This is my component.
<main id="main-content" class="t-center" role="main">
<transition :name="!phone ? 'slide-right' : 'slide-left'" mode="out-in">
<keep-alive>
<component :is="stepComponent" />
</keep-alive>
</transition>
</main>
This is my .scss
.fade-enter-active,
.fade-leave-active {
transition: opacity .3s;
}
.fade-enter,
.fade-leave-to {
opacity: 0;
}
.slide-left-enter-active,
.slide-right-enter-active,
.slide-up-enter-active,
.slide-left-leave-active,
.slide-right-leave-active,
.slide-up-leave-active {
transition: transform .2s, opacity .2s;
}
.slide-left-enter,
.slide-right-enter,
.slide-up-enter,
.slide-left-leave-to,
.slide-right-leave-to,
.slide-up-leave-to {
opacity: 0;
}
.slide-left-enter {
transform: translateX(100vw);
}
.slide-left-leave-to {
transform: translateX(-100vw);
}
.slide-right-enter {
transform: translateX(-100vw);
}
.slide-right-leave-to {
transform: translateX(100vw);
}
.slide-up-enter {
transform: translateY(10px);
}
.slide-up-leave-to {
transform: translateY(-10px);
}
Now, what happens is when the stepComponent changes, another component comes into place and the old one fades away from right to left and new component comes from right to left. but the issue is that for just a second or milliseconds, a vertical scrollbar appears and when the new component comes at final the place, that vertical scrollbar is removed.
the vertical scrollbar only appears if i test it on chrome (responsive mode). If I test it on the same screen size as responsive, but not activate a responsive mode, then no vertical scrollbar appears and it's all good.
Why does vertical scrollbar appear? overflow:hidden didn't help.
well, this might be a bit overkill, but when there is vue, there is a way. You can actually use the life cycle event hooks and watchers to flip the overflow-y property as #revliscano mentioned.
First, start watching the route changes. This is where we will get rid of the overflow-y. Every time, we change a route, overflow-y will be hidden.
watch: {
$route(to, from) {
document.getElementById("scrollContainer").style.overflowY = "hidden";
console.log("hidden now");
}
then, when the DOM is updated, we will put it back after a few miliseconds.
updated: function () {
setTimeout(function () {
document.getElementById("scrollContainer").style.overflowY = "auto";
console.log("set to visible");
}, 300);
},
scrollContainer is the inner div I use, but you should be able to change it to whichever the element you like.
You can keep this logic in your App.vue. So, your route-views will be left neat and tidy.
Related
I'd like to accomplish the following with my drop down menu.
1 - Show it upon click
2 -Hide it on second click
3 - Hide it when clicking anywhere outside of it.
4 - Do all that with a slide effect
I've got 1-3 covered. I'm blocked on 4.
How would I create a slide effect along with the following click event happening bellow?
I've got a working proof of concept using jQuery's slideToggle (not shown here)... however, I'd like to learn how to do it in the react way.
in case you'd like to see the full the code:
react drop-down nav bar
// CASE 1 Show Hide on click, no slide effect yet
class ServicesDropdown extends Component {
constructor() {
super();
this.state = {
dropdown: false
};
}
handleClick = () => {
if (!this.state.dropdown) {
// attach/remove event handler
document.addEventListener('click', this.handleOutsideClick, false);
} else {
document.removeEventListener('click', this.handleOutsideClick, false);
}
this.setState(prevState => ({
dropdown: !prevState.dropdown,
}));
}
handleOutsideClick = (e) => {
// ignore clicks on the component itself
if (this.node.contains(e.target)) {
return;
}
this.handleClick();
}
render() {
return (
<li ref={node => { this.node = node; }}>
<a href="#!" onClick={this.handleClick}>Services +</a>
{this.state.dropdown &&
(
<ul className="nav-dropdown" ref={node => { this.node = node; }}>
<li>Web Design</li>
<li>Web Development</li>
<li>Graphic Design</li>
</ul>
)}
</li>
)
}
}
A while ago I figured out how to apply a slide-down effect to a React component, it's not exactly the same behavior but you might find my code & description useful. See my answer to a different, related question here: https://stackoverflow.com/a/48743317/1216245 [Edit: It was deleted since then, so I'm pasting the description below.]
The blog post is here: http://blog.lunarlogic.io/2018/slidedown-menu-in-react/. Feel free to steal any part of the code.
Here's a short description of the most important parts of the solution.
As for the React/JSX part, you wrap the component that you'd like to slide in a CSSTransitionGroup. (You can read more about this React Add-on here: https://reactjs.org/docs/animation.html#high-level-api-reactcsstransitiongroup and here: https://reactcommunity.org/react-transition-group/.)
<div className="component-container">
<CSSTransitionGroup
transitionName="slide"
transitionEnterTimeout={300}
transitionLeaveTimeout={300}
>
{ this.state.showComponent && <Component /> }
</CSSTransitionGroup>
</div>
Note that it's all wrapped in a container, which you'll need for the animation to work like you'd like it to.
And here is the CSS I used for the slide animation effect:
/*
Slide animation styles.
You may need to add vendor prefixes for transform depending on your desired browser support.
*/
.slide-enter {
transform: translateY(-100%);
transition: .3s cubic-bezier(0, 1, 0.5, 1);
&.slide-enter-active {
transform: translateY(0%);
}
}
.slide-leave {
transform: translateY(0%);
transition: .3s ease-in-out;
&.slide-leave-active {
transform: translateY(-100%);
}
}
/*
CSS for the submenu container needed to adjust the behavior to our needs.
Try commenting out this part to see how the animation looks without the container involved.
*/
.component-container {
height: $component-height; // set to the width of your component or a higher approximation if it's not fixed
min-width: $component-width; // set to the width of your component or a higher approximation if it's not fixed
}
For the full example & demo check out http://blog.lunarlogic.io/2018/slidedown-menu-in-react/.
What aboot using CSS transitions?
UI Animations with React — The Right Way
As an example I found this slide effect that could be implemented on your navbar.
.wrapper {
position: relative;
overflow: hidden;
width: 100px;
height: 100px;
border: 1px solid black;
}
#slide {
position: absolute;
left: -100px;
width: 100px;
height: 100px;
background: blue;
-webkit-animation: slide 0.5s forwards;
-webkit-animation-delay: 2s;
animation: slide 0.5s forwards;
animation-delay: 2s;
}
#-webkit-keyframes slide {
100% { left: 0; }
}
#keyframes slide {
100% { left: 0; }
}
<div class="wrapper">
<img id="slide" src="https://cdn.xl.thumbs.canstockphoto.com/-drawings_csp14518596.jpg" />
</div>
I hope it helps :)
My current version is hiding each row, but it happens too quickly, as you can see here in my codepen. Repro by deleting the top element.
I would prefer if you the events unfolded like this:
Fade out
Slide up
I'm unsure how to do this using CSS transitions and the ReactTransitionGroup
If I can get to the stage that you see the element disappearing, then everything bunching up that would be a great start!!
My transition stuff:
const CustomerList = ({ onDelete, customers }) => {
return (
<div class="customer-list">
<TransitionGroup>
{customers.map((c, i) =>
<CSSTransition
key={i}
classNames="customer"
timeout={{ enter: 500, exit: 1200 }}
>
<CustomerRow
customer={c}
onDelete={() => onDelete(i, c)}
/>
</CSSTransition>
)}
</TransitionGroup>
</div>
);
}
My CSS:
.customer-enter {
opacity: 0.01;
}
.customer-enter.customer-enter-active {
opacity: 1;
transition: 500ms;
}
.customer-exit {
opacity: 1;
}
.customer-exit.customer-exit-active {
opacity: 0.01;
transition: 1200ms;
}
Update
I've figured out with css you can have two transitions happening in sequence something like this
.something {
width: 200px;
height: 100px;
background-color: black;
transition: background-color 200ms ease, height 200ms ease 200ms;
}
.something:hover {
height: 0px;
background-color: blue;
}
So it is just a case of the <CSSTransition timeout={} /> actually waiting for it...
React Update
I have the "effect" working.... see codepen
But, obviously here, the elements still remain, which isn't the right functional behaviour
So, I asked this question on the Github repo for this library, and got a reply with a proper working version. Until the guy who responded posts an answer here, I want to share his answer.
Hi there, you have two issues with the codepen. The first is you
aren't using a stable key for your list items, so removing something
in the middle of the list won't work right. The second is that your
setup is correct, and the timeout is working and the animation is
playing, but you don't see the animation for height play because you
can't animate from height: auto with plain css transitions.
here https://codepen.io/anon/pen/dzPvEO?editors=0110 is a working pen
but it requires setting an explicit hieght on the items (max-height
isn't enough). One way of dealing with this neatly in a dynamic manner
is to use the onExit callback to measure and set the height on the
exiting item so it has an explicit height set while exiting
So the first thing was setting a more consistent key property value:
<CSSTransition
key={c.name}
classNames="customer"
timeout={{ enter: 500, exit: 700 }}
>
<CustomerRow
customer={c}
onDelete={() => onDelete(i, c)}
/>
</CSSTransition>
Secondly was to make sure I set a height on the containing div class.
Is this better?
I used keyframes:
.customer-exit {
opacity: 0;
/*transition: opacity 300ms ease, height 400ms ease 300ms;*/
-webkit-animation: slideIn 0.7s ease;forwards;
-moz-animation: slideIn 0.7s ease;
animation: slideIn 0.7s ease;
}
.customer-exit.customer-exit-active {
opacity: 0.01;
height: 0px;
}
#keyframes slideIn {
0% {
opacity:1
}
50% {
opacity:0
}
90% {
transform: translate(0,-100px);
}
100% {
opacity:0
/*transform: translateY(0px);*/
/*opacity:1*/
}
}
https://codepen.io/vladop/pen/PKwmMg
I wanted to share a recent library that solves these types of problems very easily and intuitively, called React Sequencer.
The library is similar to ReactCSSTransition only it gives you full control over the stages of sequences and their durations. I have made an example that solves your problem here:
https://codesandbox.io/s/nrw3261m20
What's nice is that the solution doesn't require any repaint hacks or clunky callbacks - but performs the animation just by giving you state and letting you render the state you like.
The example shows how you could use it to fade an element out, and then collapse it as a second step in the sequence, making for a very smooth looking animation.
What I would do in this case is to separate the animations out into two separate ones.
- Each item would animate themselves in/out
- The "Y" position of each item would be calculated based on its position in the list (e.g. 0, 1, 2, 3, 4 etc), which can be passed in from the list
This way once, say, the 1st item finishes animating itself out, the list would be updated so the items previously in the 2nd, 3rd, and 4th rows would now be told they're in the 1st, 2nd, and 3rd row, and have a new Y position they should be in, and transition to those new Y positions
I would recommend looking at react-motion for transitioning on the Y
I am using Vue 2 and attempting to include CSS animations on elements that are created and destroyed frequently. Below is an example of my code:
export default {
name: 'MyElement',
methods: {
enterStart: function (el) {
console.log('about to enter');
el.classList.add('testing-enter');
},
enter: function (el) {
console.log('entered');
},
leaveStart: function (el) {
console.log('starting to leave!');
},
leave: function (el) {
console.log('leaving!');
},
}
};
.testing-enter {
animation: enter .2s;
}
.testing-leave {
animation: leave .2s;
}
#keyframes enter {
0% {
opacity: 0;
transform: scale(0);
}
100% {
opacity: 1;
transform: scale(1);
}
}
#keyframes leave {
0% {
opacity: 1;
transform: scale(1);
}
100% {
opacity: 0;
transform: scale(0);
}
}
<template>
<div>
<transition
#before-enter="enterStart"
#enter="enter"
#leave="leaveStart"
#leave-active="leave"
appear
>
<div>My element is here!</div>
</transition>
</div>
</template>
First off, none of this works unless I include appear in my <transition ...> element. I know that this makes the transition happen on initial rendering, but I want them to happen any time the element is created or destroyed.
Next, in my console. I can see enterStart and enter both run, but leaveStart and leave never run, even when the elements are destroyed. What am I doing wrong?
The element inside the transition needs a state (show or hide). Also your transition needs a name that must much the transition in the CSS and it should be named with
name="transitionName"
e.g:
new Vue({
el: "#app",
data: function() {
return {
showThisElement: false
}
},
methods: {
toggleShow: function() {
this.showThisElement = !this.showThisElement
}
}
});
.testing-enter-active {
animation: enter .2s;
}
.testing-leave-active {
animation: leave .2s;
}
#keyframes enter {
0% {
opacity: 0;
transform: scale(0);
}
100% {
opacity: 1;
transform: scale(1);
}
}
#keyframes leave {
0% {
opacity: 1;
transform: scale(1);
}
100% {
opacity: 0;
transform: scale(0);
}
}
<div id="app">
<div #click="toggleShow">Show/Hide</div>
<transition
name="testing">
<div v-if="showThisElement">My element is here!</div>
</transition>
</div>
In the codepen, click on 'show/hide' to toggle the transition.
http://codepen.io/anon/pen/WpZPJp
Problem solved!
So I took out the transition from the individual component and created a transition-group instead around the container component that rendered them.
Then, after a bit more reading I realized I wanted to add the mode="out-in" field to my transition-group so that the leaving components fully animate before the new ones are rendered.
I also looked at the HTML when the animations were supposed to be happening to see what classes Vue added. It looks like Vue added v-enter-active, v-enter-to, and v-leave-to. Instead of customizing any names I just stuck with those classes and added my animations to them in the styling.
Hopefully if anybody else wants a similar effect this helps them decrease their stress levels a bit...
I have a page that has an input field and a "next" button. When the user clicks next, I want to shrink the input field and remove the "next" button. I have it mostly working as seen in this Bootply. My CSS animations look like this:
.default {
transition: all 1.0s ease;
}
.exit {
padding-left:5rem;
padding-right:5rem;
}
.remove-button {
animation: animate-remove-button 1.0s;
}
#keyframes animate-remove-button {
from {
transform:scaleX(1.0);
opacity: 1.0;
}
to {
transform:scaleX(0);
opacity: 0;
}
}
The animation runs. However, after the animation has completed, the button reappears. In addition, I basically want the width of the button to shrink all the way to 0 so that the text field grows. But, as you can see in the Bootply, that's not really happening, even though it kind of looks like it is.
What am I missing?
.remove-button {
animation: animate-remove-button 1.0s;
animation-fill-mode: forwards;
}
animation fill mode: forwards tell to keep the last state of animation.
Documentation: https://developer.mozilla.org/en/docs/Web/CSS/animation-fill-mode
I am building a custom wizard form with waypoints. Something interesting is happening and I can't figure it out for the life of me.
My sample CODEPEN is showing 2 pages of the wizard process to show you what I mean.
When you hit the forward action button (search in the first page of the wizard), the waypoints slide from the right and the next page or screen shows. That would repeat on-forward and backwards if I click on the backward action button. That is working.
The problem I see is with the initial horizontal scrollbar. It shows on page load, which it's a problem because the user could just scroll to the next screen by dragging the scrollbar. I thought of giving it an overflow-x but it didn't fix the issue. The interesting thing is, if I click on the search button and the waypoint slides, the scroll bar disappears and gives me the desired effect! What gives?
I built the CODEPEN as close as possible to the real environment so that you guys can catch any conflict with other elements instead of isolating the problem.
Here is the related code just in case:
HTML:
<div id="content" class="content">
<div class="row page">
<!-- First page content here -->
</div>
<div class="row page2">
<!-- Second page content here -->
</div>
</div>
CSS:
.page, .page2 {
position: absolute;
top: 20px;
left: 10px;
width: 100%;
-webkit-transition: -webkit-transform 0.8s;
transition: -webkit-transform 0.8s;
transition: transform 0.8s;
transition: transform 0.8s, -webkit-transform 0.8s
}
.page {
-webkit-transform: translateX(0%);
transform: translateX(0%)
}
.show-page2 .page {
-webkit-transform: translateX(-100%);
transform: translateX(-100%)
}
.page2 {
-webkit-transform: translateX(100%);
transform: translateX(100%)
}
.show-page2 .page2 {
-webkit-transform: translateX(0%);
transform: translateX(0%)
}
JS:
(function () {
var body = $('#content'),
nav = $('.btn-waypoint'),
panels = $('#content');
nav.on('click', function (e) {
e.preventDefault();
var dest = $(this).data('panel-link');
body
.removeClass(function (index, css) {
// remove only classes start with show-
// http://stackoverflow.com/questions/2644299/jquery-removeclass-wildcard
return (css.match(/\bshow-\S+/g) || []).join(' ');
})
.addClass('show-' + dest);
});
}());
The closest fix I've tried to solve this is making page2 display:none on page load to eliminate the scrollbar and then making it visible on button click. That almost did it except a funky look happens between the waypoint sliding effect and the css fade effect. Here is the code for that:
JS
$( document ).ready(function() {
$('.page2').css('display', 'none');
$('[data-panel-link]').on('click', function(){
$('.page2').css('display', 'block');
});
});
Here is the link to my CODEPEN
Thanks in advance!
As it plays out the root of the problem is the hard positioning. The waypoint divs are natively in vertical position which they would obviously not produce a horizontal scrollbar. They are being forced to be side by side by position:absolute and the transform: translateX(-100%) and this creates the horizontal scrollbar. If the mousewheel is disabled via jQuery the scrollbar goes away, but it goes away vertically as well. So instead of fighting that battle, a better alternative is to use a different transition that looks good but doesn't require a side by side animation. A fade will do just nice:
Simply replace the css effects from translateX to the following:
.page, .page2{
position: absolute;
width:100%;
transition: opacity 1s ease-in-out;
-moz-transition: opacity 1s ease-in-out;
-webkit-transition: opacity 1s ease-in-out;
}
.page {
opacity: 1;
}
.show-page2 .page {
opacity: 0;
}
.page2{
opacity: 0;
}
.show-page2 .page2{
opacity: 1;
}