Add ability to customize styles. Rename filtered => visible and concealed => hidden.

Prefix shuffle item state classes.
Change ClassName => Classes because it's shorter.
pull/111/head
Glen Cheney 8 years ago
parent 150de2024f
commit 99ed4e37fe

@ -0,0 +1,50 @@
<h2>Customizing Styles<a href="#custom-styles"></a></h2>
<p>You can customize the default styles which are applied to Shuffle items upon initialization, before layout, after layout, before hiding, and after hidden.</p>
<p>Here are the defaults:</p>
<div class="code-block">
<pre rel="JavaScript"><code class="language-javascript">ShuffleItem.Css = {
INITIAL: {
position: 'absolute',
top: 0,
left: 0,
visibility: 'visible',
'will-change': 'transform',
},
VISIBLE: {
before: {
opacity: 1,
visibility: 'visible',
},
after: {},
},
HIDDEN: {
before: {
opacity: 0,
},
after: {
visibility: 'hidden',
},
},
};
ShuffleItem.Scale = {
VISIBLE: 1,
HIDDEN: 0.001,
};</code></pre>
</div>
<p>If you wanted to add a 50% red background to every item when they initialize, you could do this:</p>
<div class="code-block">
<pre rel="JavaScript"><code class="language-javascript">Shuffle.ShuffleItem.Css.INITIAL.backgroundColor = 'rgba(255, 0, 0, 0.5)';</code></pre>
</div>
<p>To set the text color to <code>teal</code> after the item has finished moving:</p>
<div class="code-block">
<pre rel="JavaScript"><code class="language-javascript">Shuffle.ShuffleItem.Css.VISIBLE.after.color = 'teal';</code></pre>
</div>
<p>You can also customize the scaling effect with visible or hidden items, <strong>however</strong>, the <code>VISIBLE</code> and <code>HIDDEN</code> values <em>cannot</em> be the exact same.</p>
<div class="code-block">
<pre rel="JavaScript"><code class="language-javascript">Shuffle.ShuffleItem.Scale.HIDDEN = 0.5;</code></pre>
</div>

@ -1,4 +1,4 @@
<h2>Public Methods<a href="#adding-removing"></a></h2> <h2>Public Methods<a href="#public-methods"></a></h2>
<p>A list of the methods available to you and what they do.</p> <p>A list of the methods available to you and what they do.</p>
<ul> <ul>

184
dist/shuffle.js vendored

@ -179,7 +179,7 @@ return /******/ (function(modules) { // webpackBootstrap
} }
// Add class and invalidate styles // Add class and invalidate styles
this.element.classList.add(Shuffle.ClassName.BASE); this.element.classList.add(Shuffle.Classes.BASE);
// Set initial css for each item // Set initial css for each item
this._initItems(); this._initItems();
@ -265,7 +265,7 @@ return /******/ (function(modules) { // webpackBootstrap
this.element.style.position = 'relative'; this.element.style.position = 'relative';
} }
// Overflow has to be hidden // Overflow has to be hidden.
if (styles.overflow !== 'hidden') { if (styles.overflow !== 'hidden') {
this.element.style.overflow = 'hidden'; this.element.style.overflow = 'hidden';
} }
@ -277,7 +277,7 @@ return /******/ (function(modules) { // webpackBootstrap
* category will be used to filter the items. * category will be used to filter the items.
* @param {Array} [collection] Optionally filter a collection. Defaults to * @param {Array} [collection] Optionally filter a collection. Defaults to
* all the items. * all the items.
* @return {!{filtered: Array, concealed: Array}} * @return {!{visible: Array, hidden: Array}}
* @private * @private
*/ */
@ -289,7 +289,7 @@ return /******/ (function(modules) { // webpackBootstrap
var set = this._getFilteredSets(category, collection); var set = this._getFilteredSets(category, collection);
// Individually add/remove concealed/filtered classes // Individually add/remove hidden/visible classes
this._toggleFilterClasses(set); this._toggleFilterClasses(set);
// Save the last filter in case elements are appended. // Save the last filter in case elements are appended.
@ -305,10 +305,10 @@ return /******/ (function(modules) { // webpackBootstrap
} }
/** /**
* Returns an object containing the filtered and concealed elements. * Returns an object containing the visible and hidden elements.
* @param {string|Function} category Category or function to filter by. * @param {string|Function} category Category or function to filter by.
* @param {Array.<Element>} items A collection of items to filter. * @param {Array.<Element>} items A collection of items to filter.
* @return {!{filtered: Array, concealed: Array}} * @return {!{visible: Array, hidden: Array}}
* @private * @private
*/ */
@ -317,28 +317,28 @@ return /******/ (function(modules) { // webpackBootstrap
value: function _getFilteredSets(category, items) { value: function _getFilteredSets(category, items) {
var _this = this; var _this = this;
var filtered = []; var visible = [];
var concealed = []; var hidden = [];
// category === 'all', add filtered class to everything // category === 'all', add visible class to everything
if (category === Shuffle.ALL_ITEMS) { if (category === Shuffle.ALL_ITEMS) {
filtered = items; visible = items;
// Loop through each item and use provided function to determine // Loop through each item and use provided function to determine
// whether to hide it or not. // whether to hide it or not.
} else { } else {
items.forEach(function (item) { items.forEach(function (item) {
if (_this._doesPassFilter(category, item.element)) { if (_this._doesPassFilter(category, item.element)) {
filtered.push(item); visible.push(item);
} else { } else {
concealed.push(item); hidden.push(item);
} }
}); });
} }
return { return {
filtered: filtered, visible: visible,
concealed: concealed hidden: hidden
}; };
} }
@ -372,23 +372,23 @@ return /******/ (function(modules) { // webpackBootstrap
} }
/** /**
* Toggles the filtered and concealed class names. * Toggles the visible and hidden class names.
* @param {{filtered, concealed}} Object with filtered and concealed arrays. * @param {{visible, hidden}} Object with visible and hidden arrays.
* @private * @private
*/ */
}, { }, {
key: '_toggleFilterClasses', key: '_toggleFilterClasses',
value: function _toggleFilterClasses(_ref) { value: function _toggleFilterClasses(_ref) {
var filtered = _ref.filtered; var visible = _ref.visible;
var concealed = _ref.concealed; var hidden = _ref.hidden;
filtered.forEach(function (item) { visible.forEach(function (item) {
item.reveal(); item.show();
}); });
concealed.forEach(function (item) { hidden.forEach(function (item) {
item.conceal(); item.hide();
}); });
} }
@ -424,7 +424,7 @@ return /******/ (function(modules) { // webpackBootstrap
} }
/** /**
* Updates the filtered item count. * Updates the visible item count.
* @private * @private
*/ */
@ -681,20 +681,27 @@ return /******/ (function(modules) { // webpackBootstrap
var itemSize = Shuffle.getSize(item.element, true); var itemSize = Shuffle.getSize(item.element, true);
var pos = _this3._getItemPosition(itemSize); var pos = _this3._getItemPosition(itemSize);
function callback() {
item.applyCss(_shuffleItem2.default.Css.VISIBLE.after);
}
// If the item will not change its position, do not add it to the render // If the item will not change its position, do not add it to the render
// queue. Transitions don't fire when setting a property to the same value. // queue. Transitions don't fire when setting a property to the same value.
if (_point2.default.equals(currPos, pos) && currScale === _shuffleItem2.default.Scale.VISIBLE) { if (_point2.default.equals(currPos, pos) && currScale === _shuffleItem2.default.Scale.VISIBLE) {
callback();
return; return;
} }
item.point = pos; item.point = pos;
item.scale = _shuffleItem2.default.Scale.VISIBLE; item.scale = _shuffleItem2.default.Scale.VISIBLE;
var styles = _shuffleItem2.default.Css.VISIBLE.before;
styles.transitionDelay = _this3._getStaggerAmount(count);
_this3._queue.push({ _this3._queue.push({
item: item, item: item,
opacity: 1, styles: styles,
visibility: 'visible', callback: callback
transitionDelay: _this3._getStaggerAmount(count)
}); });
count++; count++;
@ -845,22 +852,30 @@ return /******/ (function(modules) { // webpackBootstrap
var count = 0; var count = 0;
collection.forEach(function (item) { collection.forEach(function (item) {
function callback() {
item.applyCss(_shuffleItem2.default.Css.HIDDEN.after);
}
// Continuing would add a transitionend event listener to the element, but // Continuing would add a transitionend event listener to the element, but
// that listener would not execute because the transform and opacity would // that listener would not execute because the transform and opacity would
// stay the same. // stay the same.
if (item.scale === _shuffleItem2.default.Scale.FILTERED) { // The callback is executed here because it is not guaranteed to be called
// after the transitionend event because the transitionend could be
// canceled if another animation starts.
if (item.scale === _shuffleItem2.default.Scale.HIDDEN) {
callback();
return; return;
} }
item.scale = _shuffleItem2.default.Scale.FILTERED; item.scale = _shuffleItem2.default.Scale.HIDDEN;
var styles = _shuffleItem2.default.Css.HIDDEN.before;
styles.transitionDelay = _this4._getStaggerAmount(count);
_this4._queue.push({ _this4._queue.push({
item: item, item: item,
opacity: 0, styles: styles,
transitionDelay: _this4._getStaggerAmount(count), callback: callback
callback: function callback() {
item.element.style.visibility = 'hidden';
}
}); });
count++; count++;
@ -900,13 +915,13 @@ return /******/ (function(modules) { // webpackBootstrap
}, { }, {
key: '_getStylesForTransition', key: '_getStylesForTransition',
value: function _getStylesForTransition(obj) { value: function _getStylesForTransition(_ref2) {
var item = obj.item; var item = _ref2.item;
var styles = { var styles = _ref2.styles;
opacity: obj.opacity,
visibility: obj.visibility, if (!styles.transitionDelay) {
transitionDelay: (obj.transitionDelay || 0) + 'ms' styles.transitionDelay = '0ms';
}; }
var x = item.point.x; var x = item.point.x;
var y = item.point.y; var y = item.point.y;
@ -929,11 +944,7 @@ return /******/ (function(modules) { // webpackBootstrap
return new Promise(function (resolve) { return new Promise(function (resolve) {
var id = (0, _onTransitionEnd.onTransitionEnd)(element, function (evt) { var id = (0, _onTransitionEnd.onTransitionEnd)(element, function (evt) {
evt.currentTarget.style.transitionDelay = ''; evt.currentTarget.style.transitionDelay = '';
itemCallback();
if (itemCallback) {
itemCallback();
}
resolve(); resolve();
}); });
_this5._transitions.push(id); _this5._transitions.push(id);
@ -977,7 +988,7 @@ return /******/ (function(modules) { // webpackBootstrap
if (transitions.length > 0 && this.options.speed > 0) { if (transitions.length > 0 && this.options.speed > 0) {
this._startTransitions(transitions); this._startTransitions(transitions);
// A call to layout happened, but none of the newly filtered items will // A call to layout happened, but none of the newly visible items will
// change position. Asynchronously fire the callback here. // change position. Asynchronously fire the callback here.
} else { } else {
setTimeout(this._dispatchLayout.bind(this), 0); setTimeout(this._dispatchLayout.bind(this), 0);
@ -1038,10 +1049,7 @@ return /******/ (function(modules) { // webpackBootstrap
Shuffle._skipTransitions(elements, function () { Shuffle._skipTransitions(elements, function () {
objects.forEach(function (obj) { objects.forEach(function (obj) {
obj.item.applyCss(_this8._getStylesForTransition(obj)); obj.item.applyCss(_this8._getStylesForTransition(obj));
obj.callback();
if (obj.callback) {
obj.callback();
}
}); });
}); });
} }
@ -1063,7 +1071,7 @@ return /******/ (function(modules) { // webpackBootstrap
* The magic. This is what makes the plugin 'shuffle' * The magic. This is what makes the plugin 'shuffle'
* @param {string|Function|Array.<string>} [category] Category to filter by. * @param {string|Function|Array.<string>} [category] Category to filter by.
* Can be a function, string, or array of strings. * Can be a function, string, or array of strings.
* @param {Object} [sortObj] A sort object which can sort the filtered set * @param {Object} [sortObj] A sort object which can sort the visible set
*/ */
}, { }, {
@ -1079,18 +1087,18 @@ return /******/ (function(modules) { // webpackBootstrap
this._filter(category); this._filter(category);
// Shrink each concealed item // Shrink each hidden item
this._shrink(); this._shrink();
// How many filtered elements? // How many visible elements?
this._updateItemCount(); this._updateItemCount();
// Update transforms on .filtered elements so they will animate to their new positions // Update transforms on visible elements so they will animate to their new positions.
this.sort(sortObj); this.sort(sortObj);
} }
/** /**
* Gets the .filtered elements, sorts them, and passes them to layout. * Gets the visible elements, sorts them, and passes them to layout.
* @param {Object} opts the options object for the sorted plugin * @param {Object} opts the options object for the sorted plugin
*/ */
@ -1244,8 +1252,8 @@ return /******/ (function(modules) { // webpackBootstrap
// Hide collection first. // Hide collection first.
this._toggleFilterClasses({ this._toggleFilterClasses({
filtered: [], visible: [],
concealed: oldItems hidden: oldItems
}); });
this._shrink(oldItems); this._shrink(oldItems);
@ -1411,7 +1419,7 @@ return /******/ (function(modules) { // webpackBootstrap
}; };
/** @enum {string} */ /** @enum {string} */
Shuffle.ClassName = _classes2.default; Shuffle.Classes = _classes2.default;
// Overrideable options // Overrideable options
Shuffle.options = { Shuffle.options = {
@ -1756,33 +1764,36 @@ return /******/ (function(modules) { // webpackBootstrap
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
var id = 0;
var ShuffleItem = function () { var ShuffleItem = function () {
function ShuffleItem(element) { function ShuffleItem(element) {
_classCallCheck(this, ShuffleItem); _classCallCheck(this, ShuffleItem);
this.id = id++;
this.element = element; this.element = element;
this.isVisible = true; this.isVisible = true;
} }
_createClass(ShuffleItem, [{ _createClass(ShuffleItem, [{
key: 'reveal', key: 'show',
value: function reveal() { value: function show() {
this.isVisible = true; this.isVisible = true;
this.element.classList.remove(_classes2.default.CONCEALED); this.element.classList.remove(_classes2.default.HIDDEN);
this.element.classList.add(_classes2.default.FILTERED); this.element.classList.add(_classes2.default.VISIBLE);
} }
}, { }, {
key: 'conceal', key: 'hide',
value: function conceal() { value: function hide() {
this.isVisible = false; this.isVisible = false;
this.element.classList.remove(_classes2.default.FILTERED); this.element.classList.remove(_classes2.default.VISIBLE);
this.element.classList.add(_classes2.default.CONCEALED); this.element.classList.add(_classes2.default.HIDDEN);
} }
}, { }, {
key: 'init', key: 'init',
value: function init() { value: function init() {
this.addClasses([_classes2.default.SHUFFLE_ITEM, _classes2.default.FILTERED]); this.addClasses([_classes2.default.SHUFFLE_ITEM, _classes2.default.VISIBLE]);
this.applyCss(ShuffleItem.css); this.applyCss(ShuffleItem.Css.INITIAL);
this.scale = ShuffleItem.Scale.VISIBLE; this.scale = ShuffleItem.Scale.VISIBLE;
this.point = new _point2.default(); this.point = new _point2.default();
} }
@ -1814,7 +1825,7 @@ return /******/ (function(modules) { // webpackBootstrap
}, { }, {
key: 'dispose', key: 'dispose',
value: function dispose() { value: function dispose() {
this.removeClasses([_classes2.default.CONCEALED, _classes2.default.FILTERED, _classes2.default.SHUFFLE_ITEM]); this.removeClasses([_classes2.default.HIDDEN, _classes2.default.VISIBLE, _classes2.default.SHUFFLE_ITEM]);
this.element.removeAttribute('style'); this.element.removeAttribute('style');
this.element = null; this.element = null;
@ -1824,17 +1835,34 @@ return /******/ (function(modules) { // webpackBootstrap
return ShuffleItem; return ShuffleItem;
}(); }();
ShuffleItem.css = { ShuffleItem.Css = {
position: 'absolute', INITIAL: {
top: 0, position: 'absolute',
left: 0, top: 0,
visibility: 'visible', left: 0,
'will-change': 'transform' visibility: 'visible',
'will-change': 'transform'
},
VISIBLE: {
before: {
opacity: 1,
visibility: 'visible'
},
after: {}
},
HIDDEN: {
before: {
opacity: 0
},
after: {
visibility: 'hidden'
}
}
}; };
ShuffleItem.Scale = { ShuffleItem.Scale = {
VISIBLE: 1, VISIBLE: 1,
FILTERED: 0.001 HIDDEN: 0.001
}; };
exports.default = ShuffleItem; exports.default = ShuffleItem;
@ -1848,8 +1876,8 @@ return /******/ (function(modules) { // webpackBootstrap
module.exports = { module.exports = {
BASE: 'shuffle', BASE: 'shuffle',
SHUFFLE_ITEM: 'shuffle-item', SHUFFLE_ITEM: 'shuffle-item',
FILTERED: 'filtered', VISIBLE: 'shuffle-item--visible',
CONCEALED: 'concealed' HIDDEN: 'shuffle-item--hidden'
}; };
/***/ }, /***/ },

File diff suppressed because one or more lines are too long

@ -171,6 +171,16 @@ prism: true
</div> </div>
</section> </section>
<section id="custom-styles">
<div class="container">
<div class="row">
<div class="col-12@sm">
{% include custom-styles.html %}
</div>
</div>
</div>
</section>
<section id="extra-features"> <section id="extra-features">
<div class="container"> <div class="container">
<div class="row"> <div class="row">

@ -1,6 +1,6 @@
module.exports = { module.exports = {
BASE: 'shuffle', BASE: 'shuffle',
SHUFFLE_ITEM: 'shuffle-item', SHUFFLE_ITEM: 'shuffle-item',
FILTERED: 'filtered', VISIBLE: 'shuffle-item--visible',
CONCEALED: 'concealed', HIDDEN: 'shuffle-item--hidden',
}; };

@ -1,27 +1,30 @@
import Point from './point'; import Point from './point';
import Classes from './classes'; import Classes from './classes';
let id = 0;
class ShuffleItem { class ShuffleItem {
constructor(element) { constructor(element) {
this.id = id++;
this.element = element; this.element = element;
this.isVisible = true; this.isVisible = true;
} }
reveal() { show() {
this.isVisible = true; this.isVisible = true;
this.element.classList.remove(Classes.CONCEALED); this.element.classList.remove(Classes.HIDDEN);
this.element.classList.add(Classes.FILTERED); this.element.classList.add(Classes.VISIBLE);
} }
conceal() { hide() {
this.isVisible = false; this.isVisible = false;
this.element.classList.remove(Classes.FILTERED); this.element.classList.remove(Classes.VISIBLE);
this.element.classList.add(Classes.CONCEALED); this.element.classList.add(Classes.HIDDEN);
} }
init() { init() {
this.addClasses([Classes.SHUFFLE_ITEM, Classes.FILTERED]); this.addClasses([Classes.SHUFFLE_ITEM, Classes.VISIBLE]);
this.applyCss(ShuffleItem.css); this.applyCss(ShuffleItem.Css.INITIAL);
this.scale = ShuffleItem.Scale.VISIBLE; this.scale = ShuffleItem.Scale.VISIBLE;
this.point = new Point(); this.point = new Point();
} }
@ -46,8 +49,8 @@ class ShuffleItem {
dispose() { dispose() {
this.removeClasses([ this.removeClasses([
Classes.CONCEALED, Classes.HIDDEN,
Classes.FILTERED, Classes.VISIBLE,
Classes.SHUFFLE_ITEM, Classes.SHUFFLE_ITEM,
]); ]);
@ -56,17 +59,34 @@ class ShuffleItem {
} }
} }
ShuffleItem.css = { ShuffleItem.Css = {
position: 'absolute', INITIAL: {
top: 0, position: 'absolute',
left: 0, top: 0,
visibility: 'visible', left: 0,
'will-change': 'transform', visibility: 'visible',
'will-change': 'transform',
},
VISIBLE: {
before: {
opacity: 1,
visibility: 'visible',
},
after: {},
},
HIDDEN: {
before: {
opacity: 0,
},
after: {
visibility: 'hidden',
},
},
}; };
ShuffleItem.Scale = { ShuffleItem.Scale = {
VISIBLE: 1, VISIBLE: 1,
FILTERED: 0.001, HIDDEN: 0.001,
}; };
export default ShuffleItem; export default ShuffleItem;

@ -82,7 +82,7 @@ class Shuffle {
} }
// Add class and invalidate styles // Add class and invalidate styles
this.element.classList.add(Shuffle.ClassName.BASE); this.element.classList.add(Shuffle.Classes.BASE);
// Set initial css for each item // Set initial css for each item
this._initItems(); this._initItems();
@ -161,7 +161,7 @@ class Shuffle {
this.element.style.position = 'relative'; this.element.style.position = 'relative';
} }
// Overflow has to be hidden // Overflow has to be hidden.
if (styles.overflow !== 'hidden') { if (styles.overflow !== 'hidden') {
this.element.style.overflow = 'hidden'; this.element.style.overflow = 'hidden';
} }
@ -173,13 +173,13 @@ class Shuffle {
* category will be used to filter the items. * category will be used to filter the items.
* @param {Array} [collection] Optionally filter a collection. Defaults to * @param {Array} [collection] Optionally filter a collection. Defaults to
* all the items. * all the items.
* @return {!{filtered: Array, concealed: Array}} * @return {!{visible: Array, hidden: Array}}
* @private * @private
*/ */
_filter(category = this.lastFilter, collection = this.items) { _filter(category = this.lastFilter, collection = this.items) {
var set = this._getFilteredSets(category, collection); var set = this._getFilteredSets(category, collection);
// Individually add/remove concealed/filtered classes // Individually add/remove hidden/visible classes
this._toggleFilterClasses(set); this._toggleFilterClasses(set);
// Save the last filter in case elements are appended. // Save the last filter in case elements are appended.
@ -195,35 +195,35 @@ class Shuffle {
} }
/** /**
* Returns an object containing the filtered and concealed elements. * Returns an object containing the visible and hidden elements.
* @param {string|Function} category Category or function to filter by. * @param {string|Function} category Category or function to filter by.
* @param {Array.<Element>} items A collection of items to filter. * @param {Array.<Element>} items A collection of items to filter.
* @return {!{filtered: Array, concealed: Array}} * @return {!{visible: Array, hidden: Array}}
* @private * @private
*/ */
_getFilteredSets(category, items) { _getFilteredSets(category, items) {
let filtered = []; let visible = [];
let concealed = []; let hidden = [];
// category === 'all', add filtered class to everything // category === 'all', add visible class to everything
if (category === Shuffle.ALL_ITEMS) { if (category === Shuffle.ALL_ITEMS) {
filtered = items; visible = items;
// Loop through each item and use provided function to determine // Loop through each item and use provided function to determine
// whether to hide it or not. // whether to hide it or not.
} else { } else {
items.forEach((item) => { items.forEach((item) => {
if (this._doesPassFilter(category, item.element)) { if (this._doesPassFilter(category, item.element)) {
filtered.push(item); visible.push(item);
} else { } else {
concealed.push(item); hidden.push(item);
} }
}); });
} }
return { return {
filtered, visible,
concealed, hidden,
}; };
} }
@ -256,17 +256,17 @@ class Shuffle {
} }
/** /**
* Toggles the filtered and concealed class names. * Toggles the visible and hidden class names.
* @param {{filtered, concealed}} Object with filtered and concealed arrays. * @param {{visible, hidden}} Object with visible and hidden arrays.
* @private * @private
*/ */
_toggleFilterClasses({ filtered, concealed }) { _toggleFilterClasses({ visible, hidden }) {
filtered.forEach((item) => { visible.forEach((item) => {
item.reveal(); item.show();
}); });
concealed.forEach((item) => { hidden.forEach((item) => {
item.conceal(); item.hide();
}); });
} }
@ -292,7 +292,7 @@ class Shuffle {
} }
/** /**
* Updates the filtered item count. * Updates the visible item count.
* @private * @private
*/ */
_updateItemCount() { _updateItemCount() {
@ -498,20 +498,27 @@ class Shuffle {
var itemSize = Shuffle.getSize(item.element, true); var itemSize = Shuffle.getSize(item.element, true);
var pos = this._getItemPosition(itemSize); var pos = this._getItemPosition(itemSize);
function callback() {
item.applyCss(ShuffleItem.Css.VISIBLE.after);
}
// If the item will not change its position, do not add it to the render // If the item will not change its position, do not add it to the render
// queue. Transitions don't fire when setting a property to the same value. // queue. Transitions don't fire when setting a property to the same value.
if (Point.equals(currPos, pos) && currScale === ShuffleItem.Scale.VISIBLE) { if (Point.equals(currPos, pos) && currScale === ShuffleItem.Scale.VISIBLE) {
callback();
return; return;
} }
item.point = pos; item.point = pos;
item.scale = ShuffleItem.Scale.VISIBLE; item.scale = ShuffleItem.Scale.VISIBLE;
let styles = ShuffleItem.Css.VISIBLE.before;
styles.transitionDelay = this._getStaggerAmount(count);
this._queue.push({ this._queue.push({
item, item,
opacity: 1, styles,
visibility: 'visible', callback,
transitionDelay: this._getStaggerAmount(count),
}); });
count++; count++;
@ -645,22 +652,30 @@ class Shuffle {
_shrink(collection = this._getConcealedItems()) { _shrink(collection = this._getConcealedItems()) {
let count = 0; let count = 0;
collection.forEach((item) => { collection.forEach((item) => {
function callback() {
item.applyCss(ShuffleItem.Css.HIDDEN.after);
}
// Continuing would add a transitionend event listener to the element, but // Continuing would add a transitionend event listener to the element, but
// that listener would not execute because the transform and opacity would // that listener would not execute because the transform and opacity would
// stay the same. // stay the same.
if (item.scale === ShuffleItem.Scale.FILTERED) { // The callback is executed here because it is not guaranteed to be called
// after the transitionend event because the transitionend could be
// canceled if another animation starts.
if (item.scale === ShuffleItem.Scale.HIDDEN) {
callback();
return; return;
} }
item.scale = ShuffleItem.Scale.FILTERED; item.scale = ShuffleItem.Scale.HIDDEN;
let styles = ShuffleItem.Css.HIDDEN.before;
styles.transitionDelay = this._getStaggerAmount(count);
this._queue.push({ this._queue.push({
item, item,
opacity: 0, styles,
transitionDelay: this._getStaggerAmount(count), callback,
callback() {
item.element.style.visibility = 'hidden';
},
}); });
count++; count++;
@ -694,13 +709,10 @@ class Shuffle {
* @return {!Object} Transforms for transitions, left/top for animate. * @return {!Object} Transforms for transitions, left/top for animate.
* @private * @private
*/ */
_getStylesForTransition(obj) { _getStylesForTransition({ item, styles }) {
let item = obj.item; if (!styles.transitionDelay) {
let styles = { styles.transitionDelay = '0ms';
opacity: obj.opacity, }
visibility: obj.visibility,
transitionDelay: (obj.transitionDelay || 0) + 'ms',
};
let x = item.point.x; let x = item.point.x;
let y = item.point.y; let y = item.point.y;
@ -720,11 +732,7 @@ class Shuffle {
return new Promise((resolve) => { return new Promise((resolve) => {
let id = onTransitionEnd(element, (evt) => { let id = onTransitionEnd(element, (evt) => {
evt.currentTarget.style.transitionDelay = ''; evt.currentTarget.style.transitionDelay = '';
itemCallback();
if (itemCallback) {
itemCallback();
}
resolve(); resolve();
}); });
this._transitions.push(id); this._transitions.push(id);
@ -762,7 +770,7 @@ class Shuffle {
if (transitions.length > 0 && this.options.speed > 0) { if (transitions.length > 0 && this.options.speed > 0) {
this._startTransitions(transitions); this._startTransitions(transitions);
// A call to layout happened, but none of the newly filtered items will // A call to layout happened, but none of the newly visible items will
// change position. Asynchronously fire the callback here. // change position. Asynchronously fire the callback here.
} else { } else {
setTimeout(this._dispatchLayout.bind(this), 0); setTimeout(this._dispatchLayout.bind(this), 0);
@ -808,10 +816,7 @@ class Shuffle {
Shuffle._skipTransitions(elements, () => { Shuffle._skipTransitions(elements, () => {
objects.forEach((obj) => { objects.forEach((obj) => {
obj.item.applyCss(this._getStylesForTransition(obj)); obj.item.applyCss(this._getStylesForTransition(obj));
obj.callback();
if (obj.callback) {
obj.callback();
}
}); });
}); });
} }
@ -831,7 +836,7 @@ class Shuffle {
* The magic. This is what makes the plugin 'shuffle' * The magic. This is what makes the plugin 'shuffle'
* @param {string|Function|Array.<string>} [category] Category to filter by. * @param {string|Function|Array.<string>} [category] Category to filter by.
* Can be a function, string, or array of strings. * Can be a function, string, or array of strings.
* @param {Object} [sortObj] A sort object which can sort the filtered set * @param {Object} [sortObj] A sort object which can sort the visible set
*/ */
filter(category, sortObj) { filter(category, sortObj) {
if (!this.isEnabled) { if (!this.isEnabled) {
@ -844,18 +849,18 @@ class Shuffle {
this._filter(category); this._filter(category);
// Shrink each concealed item // Shrink each hidden item
this._shrink(); this._shrink();
// How many filtered elements? // How many visible elements?
this._updateItemCount(); this._updateItemCount();
// Update transforms on .filtered elements so they will animate to their new positions // Update transforms on visible elements so they will animate to their new positions.
this.sort(sortObj); this.sort(sortObj);
} }
/** /**
* Gets the .filtered elements, sorts them, and passes them to layout. * Gets the visible elements, sorts them, and passes them to layout.
* @param {Object} opts the options object for the sorted plugin * @param {Object} opts the options object for the sorted plugin
*/ */
sort(opts = this.lastSort) { sort(opts = this.lastSort) {
@ -980,8 +985,8 @@ class Shuffle {
// Hide collection first. // Hide collection first.
this._toggleFilterClasses({ this._toggleFilterClasses({
filtered: [], visible: [],
concealed: oldItems, hidden: oldItems,
}); });
this._shrink(oldItems); this._shrink(oldItems);
@ -1130,7 +1135,7 @@ Shuffle.EventType = {
}; };
/** @enum {string} */ /** @enum {string} */
Shuffle.ClassName = Classes; Shuffle.Classes = Classes;
// Overrideable options // Overrideable options
Shuffle.options = { Shuffle.options = {

@ -125,7 +125,7 @@ describe('shuffle', function () {
instance.items.forEach(function (item) { instance.items.forEach(function (item) {
expect(item.element).to.have.class('shuffle-item'); expect(item.element).to.have.class('shuffle-item');
expect(item.element).to.have.class('filtered'); expect(item.element).to.have.class('shuffle-item--visible');
expect(item.element.style.opacity).to.be.defined; expect(item.element.style.opacity).to.be.defined;
expect(item.element.style.position).to.equal('absolute'); expect(item.element.style.position).to.equal('absolute');
expect(item.element.style.visibility).to.equal('visible'); expect(item.element.style.visibility).to.equal('visible');
@ -217,21 +217,21 @@ describe('shuffle', function () {
function second() { function second() {
expect(instance.visibleItems).to.equal(3); expect(instance.visibleItems).to.equal(3);
var concealed = [3, 4, 5, 6, 7, 8, 10].map(function (num) { var hidden = [3, 4, 5, 6, 7, 8, 10].map(function (num) {
return id('item' + num); return id('item' + num);
}); });
var filtered = [1, 2, 9].map(function (num) { var visible = [1, 2, 9].map(function (num) {
return id('item' + num); return id('item' + num);
}); });
concealed.forEach(function (element) { hidden.forEach(function (element) {
expect(element).to.have.class(Shuffle.ClassName.CONCEALED); expect(element).to.have.class(Shuffle.Classes.HIDDEN);
expect(element.style.visibility).to.equal('hidden'); expect(element.style.visibility).to.equal('hidden');
}); });
filtered.forEach(function (element) { visible.forEach(function (element) {
expect(element).to.have.class(Shuffle.ClassName.FILTERED); expect(element).to.have.class(Shuffle.Classes.VISIBLE);
expect(element.style.visibility).to.equal('visible'); expect(element.style.visibility).to.equal('visible');
}); });
@ -243,21 +243,21 @@ describe('shuffle', function () {
function third() { function third() {
expect(instance.visibleItems).to.equal(2); expect(instance.visibleItems).to.equal(2);
var concealed = [1, 2, 5, 6, 7, 8, 9, 10].map(function (num) { var hidden = [1, 2, 5, 6, 7, 8, 9, 10].map(function (num) {
return id('item' + num); return id('item' + num);
}); });
var filtered = [3, 4].map(function (num) { var visible = [3, 4].map(function (num) {
return id('item' + num); return id('item' + num);
}); });
concealed.forEach(function (element) { hidden.forEach(function (element) {
expect(element).to.have.class(Shuffle.ClassName.CONCEALED); expect(element).to.have.class(Shuffle.Classes.HIDDEN);
expect(element.style.visibility).to.equal('hidden'); expect(element.style.visibility).to.equal('hidden');
}); });
filtered.forEach(function (element) { visible.forEach(function (element) {
expect(element).to.have.class(Shuffle.ClassName.FILTERED); expect(element).to.have.class(Shuffle.Classes.VISIBLE);
expect(element.style.visibility).to.equal('visible'); expect(element.style.visibility).to.equal('visible');
}); });
@ -389,8 +389,8 @@ describe('shuffle', function () {
toArray(fixture.children).forEach(function (child) { toArray(fixture.children).forEach(function (child) {
expect(child).to.not.have.class('shuffle-item'); expect(child).to.not.have.class('shuffle-item');
expect(child).to.not.have.class('filtered'); expect(child).to.not.have.class('shuffle-item--visible');
expect(child).to.not.have.class('concealed'); expect(child).to.not.have.class('shuffle-item--hidden');
}); });
}); });

Loading…
Cancel
Save