AngularJS directive for JuxtaposeJS, a JavaScript library for making before/after image sliders.
Simplest usage possible! Just add the directive in your html page.
<juxtapose before-image-url="'http://online.wsj.com/media/LIONDOORA.jpg'" after-image-url="'http://online.wsj.com/media/LIONDOOR_2A.jpg'"> </juxtapose>
A little more complete usage, with some options given via hardcoded values.
<juxtapose before-image-url="'http://online.wsj.com/media/LIONDOORA.jpg'" before-image-label="'Maidan square in 2009'" before-image-credit="'WSJ'" after-image-url="'http://online.wsj.com/media/LIONDOOR_2A.jpg'" after-image-label="'Maidan square in 2014'" after-image-credit=""> </juxtapose>
The following options are available on the directive element, and directly affect the two images:
alt
) for the before imagealt
) for the after imageThe following options are available on both the directive element and as config parameters (see juxtaposeConfigProvider), and globally affect the slider itself:
50%
, at the middle of the two images)true
is set (default value)true
is set (default value)true
, the divider will glide to the point where someone clicks; otherwise, it will jump (default is true
)true
, the divider will move vertically instead of horizontally (default is true
)When config options are defined as attributes, they take precedence over values defined via the juxtaposeConfigProvider
Another usage, with some options given via scope values.
<div ng-controller="MyCtrl"> <juxtapose before-image-url="options.beforeImageUrl" before-image-label="options.beforeImageLabel" before-image-credit="options.beforeImageCredit" before-image-alt="options.beforeImageAlt" after-image-url="options.afterImageUrl" after-image-label="options.afterImageLabel" after-image-credit="options.afterImageCredit" after-image-alt="options.afterImageAlt"> </juxtapose> </div>
angular.module('ngJuxtaposeDemo').controller('MyCtrl', ['$scope', function ($scope) { $scope.options = {}; $scope.options.beforeImageUrl = 'http://online.wsj.com/media/LIONDOORA.jpg'; $scope.options.beforeImageLabel = '2009'; $scope.options.beforeImageCredit = 'WSJ'; $scope.options.beforeImageAlt = '2009 Alternate text'; $scope.options.afterImageUrl = 'http://online.wsj.com/media/LIONDOOR_2A.jpg'; $scope.options.afterImageLabel = '2014'; $scope.options.afterImageCredit = 'TKO'; $scope.options.afterImageAlt = '2014 Alternate text'; }]);
<div ng-controller="HolyGrailCtrl"> <juxtapose starting-position="options.startingPosition" show-labels="options.showLabels" show-credits="options.showCredits" animate="options.animate" vertical="options.vertical" before-image-url="options.beforeImageUrl" before-image-label="options.beforeImageLabel" before-image-credit="options.beforeImageCredit" before-image-alt="options.beforeImageAlt" after-image-url="options.afterImageUrl" after-image-label="options.afterImageLabel" after-image-credit="options.afterImageCredit" after-image-alt="options.afterImageAlt"> </juxtapose> <h1>Options</h1> <form name="optionsForm" role="form" class="form-horizontal" novalidate> <fieldset> <div class="form-group"> <strong>Before Image:</strong> </div> <div class="form-group" ng-class="{'has-error': optionsForm.beforeImageUrl.$error.required}"> <input type="text" class="form-control" name="beforeImageUrl" ng-model="options.beforeImageUrl" placeholder="URL (mandatory)" required> </div> <div class="form-group"> <div class="row"> <div class="col-xs-12 col-sm-6 col-md-6"> <input type="text" class="form-control" ng-model="options.beforeImageLabel" placeholder="Label (optional)"> </div> <div class="col-xs-12 col-sm-6 col-md-6"> <input type="text" class="form-control" ng-model="options.beforeImageCredit" placeholder="Credit (optional)"> </div> </div> </div> <div class="form-group"> <input type="text" class="form-control" ng-model="options.beforeImageAlt" placeholder="Alternate Text (optional)" required> </div> </fieldset> <fieldset> <div class="form-group"> <strong>After Image:</strong> </div> <div class="form-group" ng-class="{'has-error': optionsForm.afterImageUrl.$error.required}"> <input type="text" class="form-control" name="afterImageUrl" ng-model="options.afterImageUrl" placeholder="URL (mandatory)" required> </div> <div class="form-group"> <div class="row"> <div class="col-xs-12 col-sm-6 col-md-6"> <input type="text" class="form-control" ng-model="options.afterImageLabel" placeholder="Label (optional)"> </div> <div class="col-xs-12 col-sm-6 col-md-6"> <input type="text" class="form-control" ng-model="options.afterImageCredit" placeholder="Credit (optional)"> </div> </div> </div> <div class="form-group"> <input type="text" class="form-control" ng-model="options.afterImageAlt" placeholder="Alternate Text (optional)" required> </div> </fieldset> <fieldset> <div class="row"> <div class="col-xs-12 col-sm-12 col-md-12 col-lg-12"> <div class="checkbox-inline"> <label> <input type="checkbox" ng-model="options.showLabels"> Show Labels ? </label> </div> <div class="checkbox-inline"> <label> <input type="checkbox" ng-model="options.showCredits"> Show Credits ? </label> </div> <div class="checkbox-inline"> <label> <input type="checkbox" ng-model="options.animate"> Animate Slider ? </label> </div> <div class="checkbox-inline"> <label> <input type="checkbox" ng-model="options.vertical"> Vertical Slider ? </label> </div> </div> </div> </fieldset> </form> </div>
angular.module('ngJuxtaposeDemo').controller('HolyGrailCtrl', ['$scope', function ($scope) { $scope.options = {}; $scope.options.startingPosition = '30%'; $scope.options.showLabels = true; $scope.options.showCredits = true; $scope.options.animate = true; $scope.options.vertical = false; $scope.options.beforeImageUrl = 'https://juxtapose.knightlab.com/static/img/Sochi_11April2005.jpg'; $scope.options.beforeImageLabel = 'April 2005'; $scope.options.beforeImageCredit = ''; $scope.options.beforeImageAlt = '2009 Alternate text'; $scope.options.afterImageUrl = 'https://juxtapose.knightlab.com/static/img/Sochi_22Nov2013.jpg'; $scope.options.afterImageLabel = 'Nov 2013'; $scope.options.afterImageCredit = ''; $scope.options.afterImageAlt = ''; }]);
You can change how JuxtaposeJS appears in the browser by overriding the CSS rules. More information can be found here
<juxtapose before-image-url="'http://online.wsj.com/media/LIONDOORA.jpg'" after-image-url="'http://online.wsj.com/media/LIONDOOR_2A.jpg'"> </juxtapose>
div.juxtapose { width: 100%; font-family: Helvetica, Arial, sans-serif; } div.jx-slider { width: 100%; height: 100%; position: relative; overflow: hidden; cursor: pointer; } div.jx-handle { position: absolute; height: 100%; width: 40px; cursor: col-resize; z-index: 10; margin-left: -20px; } .vertical div.jx-handle { height: 40px; width: 100%; cursor: row-resize; margin-top: -20px; margin-left: 0; } div.jx-control { height: 100%; margin-right: auto; margin-left: auto; width: 3px; background-color: white; } .vertical div.jx-control { height: 3px; width: 100%; background-color: white; position: relative; top: 50%; transform: translateY(-50%); } div.jx-controller { position: absolute; margin: auto; top: 0; bottom: 0; height: 60px; width: 9px; margin-left: -3px; background-color: white; } .vertical div.jx-controller { height: 9px; width: 100px; margin-left: auto; margin-right: auto; top: -3px; position: relative; } div.jx-arrow { position: absolute; margin: auto; top: 0; bottom: 0; width: 0; height: 0; transition: all .2s ease; } .vertical div.jx-arrow { position: absolute; margin: 0 auto; left: 0; right: 0; width: 0; height: 0; transition: all .2s ease; } div.jx-arrow.jx-left { left: 2px; border-style: solid; border-width: 8px 8px 8px 0; border-color: transparent #FFF transparent transparent; } div.jx-arrow.jx-right { right: 2px; border-style: solid; border-width: 8px 0 8px 8px; border-color: transparent transparent transparent #FFF; } .vertical div.jx-arrow.jx-left { left: 0px; top: 2px; border-style: solid; border-width: 0px 8px 8px 8px; border-color: transparent transparent #FFF transparent; } .vertical div.jx-arrow.jx-right { right: 0px; top: initial; bottom: 2px; border-style: solid; border-width: 8px 8px 0 8px; border-color: #FFF transparent transparent transparent; } div.jx-handle:hover div.jx-arrow.jx-left, div.jx-handle:active div.jx-arrow.jx-left { left: -1px; } div.jx-handle:hover div.jx-arrow.jx-right, div.jx-handle:active div.jx-arrow.jx-right { right: -1px; } .vertical div.jx-handle:hover div.jx-arrow.jx-left, .vertical div.jx-handle:active div.jx-arrow.jx-left { left: 0px; top: 0px; } .vertical div.jx-handle:hover div.jx-arrow.jx-right, .vertical div.jx-handle:active div.jx-arrow.jx-right { right: 0px; bottom: 0px; } div.jx-image { position: absolute; height: 100%; display: inline-block; top: 0; overflow: hidden; -webkit-backface-visibility: hidden; -webkit-transform: scale(1); } .vertical div.jx-image { width: 100%; left: 0; top: initial; } div.jx-image img { height: 100%; z-index: 5; position: absolute; max-height: initial; max-width: initial; } .vertical div.jx-image img { height: initial; width: 100%; } div.jx-image.jx-left { left: 0; background-position: left; } div.jx-image.jx-left img { left: 0; } div.jx-image.jx-right { right: 0; background-position: right; } div.jx-image.jx-right img { right: 0; bottom: 0; } .veritcal div.jx-image.jx-left { top: 0; background-position: top; } .veritcal div.jx-image.jx-left img { top: 0; } .vertical div.jx-image.jx-right { bottom: 0; background-position: bottom; } .veritcal div.jx-image.jx-right img { bottom: 0; } div.jx-image div.jx-label { font-size: 1em; padding: .25em .75em; position: relative; display: inline-block; top: 0; background-color: #000; /* IE 8 */ background-color: rgba(0,0,0,.7); color: white; z-index: 10; white-space: nowrap; line-height: 18px; vertical-align: middle; } div.jx-image.jx-left div.jx-label { float: left; left: 0; } div.jx-image.jx-right div.jx-label { float: right; right: 0; } .vertical div.jx-image div.jx-label { display: table; position: absolute; } .vertical div.jx-image.jx-right div.jx-label { left: 0; bottom: 0; top: initial; } div.jx-credit { line-height: 1.1; font-size: 0.75em; } div.jx-credit em { font-weight: bold; font-style: normal; } /* Animation */ div.jx-image.transition { transition: width .5s ease; } div.jx-handle.transition { transition: left .5s ease; } .vertical div.jx-image.transition { transition: height .5s ease; } .vertical div.jx-handle.transition { transition: top .5s ease; } /* Knight Lab Credit */ a.jx-knightlab { background-color: #000; /* IE 8 */ background-color: rgba(0,0,0,.25); bottom: 0; display: table; height: 14px; line-height: 14px; padding: 1px 4px 1px 5px; position: absolute; right: 0; text-decoration: none; z-index: 10; } a.jx-knightlab div.knightlab-logo { display: inline-block; vertical-align: middle; height: 8px; width: 8px; background-color: #c34528; transform: rotate(45deg); -ms-transform: rotate(45deg); -webkit-transform: rotate(45deg); top: -1.25px; position: relative; cursor: pointer; } a.jx-knightlab:hover { background-color: #000; /* IE 8 */ background-color: rgba(0,0,0,.35); } a.jx-knightlab:hover div.knightlab-logo { background-color: #ce4d28; } a.jx-knightlab span.juxtapose-name { display: table-cell; margin: 0; padding: 0; font-family: Helvetica, Arial, sans-serif; font-weight: 300; color: white; font-size: 10px; padding-left: 0.375em; vertical-align: middle; line-height: normal; } /* keyboard accessibility */ div.jx-controller:focus, div.jx-image.jx-left div.jx-label:focus, div.jx-image.jx-right div.jx-label:focus, a.jx-knightlab:focus { background: #eae34a; color: #000; } a.jx-knightlab:focus span.juxtapose-name{ color: #000; border: none; }