Is it possible to show both the new and old pages

2019-02-20 05:37发布

问题:

I'm trying to build an effect like this for smoothstate: http://tympanus.net/Development/PageTransitions/, specifically the "room" transitions.

I'm getting stuck on trying to display both pages at once - I want the new content to push the old off the screen.

lots of code follows...it all works, but it waits until after the old content is offscreen to start adding new content

$(function(){
  'use strict';
  var options = {
    prefetch: true,
    cacheLength: 10,
    onStart: {
      duration: 500, // Duration of our animation
      render: function ($container) {
        // scroll up
        $("html, body").animate({ scrollTop: "0px" });
        var element = $('.row', $container);

        // do animations
        $(element).animate({opacity : 0}, {
            duration: 500,
            easing: "linear",
            step: function(number, tween) {
                number = 1 - number;
                var element = document.getElementsByClassName('row')[0];
                element.style.transform = "translateX(-" + 45*number + "%) rotateY(" + 90*number + "deg)";
            }
        });
      }
    },
    onReady: {
      duration: 500,
      render: function ($container, $newContent) {

        // Inject the new content
        $container.html($newContent);
        $container.css({overflow : 'hidden'});
        // do animations
        var element = document.getElementById($container[0].id).getElementsByClassName('row')[0];
        element.style.opacity = 0;
        $(element).animate({opacity : 1}, {
            duration: 500,
            step: function(number, tween) {
                number = 1 - number;
                var element = document.getElementsByClassName('row')[0];
                element.style.transform = "translateX(" + 45*number + "%) rotateY(-" + 90*number + "deg)";
            }
        }); 

      }
    }
  },
  smoothState = $('#main').smoothState(options).data('smoothState');
});

I would have though that changing the onStart duration to be shorter than the animation duration would work, but it just cuts the animation short, leaving a blank screen.

I'm aware that $container is used for both, but I believe I can fix that with $container.clone(); to hold the old content while it moves off the page.

My question: is there a way to access $newContent other than waiting for onStart to complete?

Note: the same behavior occurs with CSS animations - they terminate at the end of onStart.

回答1:

Yes. The trick is to use setTimeout(,0) to run the animation. I ended up moving the animations to a CSS class for simplicity. This may be laggy on long pages due to content duplication (facebook, youtube, etc.)

It immediately returns from the onStart handler, but runs the animation through to the end. It calls onReady when ready and starts the entry animation.

[...]
onStart: {
  duration: 0,
  render: function ($container) {
    $('#tempWrapper').remove(); //if we have the temp wrapper, kill it now.
    $("html, body").animate({ scrollTop: "0px" });

    //make a duplicate container for animation...
    var $newContainer = $container.clone();
    $newContainer.attr("id", "tempWrapper");
    $newContainer.css({position:'absolute', top:$container.offset().top, width:$container.css("width")});
    $container.css({height:$container.css("height")});
    $container.empty(); //empty the old content so that it takes up 0 space
    $container.before($newContainer); // and immediately add the duplicate back on
    $('.row').removeClass('entering'); // just in case we have the class still
    var element = $('.row', $newContainer);
    setTimeout(callAnimation(element, true), 0); //start the animation
  }
},
onReady: {
  duration: 0,
  render: function ($container, $newContent) {
    // Inject the new content

    $container.html($newContent);

    // do animations
    var element = document.getElementById($container[0].id).getElementsByClassName('row')[0];

    callAnimation(element);
  }
}
[...]

function callAnimation(element, exiting) {
    if (!exiting) {
        $(element).addClass("entering");
    } else {
        $(element).addClass('exiting');
    }
}


回答2:

Hope you still need it. That's how I implemented this:

$(function () {
    //'use strict';
    var $page = $('.m-scene'),
      options = {
          debug: true,
          onStart: {
              duration: 0,
              render: function ($container) {

                  $('.m-page.temp').remove(); //make sure we don't have more than two `pages` at a time
                  $('#move').removeClass('slideup'); //remove old animation; #move is the wrapper for original and injected content
                  $container.find('.m-page').addClass('temp'); //mark original content for removal
              }
          },
          onReady: {
              duration: 50, //prevents flickering of content 
              render: function ($container, $newContent) {
                  $('#move').append($newContent.find('.m-page')); //select only stuff you actually need injected
              }
          },
          onAfter: function ($container, $newContent) {
                var target = $('#move');
                animate(target); //it's animation time!
          }
      },
      smoothState = $page.smoothState(options).data('smoothState');
});

function animate(target) {
    target.addClass('slideup'); //add animation class
}