Dual Viewer

A draggable UI component which reveals two panes.

IE9+ with polyfill.

To support IE<=11, you will need to add classList polyfill.

Dependencies

Odo Window Events, Odo Helpers, Odo Draggable, Odo Object Fit.

Object.assign should also be available.

Styles

This component has required css - css/odo-dual-viewer.css. The theme (css/odo-dual-viewer-theme.css) should also be included and customized to your design.

Horizontal With Images

By default, the dual viewer is horizontal. When the user releases the scrubber, it will animate to the start, end, or center, depending on the zones option.

Slide right or left to reveal more of one perspective.

percent

Markup

This markup needs to be followed closely and is dependent on the dual viewer's css. You may put anything inside odo-dual-viewer__object. This example is full-bleed, but it doesn’t have to be. See the video demo below for one that isn’t.

<div id="regular" class="odo-dual-viewer">
  <div class="odo-dual-viewer__wrap odo-dual-viewer__overlay">
    <div class="odo-dual-viewer__inner">
      <div class="odo-dual-viewer__object">
        <img class="odo-dual-viewer__media" src="https://placehold.it/320x400/1abc9c/ffffff&text=320w">
      </div>
    </div>
  </div>

  <div class="odo-dual-viewer__wrap odo-dual-viewer__underlay">
    <div class="odo-dual-viewer__inner">
      <div class="odo-dual-viewer__object">
        <img class="odo-dual-viewer__media" src="https://placehold.it/320x400/3498db/ffffff&text=320w">
      </div>
    </div>
  </div>

  <div class="odo-dual-viewer__scrubber">
    <div class="odo-dual-viewer__scrubber-handle grabbable"></div>
  </div>

  <div class="container">
    <div class="odo-dual-viewer__scrubber-bounds">
    </div>
  </div>
</div>

Setup

var dualViewer = new OdoDualViewer(document.getElementById('regular'), {});

Scrubber Boundaries

The odo-dual-viewer__scrubber-bounds element determines the limits of the draggable (scrubber). In this example, it is wrapped in a container element, but that only applies to this page. The dual viewer will calculate the limits based on the distance from the left edge of odo-dual-viewer to the left edge of odo-dual-viewer__scrubber-bounds. The right edge is based on the width of the scrubber bounds element.

Animating the dual viewer

You can programmatically animate the position of the scrubber in the dual viewer with the animateTo method. The only parameter is a value between 0 and 1, which represents the percentage to animate to. This value will be clamped to the scrubber’s limits.

document.getElementById('animate-to').addEventListener('click', function () {
  var value = parseInt(document.getElementById('animate-to-value').value, 10);
  dualViewer.animateTo(value / 100);
});

Vertical With Images

Slide right or left to reveal more of one perspective.

Markup

The markup for this vertical demo is identical to the horizontal demo above, except the id attribute is different.

Setup

var vertical = new OdoDualViewer(document.getElementById('vert'), {
  isVertical: true
});

Horizontal With Videos

The dual viewer can hold any type of content. This one holds some html5 videos from gfycat. The first is Moon and cloudlapse and the second is Pouring Beauty Iceland.

Slide right or left to reveal more of one perspective.

Markup

The markup is nearly identical. <img>s have been replaced by <video>s and the scrubber bounds element now has inline styles defining its width.

<div id="dual-viewer-videos" class="odo-dual-viewer">
  <div class="odo-dual-viewer__wrap odo-dual-viewer__overlay">
    <div class="odo-dual-viewer__inner">
      <div class="odo-dual-viewer__object">
        <video class="odo-dual-viewer__media" autoplay loop>
          <source src="https://zippy.gfycat.com/BestReflectingJunco.webm" type="video/webm">
          <source src="https://zippy.gfycat.com/BestReflectingJunco.mp4" type="video/mp4">
        </video>
      </div>
    </div>
  </div>

  <div class="odo-dual-viewer__wrap odo-dual-viewer__underlay">
    <div class="odo-dual-viewer__inner">
      <div class="odo-dual-viewer__object">
        <video class="odo-dual-viewer__media" autoplay loop>
          <source src="https://zippy.gfycat.com/EsteemedJovialHoneybadger.webm" type="video/webm">
          <source src="https://zippy.gfycat.com/EsteemedJovialHoneybadger.mp4" type="video/mp4">
        </video>
      </div>
    </div>
  </div>

  <div class="odo-dual-viewer__scrubber">
    <div class="odo-dual-viewer__scrubber-handle grabbable"></div>
  </div>

  <!-- Not part of odo-dual-viewer.css - absolutely positioned -->
  <p class="dual-viewer__help">Slide right or left to reveal more of one perspective.</p>

  <div class="odo-dual-viewer__scrubber-bounds" style="width:80%;margin:auto;"></div>
</div>

Setup

var vid = new OdoDualViewer(document.getElementById('dual-viewer-videos'), {
  animationDuration: 400,
  zones: [0.3, 0.4, 0.6, 0.7]
});

Zones

When the user releases the scrubber, the dual viewer uses the position of scrubber as a percentage as a zone. The default zones option is [0.33, 0.33, 0.66, 0.66]. The first value in the array represents the lower zone. If the scrubber position (as a percent) is less than that value, the dual viewer will animate to 0 percent (within the boundaries). The second and third values create a zone for the middle. When the scrubber is released in the middle zone, it animates to the center. The last value is the upper zone. Any value above it will animate the scrubber to 1 percent (within the boundaries).

L = lower zone
M = middle zone
U = upper zone

+-------------------------------------+
|          |   |       |   |          |
|          |   |       |   |          |
|    L     |   |   M   |   |     U    |
|          |   |       |   |          |
|          |   |       |   |          |
+-------------------------------------+
        |          |         |
       0.25       0.5       0.75

If the scrubber is released between zones, nothing will happen.

Zones work the same way for vertical dual viewers with the lower zone on top and the upper zone at the bottom.

Like the example above, zones can overlap. The lower and upper zones take precedence over the middle.

Events

The dual viewer emits a CAME_TO_REST event when the handle stops animating.

vid.on(OdoDualViewer.EventType.CAME_TO_REST, function(event) {
  switch (event.position) {
    case OdoDualViewer.Position.START:
      console.log('Handle came to rest at the start');
      break;
    case OdoDualViewer.Position.END:
      console.log('Handle came to rest at the end');
      break;
    case OdoDualViewer.Position.CENTER:
      console.log('Handle came to rest in the center');
      break;
  }
});

No Zones

This example does not use zones to animate to a resting position.

Setup

var noZones = new OdoDualViewer(document.getElementById('no-zones'), {
  hasZones: false,
  startPosition: 0.1
});

Starting Position

The starting position of the scrubber and the amount revealed when the module is initialized can be changed. By default, it's 50%, but using the startPosition option and a value between zero and one, you may change this behavior.