survey-library icon indicating copy to clipboard operation
survey-library copied to clipboard

Animated page transitions

Open SamMousa opened this issue 4 years ago • 11 comments

Are you requesting a feature, reporting a bug or asking a question?

Feature

I'm working on animating page transitions but am running into some issues that make the solution quite hacky. test

What is the current behavior?

The event to detect a page change even is onCurrentPageChanging. After that returns the element is immediately removed from the DOM, meaning any animations won't show.

What I do now when the event triggers is:

  • Add a CSS class to trigger the animation.
  • Prevent the page change (options.allowChange)
  • Call survey.nextPage() after a delay
  • Allow the page to change if the container already has the css class.

For the entrance transition, I store the direction we're moving in (important to make sure a slide-in occurs from the correct window side) and use that to add a CSS class in the onAfterRenderPage event.

What is the expected behavior?

Ideally the library would support animations a bit better by allowing us to control when elements get removed from the DOM.

Create a callback that can be used to hide the page container for example like this:

survey.hidePage = (htmlElement, cb) => {
// This code should hide the page, when it is done it should call `cb()` to notify SurveyJS that it may remove the element from the DOM.

// No animation:
cb();

// CSS Animation:
htmlElement.classList.add('fade-out-cool');
setTimeout(cb, 1000);

};

Fade-ins are inherently easier because we already have an event for elements that are added to the DOM.

Added benefit of my sample above is that it allows for some asynchronous behavior that can make the library appear smoother.

  1. Before removing the page we start the CSS animation.
  2. While the animation runs library code can already do the calculations required and therefore immediately load the next page when cb() is called.

While my example uses callbacks, ofcourse anno ~ 2021 it probably makes more sense to use promises. For example, hidePage could return a promise and when it resolves you remove the element:

survey.hidePage = (element) => {
    element.classList.add('fade-out-cool');
    return new Promise((resolve, reject) => {
        setTimeout(resolve, 1000);
    });
};

// Imaginary code:
... 
    async nextPageInternal() => {
        if (!currentPage.validate()) {
            return currentPage.highlightErrors();
        }
        // This is the existing event
        trigger(beforeChanging);
        if (!changeAllowed) { 
            // Exit early
            return;
        }
        let element = getDOMElementForPage(currentPage);
        let hidePage = survey.hidePage(element);
        let nextPage = calculateNextPage();
        let renderedElement = renderPage(nextPage);

        await hidePage;
        // Try to do heavy lifting before awaiting the promise
        activateAndInsertIntoDOM(nextPage, renderedElement);


    }

SamMousa avatar Dec 23 '20 16:12 SamMousa

Would you be open to a PR for this?

SamMousa avatar Dec 30 '20 14:12 SamMousa

Bump?

SamMousa avatar Apr 13 '21 09:04 SamMousa

Hi there @SamMousa ,

Could you please share, how did you manage to do with your "hacky" way? :) I'm having some problems with my implementation.

Thx. ✌

bakdakonusuruz avatar May 25 '21 21:05 bakdakonusuruz

To summarize my hacky way was:

  1. On first call to onPageChanging start animation, return false so that page doesn't actually change.
  2. After animation manually call nextPage()
  3. On second call return true

I used Animate.css for the animations themselves.

SamMousa avatar May 26 '21 07:05 SamMousa

Thx for your comment @SamMousa. I'm doing the same thing, even with the same library ( good old animate.css 🙂 ) But nextPage() triggers couple of times. I'm using React, maybe that is effecting. That means I'm missing something. Okay, I'll look into it, thx. 👍

bakdakonusuruz avatar May 26 '21 08:05 bakdakonusuruz

Hi again @SamMousa, Are you having problems with "On answering all questions, go to the next page automatically" option as well? Because if I enable this option and click next page button after filling the question, it's skiping a page/question. Event fires 2 times.

bakdakonusuruz avatar May 26 '21 15:05 bakdakonusuruz

I'm not using this in production since I found it too hacky. I remember something like this happened but am not sure how I fixed it.

SamMousa avatar May 26 '21 15:05 SamMousa

Okay, at least you remember that you solved it somehow. So there is still hope. 🙂

bakdakonusuruz avatar May 26 '21 15:05 bakdakonusuruz

@SamMousa hello,

I don't have much experience with CSS animations. For me, it is hard to understand what's going wrong here. Our example with the velocity-animate looks ok. And it also works with React. Here the example: https://codesandbox.io/s/survey-libraryissues2588-s434l?file=/src/SurveyComponent.jsx:343-359

If you created an example with animate.css it will probably help us to understand the problem. Thanks.

dmitry-kurmanov avatar May 27 '21 12:05 dmitry-kurmanov

I don't have an example lying around at the moment and currently don't have time to spend on it.

SamMousa avatar May 28 '21 11:05 SamMousa

@bakdakonusuruz I managed to prevent the skipping with checking for originalTarget.className == '{className}'

but as I'm using onAfterRenderQuestionInput, I have no luck with the css change

Dangandy avatar Jul 12 '21 18:07 Dangandy