octocatstartv

Pen Settings

CSS Base

Vendor Prefixing

Add External CSS

These stylesheets will be added in this order and before the code you write in the CSS editor. You can also add another Pen here, and it will pull the CSS from it. Try typing "font" or "ribbon" below.

Quick-add: + add another resource
via CSS Lint

Add External JavaScript

These scripts will run in this order and before the code in the JavaScript editor. You can also link to another Pen here, and it will run the JavaScript from it. Also try typing the name of any popular library.

Quick-add: + add another resource
via JS Hint

Code Indentation

     

Save Automatically?

If active, Pens will autosave every 30 seconds after being saved once.

Want a Run Button?

If active, the preview will update automatically when you change code.

HTML

            
              <div id="container">
  <div id="viewport">
    <div id="world">
      
      <div id="world-bg"><div>
        <!-- Style this div if you want a global bg
          -- that will rotate with the content. -->  
      </div></div>
      <div id="world-fixed">
        <!-- This static div provides a content area that
          -- does not animate with the bg or content.
          -- It's not necessary, but can be useful if you
          -- want a fixed style on the viewport.
          -- For this demo there is a slight box-shadow
          -- around the edge of the viewport. -->
      </div>
      <div id="world-content">
        <!-- The region will place your views here -->
      </div> <!-- / world-content -->
      
    </div> <!-- / world -->
  </div> <!-- / viewport -->
</div> <!-- / container -->

<!-- This is the view template. Every time you press a 
  -- button to transition Marionette renders a new instance
  -- of this template and transitions to it. -->
<script id="slideTemplate" type="template">

  <div class="container-fluid">
    <div class="row-fluid">
      
      <div class="span6 scroll-y">
        <div class="page-header">
          <h1><%= title %> <br /><small> <%= subtitle %></small></h1>
        </div>
        <p>My <a href="http://codepen.io/somethingkindawierd/pen/tuhFE" target="_blank">previous pen showed how to push/pop MarionetteJS views</a> on and off of a stack. Now we're playing with transitioning to a new view within a region. You can <button class="btn next-slide">slide <i class="icon-arrow-right"></i></button> to a new view, <button class="btn next-rotate">rotate <i class="icon-retweet"></i></button> by a random angle, or <button class="btn next-drop">drop <i class="icon-arrow-down"></i></button> down to the new view.</p>
            <p class="alert">Currently only tested in WebKit</a>
            <p>This differs from the <a href="http://codepen.io/somethingkindawierd/pen/tuhFE" target="_blank">StackView</a> in a few ways:
          <ol>
            <li>It's a custom region, so the new view replaces the old one after the transition is complete.</li>
            <li>The transition relies on matrix transformations.</li>
          </ol>
        </p>
      </div>
      
      <div class="span6 span-center scroll-y">
        <div class="img-container">
          <div class="center-parent">
            <div class="center-child">
              <img src="<%= image %>" />
            </div>
          </div>
        </div>
      </div> 
      
    </div> <!-- / row-fluid -->
  </div> <!-- / container-fluid -->

</script>
            
          
!

CSS

            
              #container {
  position: absolute;
  top:0;
  left:0;
  width: 100%;
  height: 100%;
  transform: translate3d(0,0,0); /* prevent flicker */
}

/* this is the element the user is looking through */
#viewport {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  overflow:hidden;
}

/* the world contains everything
 * - the background that will move with the content
 * - an optional fixed background
 * - all content, including off-screen elements
 */
#world {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
}

/* this is where everything will be rendered into */
#world-content, 
#world-bg {
  position: absolute;
  top: 0%;
  left: 0%;
  width: 100%;
  height: 100%;
  transform-origin: 0 0;
  
  /* attempting to remove animation flicker */
  perspective: 1000;
  backface-visibility: hidden;  
  transform: translateZ(0);
  transform-style: preserve-3d;
}

/* setup moving containers, preparing for animation */
#world-content.animated, 
#world-bg.animated {
  transition: transform 0.8s ease-in-out; 
  animation-duration: 0.8s; // so we can read the value is javascript.
}

/* we think of each content element within the world
 * as a slide. It renders the full size of the container.
 * Initially it will be placed off-scren, and the parent
 * will be transitioned to bring the slide into view.
 */
#world-content > div {
  width: 100%;
  height: 100%;
  position: absolute;
  top: 0;
  left: 0;
  overflow: hidden;
  transform-origin: 0 0;
  
  /* attempting to remove animation flicker */
  perspective: 1000;
  backface-visibility: hidden;  
  transform: translateZ(0);
  transform-style: preserve-3d;
}

/* 
 * Demo Styles 
 *
 * Nothing below here is strictly necessary for 
 * the panning region to work. It just makes things 
 * look a bit prettier :)
 */

html, body {
  background: #eaeaea;
}

#container {
  background: #eee;
}

p {
  line-height: 2em;
}

.page-header {
  border-bottom: 1px solid #bbb;
}

#world-fixed {
  position: fixed;
  top: 0px;
  left: 0px;
  width: 100%;
  height: 100%;
  box-shadow: 0 0 100px 0 rgba(0,0,0,0.5) inset;
}

/*
 * Make the background element HUGE so we can place our
 * new view anywhere and safely transition to it knowing
 * our background will cover the area.
 */
#world-bg > div {
  position: absolute;
  top: -1000%;
  left: -1000%;
  width: 3000%;
  height: 3000%;
  background: #eaeaea url(http://subtlepatterns.subtlepatterns.netdna-cdn.com/patterns/light_noise_diagonal.png);
}

.span-center {
   text-align: center; 
}

.center-parent {
  display: table;
  height: 100%;
  width: 100%;
    
  .center-child {
    display: table-cell;
    vertical-align: middle;
  }
}

#world-content > div {
  line-height: 50%;
}

.scroll-y {
  overflow-y: auto;
}

/*
 * The centered-content on the right side only works 
 * on larger screens so let's limit it to > 768
 */
@media (min-width: 768px) {
  
  #world-content > div {
    
    > .container-fluid {
      
      height: 100%;
      
      > .row-fluid {
      
        height: 100%;
       
        > [class*="span"] {
          position: relative;
          height: 100%;
          
          .img-container {
            position: absolute;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            
            img {
              max-width: 300px;
              box-shadow: 0 8px 16px -4px black;
            }
          }
        }
      }
    }
  }
  
}
            
          
!

JS

            
              /**
 * In this pen we create a custom Morionette Region. Instead of simply replacing
 * the contents with a new view when calling show(), we add the new view
 * off-screen and transition the region to show the new view.
 *
 * When the transition is done the region is reset to it's original location.
 */

// Define the Panning Region class
// -------------------------------

// This region relies on Matrix.js (and Point.js) for working with
// transformation matrices.
// 
// http://dl.dropbox.com/u/46070688/js/geom/point.js
// http://dl.dropbox.com/u/46070688/js/geom/matrix.js

var getPrefixedCssProp = function(baseProp) {
  var str = Modernizr.prefixed(baseProp);
  str = str.replace(/([A-Z])/g, function(str,m1){ return '-' + m1.toLowerCase(); }).replace(/^ms-/,'-ms-');
  return str;
};

var PanningRegion = Marionette.Region.extend({
  
  initialize: function() {
    var transEndEventNames = {
      'WebkitTransition' : 'webkitTransitionEnd',
      'MozTransition'    : 'transitionend',
      'OTransition'      : 'oTransitionEnd',
      'msTransition'     : 'MSTransitionEnd',
      'transition'       : 'transitionend'
    };
    this.transEndEventName = transEndEventNames[ Modernizr.prefixed('transition') ];
    this.transformPropName = getPrefixedCssProp('transform');
    
    console.log(this.transEndEventName, this.transformPropName);
  },

  // Very similar to show(), but uses css transition class between views
  transitionToView: function(newView, type) {

    var self = this;

    // Do we have a view currently?
    var view = this.currentView;
    if (!view || view.isClosed){
      this.show(newView);
      return;
    }

    Marionette.triggerMethod.call(this, "willTransition", view);
    
    this.stopListening(newView, 'render');

    // Wait for the new view to render, then initialize a transition to
    // show the new view while hiding the old.
    this.listenTo(newView, 'render', function() {
      
      // clean up the old listeners, just to ensure we only have 1 active.
      self.$el.off(self.transEndEventName);
      
      // Move the new view to an off-screen position using transformation matrix
      var translation;
      
      // Determine the type of transition and build the css transformation.
      if(type === 'slide') {
        translation = 'translateX(100%)';
      }
      else if(type === 'rotate') {
        translation = 'translateX(100%) translateY(100%) rotate(' + ['20', '40', '60', '80', '90'][_.random(0, 4)] + 'deg)';
      }
      else if(type === 'drop') {
        translation = 'translateY(100%)';
      }
      
      newView.$el.css(self.transformPropName, translation);

      // Add the new view to the dom
      self.$el.append(newView.el);
      
      // Translate the container to show the new element
      var $background = jQuery('#world-bg');

      // Find the transformation matrix of each element.
      var worldContentMatrix = Matrix.initWithElem(self.$el);
      var worldBgMatrix = Matrix.initWithElem($background);
      var newViewMatrix = Matrix.initWithElem(newView.$el);

      // Turn on the css animations to enable the transition. We do this here, 
      // before the tranision instead of after the transition is complete 
      // because it causes less of a visual 'snap' as the pattern moves.
      self.$el.addClass('animated')
      $background.addClass('animated');
      
      // Given than we know the container has an identity matrix we can transition
      // by simply inverting the matrix of the new view and appyling it to the parent.
      self.$el.css(self.transformPropName, newViewMatrix.clone().invert().toMatrixString());
      
      // Let's make sure the background moves to the same place.
      $background.css(self.transformPropName, newViewMatrix.clone().invert().toMatrixString());

      // after transition, clean up by removing the old view, then
      // re-position everything back to a zero-point. There might be a problem
      // relying on the transitionEnd event because there are cases where it
      // does not fire.
      self.$el.on(self.transEndEventName, function () {
        self.$el.off(self.transEndEventName);
        
        // clean up the old view
        self.close();
        self.currentView = newView;

        // clean up new view and place everything back
        // to a sane starting position, ready for next transition.

        self.$el.removeClass('animated')
        $background.removeClass('animated');

        self.$el.css(self.transformPropName, (new Matrix()).toMatrixString());
        newView.$el.css(self.transformPropName, (new Matrix()).toMatrixString());
        $background.css('webkit-transform', (new Matrix()).toMatrixString());

        // do the things show would normally do after showing a new view
        Marionette.triggerMethod.call(newView, "show");
        Marionette.triggerMethod.call(self, "show", newView);
      });

    });

    newView.render();

  } // end transitionToView

});

// Let's find some images we can randomly choose from
var images = [
  'http://www.beebeography.com/Nature/i-rP8JSCv/0/M/DSC_8183-M.jpg',
  'http://www.beebeography.com/Nature/i-tKvpZDG/0/M/DSC_8216_Fotor-M.jpg',
  'http://www.beebeography.com/Nature/i-Pk9nCCz/0/M/2013-11-09-radner-lake-trees-01-M.jpg',
  'http://www.beebeography.com/Journal/i-hwfrBnq/1/M/2008-10-11_IMG_8087-M.jpg',
  'http://www.beebeography.com/Journal/i-N3c4dBs/0/M/2008-09-28_IMG_7877-M.jpg',
  'http://www.beebeography.com/Journal/i-tkW5Bbc/0/M/IMG_6293-M.jpg'
];

// Define the app and a region to show content
// -------------------------------------------

var App = new Marionette.Application();

App.addRegions({
  worldContent: {
    selector: "#world-content",
    regionType: PanningRegion
  }
});

// Create a module to contain some functionality
// ---------------------------------------------

App.module("SampleModule", function(Mod, App, Backbone, Marionette, $, _){
  
  // Define a view model
  // -------------------
  
  var SlideModel = Backbone.Model.extend();
    
  // Define a view to show
  // ---------------------
    
  var SlideView = Marionette.ItemView.extend({
    template: '#slideTemplate',
    events: {
      'click button.next-slide': 'slide',
      'click button.next-rotate': 'rotate',
      'click button.next-drop': 'drop',
    },
    slide: function() {
      this.trigger('next', 'slide');
    },
    rotate: function() {
      this.trigger('next', 'rotate');
    },
    drop: function() {
      this.trigger('next', 'drop');
    }
  });
    
  // Define a controller to run this module
  // --------------------------------------
    
  var Controller = Marionette.Controller.extend({
      
    initialize: function(options) {
      this.region = options.region;
    },
    
    show: function(type) {
      var model = new SlideModel({
        title: 'Panning Region',
        subtitle: 'Powered by MarionetteJS',
        image: images[_.random(0, images.length - 1)]
      });
      
      var view = new SlideView({
        model: model
      });
      
      this.region.transitionToView(view, type);
      
      this.listenTo(view, 'next', function(type) {
        this.show(type);
      });
    }
      
  });

  // Initialize this module when the app starts
  // ------------------------------------------
  
  Mod.addInitializer(function(){
      Mod.controller = new Controller({
          region: App.worldContent
      });
      Mod.controller.show();
  });
});

// Start the app
// -------------

App.start();
            
          
!
999px
Close

Asset uploading is a PRO feature.

As a PRO member, you can drag-and-drop upload files here to use as resources. Images, Libraries, JSON data... anything you want. You can even edit them anytime, like any other code on CodePen.

Go PRO

Loading ..................

Console