masonry.js 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203
  1. /*!
  2. * Masonry v3.3.2
  3. * Cascading grid layout library
  4. * http://masonry.desandro.com
  5. * MIT License
  6. * by David DeSandro
  7. */
  8. ( function( window, factory ) {
  9. 'use strict';
  10. // universal module definition
  11. if ( typeof define === 'function' && define.amd ) {
  12. // AMD
  13. define( [
  14. 'outlayer/outlayer',
  15. 'get-size/get-size',
  16. 'fizzy-ui-utils/utils'
  17. ],
  18. factory );
  19. } else if ( typeof exports === 'object' ) {
  20. // CommonJS
  21. module.exports = factory(
  22. require('outlayer'),
  23. require('get-size'),
  24. require('fizzy-ui-utils')
  25. );
  26. } else {
  27. // browser global
  28. window.Masonry = factory(
  29. window.Outlayer,
  30. window.getSize,
  31. window.fizzyUIUtils
  32. );
  33. }
  34. }( window, function factory( Outlayer, getSize, utils ) {
  35. 'use strict';
  36. // -------------------------- masonryDefinition -------------------------- //
  37. // create an Outlayer layout class
  38. var Masonry = Outlayer.create('masonry');
  39. Masonry.prototype._resetLayout = function() {
  40. this.getSize();
  41. this._getMeasurement( 'columnWidth', 'outerWidth' );
  42. this._getMeasurement( 'gutter', 'outerWidth' );
  43. this.measureColumns();
  44. // reset column Y
  45. var i = this.cols;
  46. this.colYs = [];
  47. while (i--) {
  48. this.colYs.push( 0 );
  49. }
  50. this.maxY = 0;
  51. };
  52. Masonry.prototype.measureColumns = function() {
  53. this.getContainerWidth();
  54. // if columnWidth is 0, default to outerWidth of first item
  55. if ( !this.columnWidth ) {
  56. var firstItem = this.items[0];
  57. var firstItemElem = firstItem && firstItem.element;
  58. // columnWidth fall back to item of first element
  59. this.columnWidth = firstItemElem && getSize( firstItemElem ).outerWidth ||
  60. // if first elem has no width, default to size of container
  61. this.containerWidth;
  62. }
  63. var columnWidth = this.columnWidth += this.gutter;
  64. // calculate columns
  65. var containerWidth = this.containerWidth + this.gutter;
  66. var cols = containerWidth / columnWidth;
  67. // fix rounding errors, typically with gutters
  68. var excess = columnWidth - containerWidth % columnWidth;
  69. // if overshoot is less than a pixel, round up, otherwise floor it
  70. var mathMethod = excess && excess < 1 ? 'round' : 'floor';
  71. cols = Math[ mathMethod ]( cols );
  72. this.cols = Math.max( cols, 1 );
  73. };
  74. Masonry.prototype.getContainerWidth = function() {
  75. // container is parent if fit width
  76. var container = this.options.isFitWidth ? this.element.parentNode : this.element;
  77. // check that this.size and size are there
  78. // IE8 triggers resize on body size change, so they might not be
  79. var size = getSize( container );
  80. this.containerWidth = size && size.innerWidth;
  81. };
  82. Masonry.prototype._getItemLayoutPosition = function( item ) {
  83. item.getSize();
  84. // how many columns does this brick span
  85. var remainder = item.size.outerWidth % this.columnWidth;
  86. var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
  87. // round if off by 1 pixel, otherwise use ceil
  88. var colSpan = Math[ mathMethod ]( item.size.outerWidth / this.columnWidth );
  89. colSpan = Math.min( colSpan, this.cols );
  90. var colGroup = this._getColGroup( colSpan );
  91. // get the minimum Y value from the columns
  92. var minimumY = Math.min.apply( Math, colGroup );
  93. var shortColIndex = utils.indexOf( colGroup, minimumY );
  94. // position the brick
  95. var position = {
  96. x: this.columnWidth * shortColIndex,
  97. y: minimumY
  98. };
  99. // apply setHeight to necessary columns
  100. var setHeight = minimumY + item.size.outerHeight;
  101. var setSpan = this.cols + 1 - colGroup.length;
  102. for ( var i = 0; i < setSpan; i++ ) {
  103. this.colYs[ shortColIndex + i ] = setHeight;
  104. }
  105. return position;
  106. };
  107. /**
  108. * @param {Number} colSpan - number of columns the element spans
  109. * @returns {Array} colGroup
  110. */
  111. Masonry.prototype._getColGroup = function( colSpan ) {
  112. if ( colSpan < 2 ) {
  113. // if brick spans only one column, use all the column Ys
  114. return this.colYs;
  115. }
  116. var colGroup = [];
  117. // how many different places could this brick fit horizontally
  118. var groupCount = this.cols + 1 - colSpan;
  119. // for each group potential horizontal position
  120. for ( var i = 0; i < groupCount; i++ ) {
  121. // make an array of colY values for that one group
  122. var groupColYs = this.colYs.slice( i, i + colSpan );
  123. // and get the max value of the array
  124. colGroup[i] = Math.max.apply( Math, groupColYs );
  125. }
  126. return colGroup;
  127. };
  128. Masonry.prototype._manageStamp = function( stamp ) {
  129. var stampSize = getSize( stamp );
  130. var offset = this._getElementOffset( stamp );
  131. // get the columns that this stamp affects
  132. var firstX = this.options.isOriginLeft ? offset.left : offset.right;
  133. var lastX = firstX + stampSize.outerWidth;
  134. var firstCol = Math.floor( firstX / this.columnWidth );
  135. firstCol = Math.max( 0, firstCol );
  136. var lastCol = Math.floor( lastX / this.columnWidth );
  137. // lastCol should not go over if multiple of columnWidth #425
  138. lastCol -= lastX % this.columnWidth ? 0 : 1;
  139. lastCol = Math.min( this.cols - 1, lastCol );
  140. // set colYs to bottom of the stamp
  141. var stampMaxY = ( this.options.isOriginTop ? offset.top : offset.bottom ) +
  142. stampSize.outerHeight;
  143. for ( var i = firstCol; i <= lastCol; i++ ) {
  144. this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
  145. }
  146. };
  147. Masonry.prototype._getContainerSize = function() {
  148. this.maxY = Math.max.apply( Math, this.colYs );
  149. var size = {
  150. height: this.maxY
  151. };
  152. if ( this.options.isFitWidth ) {
  153. size.width = this._getContainerFitWidth();
  154. }
  155. return size;
  156. };
  157. Masonry.prototype._getContainerFitWidth = function() {
  158. var unusedCols = 0;
  159. // count unused columns
  160. var i = this.cols;
  161. while ( --i ) {
  162. if ( this.colYs[i] !== 0 ) {
  163. break;
  164. }
  165. unusedCols++;
  166. }
  167. // fit container to columns that have been used
  168. return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
  169. };
  170. Masonry.prototype.needsResizeLayout = function() {
  171. var previousWidth = this.containerWidth;
  172. this.getContainerWidth();
  173. return previousWidth !== this.containerWidth;
  174. };
  175. return Masonry;
  176. }));