Facebook/Twitter 风格照片网格风格布局[关闭]

2024-02-20

我正在尝试实现 facebook 风格的照片网格布局。我为此使用 angularjs 和 bootstrap 。我遇到过某些插件,例如角砌体 https://github.com/passy/angular-masonry我认为可以用于此目的。以下是我实际尝试实现的可能布局的一些快照:

知道如何实现这一点吗?还有其他插件可以让生活变得轻松吗?


所有这些都是你的可能性:

  1. ng照片网格 http://angularscript.com/facebook-like-photo-grid-angularjs-ngphotogrid/

  2. 无缝响应式照片网格 https://css-tricks.com/seamless-responsive-photo-grid/

  3. freewall https://github.com/kombai/freewall- 使用 jQuery

  4. Bootstrap 响应式图片库 by mobirise https://mobirise.com/bootstrap-gallery/

  5. 伽玛画廊 http://tympanus.net/codrops/2012/11/06/gamma-gallery-a-responsive-image-gallery-experiment/

另请阅读 -Masonry 的 AngularJS 指令 http://angularscript.com/an-angularjs-directive-for-masonry/

ngPhotoGrid 示例:

//ngPhotoGrid.js
angular.module("ngPhotoGrid", [])
angular.module("ngPhotoGrid")
  .filter("photoUrlSafe", [
    "$sce", function($sce) {
      return function(text) {
        return $sce.trustAsResourceUrl(text);
      };
    }
  ])
  .directive('endRepeat', function() {
    return {
      restrict: 'A',
      require: "^ngPhotoGrid",
      link: function(scope, element, attrs, gridController) {
        if (scope.$last) {
          gridController.notifyDOMReady(element)
        }
      }
    };
  })
  .directive("ngPhotoGrid", ["$templateCache", function($templateCache){

    $templateCache.put("photo_grid.html",
      "<ul class='photo-grid-wrapper' ng-style = 'parentStyle'><li class='grid-cell' ng-repeat= 'image in loadedImages track by $index' ng-style = 'image.cellStyle' ng-click='cellClicked(image)' end-repeat='' ng-attr-data-src='{{image[defaultOptions.urlKey] | photoUrlSafe}}'><img class='grid-cell-image' ng-style = 'image.imageStyle' ng-src='{{image[defaultOptions.urlKey]}}' alt='#'/></li></ul>");

    function linker(scope, element, attrs) {
      scope.loadedImages      = [];
      scope.loadedTakenImages = [];
      scope.takenImages       = [];

      // ###OPTIONS
      scope.defaultOptions =  {
                                urlKey          :     "original_url",
                                sortByKey       :     "nth",
                                onClicked       :     function() {},
                                onBuilded       :     function() {},
                                onDOMReady      :     function() {},
                                margin          :     2,
                                maxLength       :     5,
                                isSquare        :     false,
                                buildOnLoading  :     true
                              }

      angular.extend(scope.defaultOptions, scope.gridOptions);

      var IS_SQUARE    = scope.defaultOptions.isSquare;
      var GRID_WIDTH   = element.prop('offsetWidth');
      var MARGIN       = scope.defaultOptions.margin;

      if (!GRID_WIDTH) { // set the default width of parent
        GRID_WIDTH = 250
      }

      scope.parentStyle = { width: GRID_WIDTH + "px", overflow: "hidden", position: "relative", margin: 0, padding: 0 }

      if(IS_SQUARE) {
        scope.parentStyle.height = GRID_WIDTH + "px";
      }

      var commonStyle = {
                      display:        'block',
                      overflow:       'hidden',
                      cssFloat:       'left',
                      cursor:         'pointer',
                      position:       'relative'
                    };

      //callback handler
      scope.cellClicked = function(image) {
        scope.defaultOptions.onClicked(image);
      }

      /**
      * choose images from the url source to build grid
      * take maximum 7 images for best looking
      *------------------------------------------------*/
      scope.chooseImages = function(images) {
        angular.forEach(images, function(image, index) {
          var randNumber; //set the id and nth value for image if does not have
          randNumber                = randomNumber();
          image.id                  = image.id || randNumber;
          image[scope.defaultOptions.sortByKey]  = image[scope.defaultOptions.sortByKey] || randNumber;
        });

        var sortedImages = images.sort(function(a, b) {
          return a[scope.defaultOptions.sortByKey] - b[scope.defaultOptions.sortByKey]
        })

        return sortedImages.slice(0, scope.defaultOptions.maxLength)
      }

      randomNumber = function(max) {
        max = max || 999;
        return Math.floor(Math.random()*max);
      }

      scope.preloadImages = function(images) {
        scope.takenImages = scope.chooseImages(images)

        angular.forEach(scope.takenImages, function(image, index) {
          var img;
          img                     = new Image();
          img.id                  = image.id ;
          img[scope.defaultOptions.sortByKey]  = image[scope.defaultOptions.sortByKey];

          img.onload              = function(loadedImage) {
            scope.loadedTakenImages.push(loadedImage);

            // store the original dimesion of image
            image.naturalWidth    = loadedImage.target.naturalWidth
            image.naturalHeight   = loadedImage.target.naturalHeight

            // build the grid immediatly after the image was loaded
            // building while images loading
            if(scope.defaultOptions.buildOnLoading) {
              scope.buildPhotoGrid();
              setTimeout(function() {
                scope.$apply()
              }, 10)
            }
            
            if(scope.loadedTakenImages.length == scope.takenImages.length) {   
              //trigger build completed handler
              scope.defaultOptions.onBuilded(element)
   
              //grid also can be build after all image loaded
              //all image would be shown correctly, loading time cause poor UX
              if(!scope.defaultOptions.buildOnLoading) {
                scope.buildPhotoGrid()
                setTimeout(function() {
                  scope.$apply()
                }, 15)
              }
            }
            
          };
          img.src = image[scope.defaultOptions.urlKey];
        });
      };

      scope.buildPhotoGrid = function() {
        var firstImage, imageStyle, smallCellHeight,
        smallCellWidth, bigCellWidth, bigCellHeight, cellCount, is2First;

        // get cell style & builded options
        styles          = scope.getCellStyles();
        smallCellHeight = styles.options.smallCellHeight;
        smallCellWidth  = styles.options.smallCellWidth;
        bigCellWidth    = styles.options.bigCellWidth;
        bigCellHeight   = styles.options.bigCellHeight;
        cellCount       = styles.options.cellCount;
        is2First        = styles.options.is2First;

        scope.loadedImages = []
        angular.forEach(scope.takenImages, function(image, index) {
          if (is2First) { //case the grid has 2 image big first
            var bigCellStyle, smallCellStyle;
            bigCellStyle          = angular.copy(styles.big);
            smallCellStyle        = angular.copy(styles.small);
            if (index == 0) {
              bigCellStyle.top    = "0";
              image.cellStyle     = bigCellStyle;
              image.imageStyle = getImageStyle(bigCellWidth, bigCellHeight, image);
            } else if (index  == 1) {
              bigCellStyle.top    = bigCellHeight + MARGIN + "px";
              image.cellStyle     = bigCellStyle;
              image.imageStyle = getImageStyle(bigCellWidth, bigCellHeight, image);
            } else {
              var margin, smallCellIndex;

              // fix the last cell of 2 first was not fit the grid height
              if(index == scope.takenImages.length - 1) {
                smallCellStyle.height = smallCellHeight + MARGIN + "px"
              }

              smallCellIndex      = index - 2;
              margin              = smallCellIndex == 0 ? 0 : MARGIN;
              smallCellStyle.top  = smallCellIndex * smallCellHeight + (margin * smallCellIndex) + "px";
              image.cellStyle     = smallCellStyle;
              image.imageStyle    = getImageStyle(smallCellWidth, smallCellHeight, image);


            }
          } else if (index == 0) { //big cell style
            image.cellStyle = styles.big;
            image.imageStyle = getImageStyle(bigCellWidth, bigCellHeight, image);
          } else if (index != cellCount - 1 || cellCount == 2){ //small cells
            image.cellStyle = styles.small;
            image.imageStyle = getImageStyle(smallCellWidth, smallCellHeight, image);
          } else { //last small cell style (remove redundant margin right or bottom)
            image.imageStyle = getImageStyle(smallCellWidth, smallCellHeight, image);
            image.cellStyle = styles.last;
          }
        })
        scope.loadedImages = scope.takenImages;
      }

      function getImageStyle(cellWidth, cellHeight, image) {
        var imageWidth, imageHeight, curImageWidth, curImageHeight, imgRatio, cellRatio;

        cellWidth  = Math.round(cellWidth);
        cellHeight = Math.round(cellHeight);
        imageWidth  = image.naturalWidth;
        imageHeight = image.naturalHeight;
        imgRatio  = imageWidth / imageHeight;
        cellRatio = cellWidth / cellHeight;

        // when the any image's dimension greater than cell's dimension
        if(cellWidth > imageWidth || cellHeight > imageHeight) {
          if (cellWidth >= imageWidth) {
            return getSmallImagePortraitStyle(cellHeight, cellWidth, imgRatio);
          } else {
            return getSmallImageLandscapeStyle(cellHeight, cellWidth, imgRatio);
          }
        } else { // when the image smaller than the cell in both dimension
          if(imgRatio >= 1) {
            return getSmallImageLandscapeStyle(cellHeight, cellWidth, imgRatio);
          } else {
            return getSmallImagePortraitStyle(cellHeight, cellWidth, imgRatio);
          }
        }
      }

      function getSmallImageLandscapeStyle(cellHeight, cellWidth, imgRatio) {
        var curImageWidth = cellWidth;
        var curImageHeight = Math.round(curImageWidth  / imgRatio);
        if(curImageHeight >= cellHeight) {
          var top = (-1) * Math.round((cellWidth / imgRatio - cellHeight) / 2);
          if(curImageWidth < cellWidth) {
            return { width: "100%", position: "relative", top: top + "px"}
          } else {
            return { maxWidth: "100%", position: "relative", top: top + "px"}
          }
        } else {
          var left = (-1) * Math.round((cellHeight * imgRatio - cellWidth) / 2);
          return { maxHeight: "100%", height: "100%", position: "relative", left: left + "px"}
        }
      }
          
      function getSmallImagePortraitStyle(cellHeight, cellWidth, imgRatio) {
        var curImageHeight = cellHeight;
        var curImageWidth = Math.round(curImageHeight  * imgRatio);
        var top = (-1) * Math.round((cellWidth / imgRatio - cellHeight) / 2);
        var left = (-1) * Math.round((cellHeight * imgRatio - cellWidth) / 2);
        if(curImageWidth <= cellWidth) {
          return { width: "100%", position: "relative", top: top + "px"}
        } else {
          return { maxHeight: "100%", height: "100%", position: "relative", left: left + "px"} 
        }
      }

      /**
      * build cell style for grid
      * @firstRatio   : ratio of the first image in list
      * @secondRatio  : ratio of the second image in list
      * @cellCount    : total cells in grid
      *------------------------------------------------*/
      buildCellStyle      = function (firstImage, secondImage, cellCount) {
        var firstRatio, secondRatio, bigCellStyle, smallCellStyle, lastCellStyle,
            WIDTH_RATE, bigCellWidth, bigCellHeight, smallCellHeight, smallCellWidth, is2First, 
            case2BigImage1, case2BigImage2;

        firstRatio              = firstImage.naturalWidth / firstImage.naturalHeight;

        if (secondImage)
          secondRatio           = secondImage.naturalWidth / secondImage.naturalHeight;
        else
          secondRatio           = 1.5 //fail all cases below

        bigCellStyle            = angular.copy(commonStyle);
        smallCellStyle          = angular.copy(commonStyle);
        lastCellStyle           = angular.copy(commonStyle);
        WIDTH_RATE              = getWidthRate(firstRatio, cellCount);
        case2BigImage1          = firstRatio  > 0.8 && firstRatio  < 1.2 &&
                                  secondRatio > 0.8 && secondRatio < 1.2
        case2BigImage2          = firstRatio >= 2 && secondRatio >= 2

        if(cellCount == 2) { //build style for grid has 2 images and first image has firstRatio > 1

          if(firstRatio >= 1) {
            bigCellStyle.marginBottom = MARGIN;
            bigCellStyle.width    = GRID_WIDTH;
            bigCellStyle.height   = GRID_WIDTH / 2;
            smallCellStyle.width  = GRID_WIDTH;
            smallCellStyle.height = GRID_WIDTH / 2 - MARGIN;
          } else {
            var marginSize              = MARGIN / cellCount;
            bigCellStyle.marginRight    = marginSize;
            smallCellStyle.marginLeft   = marginSize;

            if(IS_SQUARE) {
              bigCellWidth          = Math.floor(GRID_WIDTH / 2) - MARGIN;
              bigCellStyle.width    = bigCellWidth;
              bigCellStyle.height   = GRID_WIDTH;

              smallCellWidth        = Math.floor(GRID_WIDTH / 2) - MARGIN;
              smallCellStyle.width  = smallCellWidth;
              smallCellStyle.height = GRID_WIDTH;
            } else {
              bigCellWidth          = Math.floor(GRID_WIDTH * WIDTH_RATE) - MARGIN;
              bigCellStyle.width    = bigCellWidth;
              bigCellStyle.height   = bigCellWidth;

              smallCellWidth        = GRID_WIDTH - bigCellWidth - MARGIN;
              smallCellHeight       = bigCellWidth;
              smallCellStyle.width  = smallCellWidth;
              smallCellStyle.height = smallCellHeight;
            }
          }
        }

        // add style for first column contain 2 big images, only support for grid has more than 5 cells
        //NOTE: need check when 2 first were same size!!!
        else if (cellCount >= 5 && (case2BigImage1 || case2BigImage2)) {
          var GRID_HEIGHT;
          WIDTH_RATE            = case2BigImage1 ? 1/2 : 2/3;
          scope.parentStyle.position = "relative";
          bigCellStyle.cssFloat = smallCellStyle.cssFloat = lastCellStyle.cssFloat = null;
          bigCellStyle.position = smallCellStyle.position = lastCellStyle.position = "absolute";

          //determine the height of the big cell
          //height == width / 2 if the grid in case2BigImage1
          if(case2BigImage1) {
            bigCellHeight = GRID_WIDTH / 2;
          } else {
            bigCellHeight  = WIDTH_RATE * GRID_WIDTH / firstRatio;
          }

          GRID_HEIGHT               = bigCellHeight * 2 + MARGIN; //margin bottom the first big image
          scope.parentStyle.height  = GRID_HEIGHT + "px";

          bigCellStyle.width        = GRID_WIDTH * WIDTH_RATE - MARGIN;
          bigCellStyle.height       = bigCellHeight;
          bigCellStyle.left         = 0;

          smallCellStyle.width      = GRID_WIDTH - bigCellStyle.width - MARGIN;
          smallCellStyle.height     = Math.floor((GRID_HEIGHT / (cellCount - 2))) - MARGIN;
          smallCellStyle.right      = 0;

          is2First                  = true; //flag this style is has 2 big image style
          lastCellStyle.height      = smallCellStyle.height + MARGIN;

        } else if(firstRatio >= 1) { //build style for grid more than 2 images and first image has firstRatio > 1

          bigCellStyle.marginBottom  = MARGIN;
          smallCellStyle.marginRight = MARGIN;
          var smallCellCount         = cellCount - 1;
          
          if (IS_SQUARE) {
            bigCellStyle.height   = GRID_WIDTH * 2 / 3;
            bigCellStyle.width    = GRID_WIDTH;
            smallCellStyle.height = GRID_WIDTH * 1 / 3 - MARGIN;
          } else {
            bigCellStyle.width    = GRID_WIDTH ;
            bigCellStyle.height   = GRID_WIDTH * 2 / 3;
          }
          smallCellStyle.width  = ( GRID_WIDTH - smallCellCount * MARGIN ) / smallCellCount;
          // determine the height of smallCell below
          if (IS_SQUARE) {
            smallCellStyle.height = GRID_WIDTH - bigCellStyle.height - MARGIN;
          } else if (firstRatio > 1.3 && firstRatio < 1.5) { // 4:3 < firstRatio < 5:3
            smallCellStyle.height     = smallCellStyle.width / firstRatio;
          } else if (firstRatio > 1.5) {
            smallCellStyle.height     = smallCellStyle.width / 1.5;
          } else {
            smallCellStyle.height     = smallCellStyle.width;
          }
          lastCellStyle.height = smallCellStyle.height;
          lastCellStyle.width  = smallCellStyle.width;
        } else { //build style for grid more than 2 images and first image has firstRatio <= 1
          bigCellStyle.marginRight       = MARGIN;
          smallCellStyle.marginBottom    = MARGIN;

          if (IS_SQUARE) {
            bigCellHeight   = GRID_WIDTH;
            bigCellWidth    = GRID_WIDTH * WIDTH_RATE;
          } else {
            bigCellWidth    = Math.floor(GRID_WIDTH * WIDTH_RATE);
            bigCellHeight   = bigCellWidth / firstRatio;
          }

          bigCellStyle.width    = bigCellWidth;
          bigCellStyle.height   = bigCellHeight;

          smallCellCount        = cellCount - 1;
          smallCellWidth        = GRID_WIDTH - bigCellWidth - MARGIN;
          smallCellHeight       = bigCellHeight / smallCellCount - MARGIN

          smallCellStyle.width  = GRID_WIDTH - bigCellWidth - MARGIN;
          smallCellStyle.height = smallCellHeight;
          lastCellStyle.width   = smallCellWidth;
          lastCellStyle.height  = smallCellHeight;
        }

        return {
          big:    bigCellStyle,
          small:  smallCellStyle,
          last:   lastCellStyle,
          options:  {
            firstRatio:       firstRatio,
            // keep these value because ng style need add measured suffix
            smallCellWidth:   smallCellStyle.width,
            smallCellHeight:  smallCellStyle.height,
            bigCellWidth:     bigCellStyle.width,
            bigCellHeight:    bigCellStyle.height,
            cellCount:        cellCount,
            is2First:         is2First
          } //keep these values to style cell image after building style for cell link
        }
      }

      getWidthRate = function(firstRatio, cellCount) {
        if (cellCount == 2) { //build style for 2 images
          if(firstRatio > 1) {
            return 2/3;
          } else {
            return 1/2;
          }
        } else if(firstRatio > 1) { //build style for >= 3 images, first image has firstRatio > 1
          return 1
        } else { //build style for >= 3 images, first image has firstRatio < 1
          return 2/3
        }
      }

      scope.getCellStyles     = function() {
        var firstImage, secondImage, cellCount, buildedStyle;

        firstImage            = scope.takenImages[0];
        secondImage           = scope.takenImages[1];
        cellCount             = scope.takenImages.length;

        if (cellCount == 1) { //build style for only one image
          //@todo need implement!
        } else { //build style for >=2 images
          buildedStyle        = buildCellStyle(firstImage, secondImage, cellCount);
        }

        // remove margin right of last small cell in the bottom
        if(buildedStyle.small.marginRight) {
          buildedStyle.last.marginRight     = 0;
          buildedStyle.last.width           = buildedStyle.small.width + MARGIN;
        }

        // remove margin bottom of last small cell in the right
        if(buildedStyle.small.marginBottom) {
          buildedStyle.last.marginBottom    = 0;
          buildedStyle.last.height          = buildedStyle.small.height + MARGIN;
        }

        // add suffix px for margin and size for ng-style working
        var attrs = ["width", "height", "marginRight", "marginLeft", "marginBottom", "left", "right"];
        angular.forEach(attrs, function(attr, index) {
          if(buildedStyle.big[attr]) {
            buildedStyle.big[attr]   += "px";
          }
          if(buildedStyle.small[attr]) {
            buildedStyle.small[attr] += "px";
          }
          if(buildedStyle.last[attr]) {
            buildedStyle.last[attr]  += "px";
          }
        })

        return buildedStyle;
      }

      //trigger build grid
      scope.$watch("images", function(images) {
        if(images && images.length > 0) {
          scope.preloadImages(images);
        }
      })
    }

    return {
      restrict:       "A",
      templateUrl:    "photo_grid.html",
      scope: {
        images:       "=",
        gridOptions:  "="
      },
      controller: ["$scope", "$element", function($scope, $element) {
        this.notifyDOMReady = function() {
          $scope.defaultOptions.onDOMReady($element)
        }
      }],
      link: linker
    }

  }])





angular.module("ngApp", ["ngPhotoGrid"])
angular.module("ngApp").controller("indexCtrl", ["$scope", function($scope){

  //show loading mark while grid is building
  $scope.isBuildingGrid = true;

  // production test
  img1 = {original_url: "http://lorempixel.com/1366/768"};
  img2 = {original_url: "http://lorempixel.com/316/316"};
  img3 = {original_url: "http://lorempixel.com/400/200"};
  img4 = {original_url: "http://lorempixel.com/600/1000"};
  img5 = {original_url: "http://lorempixel.com/600/800"};
  img6 = {original_url: "http://lorempixel.com/800/600"};
  img7 = {original_url: "http://lorempixel.com/800/800"};
  img8 = {original_url: "http://lorempixel.com/900/1000"};

  // // local dev
  // img1 = {original_url: "images/1366x768.jpg"};
  // img2 = {original_url: "images/316x316.jpg"};
  // img3 = {original_url: "images/600x1000.jpg"};
  // img4 = {original_url: "images/900x1000.jpg"};
  // img5 = {original_url: "images/600x800.jpg"};
  // img6 = {original_url: "images/800x600.jpg"};
  // img7 = {original_url: "images/800x800.jpg"};
  // img8 = {original_url: "images/900x1000.jpg"};

  var sources             = [img1, img2, img3, img4, img5, img6, img7, img8]

  var sources2big            = [{original_url: "http://lorempixel.com/316/316", nth: 1}, {original_url: "http://lorempixel.com/800/800", nth: 2}, img3, img4, img5, img6]

  $scope.rand             = function(min, max) {
    return Math.floor(Math.random() * (max - min)) + min;
  }

  $scope.clickHandler     = function(image) {
    alert(JSON.stringify(image))
  }

  $scope.buildCompletedHandler = function() {
    console.log ("built completed!")
    $scope.isBuildingGrid = false;
    
  }

  getSelectedSeeds = function() {
    var photoNumbers      = $scope.rand(2, 7)
    var seeds             = []
    var arr               = []
    while(arr.length < photoNumbers){
      var randomnumber    = $scope.rand(1, 8);
      var found           = false;
      for(var i = 0; i < arr.length; i++){
        if(arr[i] == randomnumber ){
          found           = true;
          break;
        }
      }
      if(!found) {
        arr[arr.length]   = randomnumber;
        seeds.push(sources[randomnumber])
      }
    }
    return seeds;
  }

  $scope.images              = getSelectedSeeds();

  $scope.images2big          = sources2big.slice(0, 7);

  /**
   * Options definitions
   *----------------------*/
  $scope.gridOptions = {
    urlKey      :     "original_url",
    sortKey     :     "nth",
    onClicked   :     function(image) {
                        alert(JSON.stringify(image))
                      },
    onBuilded   :     function() {
                        console.log ("built completed!")
                        $scope.isBuildingGrid = false;
                        
                      },
    margin      :     2,
    maxLength   :     5
  }

  /**
   * Options definitions for square example
   *----------------------------------------*/
  $scope.gridOptionsSquare = {
    urlKey      :     "original_url",
    sortKey     :     "nth",
    onClicked   :     function(image) {
                        alert(JSON.stringify(image))
                      },
    onBuilded   :     function() {
                        console.log ("built completed!")
                        $scope.isBuildingGrid = false;
                        
                      },
    margin      :     2,
    isSquare    :     true,
    maxLength   :     4
  }
    
  $scope.squareGridGroup = [];

  angular.forEach([1,2,3,4,5,6], function() {
    $scope.squareGridGroup.push(angular.copy(getSelectedSeeds()))
  })


}])
/**
* All these styles are not a part of angular module.
*/
.center {
  text-align: center;
}
.small {
  font-size:12px;
  font-weight: normal;
  margin-left: 10px;
}

.wrapper {
  text-align: center;
}
.content {
  width: 400px;
  margin: 0 auto;
}
.feed-item{
  overflow: hidden;
}

.feed-photos {
  position: relative;
  min-height: 100px;
}

.feed-photos.loading::after {
  content: "";
  height: 100%;
  width: 100%;
  background: rgba(0,0,0,.6) url("loader.gif") center center no-repeat;
  left:0;
  top:0;
  position: absolute;

}
.feed-photos.loading .grid-cell-image{
  width: 100%;
}

/**
* It's the part of module
* used to style the border of grid
* included this style if you want to border the grid cell
*/
.grid-cell:after {  
  content: '';
  position: absolute;
  border: 1px solid rgba(0, 0, 0, 0.2); /*change this color if you want*/
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.21/angular.min.js"></script>
    <style>
      body { text-align: center; }
      .links a { padding: 0 5px ;}
      .active { color: red;}
    </style>
  </head>
  <body ng-app='ngApp'>
    <div class='wrapper' ng-controller="indexCtrl">
      <div class="header">
        <h1>ngPhotoGrid Example - 2 bigs first</h1>
      </div>
      <div class='content'>
        <div class="feed-item">
          <div  ng-photo-grid =   ""
                images        =   "images2big"
                grid-options  =   "gridOptions"
                ng-class      =   "{'loading': isBuildingGrid}"
                class         =   "feed-photos">
          </div>
        </div>
        <p class='small'><i>click on image or turn on firebug to see the callback.</i></p>
      </div>

      <div class='footer'>
        <p><a href="https://github.com/jerryc-nguyen/ng-photo-grid" target="_blank">source on github</a></p>
      </div>
    </div>
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Facebook/Twitter 风格照片网格风格布局[关闭] 的相关文章

  • 为什么我的交互式图像仅在 Internet Explorer 上出现故障?

    我的问题 我为自己制作了一个图像地图 交互式图像 它在 Chrome safari 和 Firefox 上完美运行 然而 当我在可怕的互联网浏览器上尝试它时 它真的很糟糕 这些小点应该扩展到更大的盒子中 在互联网浏览器上它要么不起作用 要么
  • 传递给 $resource 的 @id 是什么?

    resource entries id id id update method PUT id是什么 在 资源上doc page http docs angularjs org api ngResource 24resource下面有人这么说
  • 使用 CSS 的响应式图像

    我发现调整图像大小以使其具有响应能力很棘手 我正在开发一个 php 应用程序来自动将网站转换为响应式版本 我有点被图像困住了 我已经成功地为网站上的每个图像添加了一个包装类 并且可以很好地调整图像的大小 我的问题在于自然小于窗口的图像 例如
  • 使用javascript动态更新css内容

    需要将 css 更新为动态值 我不确定最好的方法是什么 div style zoom 1 div 缩放级别将根据窗口大小调整触发 应用程序将相应缩放 我将此应用程序加载到 cordova 中并让它在 iPAD 中运行 然后我意识到需要使用
  • 使用 CSS 折叠和展开元素

    我正在尝试构建一个页面 加载时仅可见标题 并且 当用户单击标题时 每个标题下方的表格会在隐藏和显示状态之间切换 我的限制是只能在 CSS 中执行此操作 这是我到目前为止想到的 https jsfiddle net Argoron c1ypx
  • Bootstrap 折叠导航菜单

    我正在尝试使用 Bootstrap 折叠插件 但收效甚微 我想要一个带有隐藏子菜单的手风琴式导航菜单 我的 HTML 和 JS ul class nav nav list li class nav header span Home span
  • Webpack 4:如何使用 LESS 获取 CSS 源映射?

    多年来我一直在尝试让 CSS 源映射在 webpack 中工作 但没有成功 我不确定链条中哪里出了问题 我希望有人能指出我正确的方向 这是发生的事情 行号是错误的 实际上文件名也是错误的 main less只是包含一堆 import也就是说
  • 仅当显式选择行时才关闭 ui-bootstrap typeahead

    我创建了这个jsBin http jsbin com livuqafe 2 edit来证明我遇到的问题 如果您转到此处 请尝试输入 五 并继续 你的自然反应是输入 五 然后按 Tab 如果你想要 五百 你可以向下箭头一次 但是 在这种情况下
  • 为什么 Twitter Bootstrap 表格的宽度总是 100%?

    假设这个标记 table class table table bordered align center 无论我有多少个单元格 表格的宽度始终为 100 为什么 引导程序中的所有表格都根据其容器进行拉伸 您可以通过将表格放入 span 您选
  • React 设置背景颜色与状态 rgb

    所以我根据数据库的结果生成一个表 在数据库中我保留 rgb 值 例如 75 75 75 现在当我生成列表时 我想使用保存的值设置我的 td 的背景 tbody this state boxes map box i gt tr td box
  • 如何在不使用变换或顶部/左侧的情况下转换列表中项目的位置

    有一天我偶然发现一个例子 https codepen io itslit pen gvKrMY它使用 Vue js 但我的问题更多是关于 Vue 用于实现状态之间转换的 CSS 和 HTML 卡片暂时获得等级 shuffleMedium m
  • Protractor+AngularJS+Jasmine - 测试按住项目

    AngularJS 和 Protractor 非常新 但我认为到目前为止我正在朝着正确的方向前进 我的网站有一个项目列表 当您单击该项目并按住 X 秒时 它会打开一个模式窗口 我如何在 Protractor Jasmine 中模拟这种行为
  • 自定义rc-time-picker的样式

    我在用rc time picker我的项目的包 但我在自定义样式时遇到问题pop up of my time picker成分 这是我的组件的屏幕截图 首先 我需要更改时间的背景颜色item在当时li from light grey 在屏幕
  • Protractor e2e 测试表头和 , 标签

    我正在使用下表 我想测试每个标签 th td 标签 该标签中的文本以及该文本的计数 HTML 片段 table class table table striped tbody tr th b a Patient Id a b th th b
  • 在表格上使用上下文样式将点边框应用于特定单元格

    我以前有过问了一个问题 https stackoverflow com questions 6326266 issue with applying dotted border to cells in table design在这个问题上 你
  • Chrome 中的性能问题

    我目前正在从事一个相对较大的项目 使用 AngularJs 构建 应用程序的一部分是一个表单 您可以向其中添加任意数量的页面 不幸的是 添加了很多不必要的垃圾 即表示表单模型的对象可能会变得非常大 在某些时候 Chrome 基本上无法处理它
  • CSS 类命名约定

    在网页上 有两个控件块 主要和次要 大多数人会使用什么类名 选择一 div class primary controls div
  • 如何在 angular-ui 中动态禁用 ui-sortable 指令

    我正在使用 Angular ui 使用 ui sortable 指令进行排序 是否可以根据范围状态动态启用 禁用可排序功能 因此 我需要一个按钮来更改范围属性的状态 并且根据此属性可排序是否应该工作 角度指令支持观察可排序选项何时发生变化
  • CSS溢出文本显示在几行中,没有断字

    我有一些长文本显示在 div 中 该 div 具有固定的宽度和高度 我希望文本显示在几行上 作为 div 高度 并且句子单词不会中断 一行中的单词前缀和下一行中的继续 此外 我想在末尾添加省略号最后一句话 CSS white space n
  • 在 Nexus 7 2013 上更改方向时 CSS 媒体查询不起作用

    我目前正在我的笔记本电脑 台式电脑和 Nexus 7 2013 上测试 CSS 媒体查询 除了 Nexus 7 之外 它们在台式机和笔记本电脑上都运行良好 当我更改方向时 除非刷新页面 否则样式不会应用 例如 以纵向模式握住设备时 页面正常

随机推荐

  • 如何处理尺寸未知的二维数组?

    我正在编写一个用于矩阵乘法的 C 函数 它需要两个二维整数数组 如果我知道输入数组的尺寸 我就可以做到这一点 但我想创建一个更通用的函数 如何找到它们的尺寸 以及当我在编译时不知道产品的尺寸时如何返回数组 如果您只有指向数组开头的指针 则无
  • 来自 C# 的多个 CMD 命令?

    所以我想知道 如何使用 C 在 CMD 中执行多个命令 我的意思是 我有一个 exe 文件 它依赖于通过 cmd 变量 VAMP PATH 查找文件 是的 我正在使用 VAMP 插件 所以我在 CMD 中使用它的方式是 set VAMP P
  • 安卓JSON解析

    我需要有关我的代码的建议 我正在尝试解析 PHP 函数生成的 JSON 数组json encode My json data streamer froggen yt length 25078 streamer wingsofdeath yt
  • Twitter Bootstrap 包含 jQuery 吗?

    Does 推特引导程序 http getbootstrap comv3 包括jQuery http jquery com 或者我需要单独包含 jQuery 吗 如果它确实包含 jQuery 则包含什么版本 编号 的 jQuery 以及使用
  • 禁用向访问网站的用户提示“记住我的密码”

    所以我有一个 ASP Net MVC 网站 我想知道是否有可能让用户必须手动输入密码 这样浏览器就不会自动填充密码 这似乎是一个简单的问题 但通过谷歌搜索我只是得到了有关客户如何禁用提示的说明 我从来没有用过它 但是有一个自动完成属性 当禁
  • 在提交到服务器之前如何在 Fine Uploader 中获取图像的尺寸并验证它?

    我使用的是5 11 10版本精美上传者 http fineuploader com 我知道有一个验证 http docs fineuploader com branch master api options html validationF
  • 创建对角矩阵(更高维度)

    我有一个n m矩阵 说A 我想创建以下内容m m n矩阵 说B for j 1 n B j diag A j end 我该如何做到这一点而不需要循环 Thanks UPDATE 我已经编辑了问题以修复示例代码中的拼写错误 我相当确定您的示例
  • SqlAlchemy(Flask+Postgres):如何仅更新json字段的特定属性?

    我有一个表 其中有一列声明为 json 我需要通过向 json 值添加键值来更新记录 model class User db Model tablename users loginId db Column db String 128 nul
  • 批量删除文本文件中的一行?

    我正在绞尽脑汁地寻找一个简单的 DOS 批处理文件示例 它将删除数千个 txt 文件的第一行 并以原始文件名保存该文件 在另一个程序执行批处理之后 我必须在外部处理之后的每个文件的开头添加已删除的行 由 X Y Z 组成的文本字符串 您可以
  • 如何从谷歌脚本发送错误状态代码,如错误请求(400)?

    这是一个doPostGoogle App 内的函数返回一个你好世界信息 function doPost e return ContentService createTextOutput Hello World 现在假设我只想接受发布到此 G
  • Tomcat 部署 WAR 文件,但应用程序未在 docker 容器中启动

    我有一个带有注释的 Spring Framework MVC 应用程序 它包含所有依赖项 当tomcat docker容器启动时 它成功部署并打包WAR文件 但应用程序无法启动 Catalina 日志有成功的启动和部署行 tomcat 1
  • 为什么我的 strace 命令不适用于 Fish?

    我正在尝试启动一个可以使用 Bash 启动但不能使用 Fish 启动的命令 On Bash这运作良好 sudo strace f s3000 p pgrep f teams d p o tmp debug log strace Proces
  • 给 UITextView 一个可点击的 URL 链接

    嗨 我已经解决这个问题有一段时间了 我已经读了几篇文章 但我不明白如何制作可点击的UITextView在互联网上发送 这是我的代码 func transformText text String underlined Bool linkURL
  • 查找总和最接近目标的数字组合

    因此 我有浮点数的组合 还有一些目标值 我想看看这些浮点数的任何组合 允许重复 的总和是否可以尽可能接近目标值 现在我从简单开始 我想看看是否可以使用当前的浮点数列表来达到一个目标值 该总和最多可以偏离目标 0 5 我搜索了很多其他代码 但
  • React-native run-ios 找不到 iPhone X 模拟器 | XRPackageModel 9.0.omo

    我首先在 Expo 上启动了我的项目 然后将其退出 现在 我尝试运行命令react native run ios 但它返回此错误 CoreData 注释 无法在路径 Applications Xcode app Contents Appli
  • 如何在 SwiftUI 中将项目与 VStack 内的顶部对齐?

    我刚刚开始使用 swiftui 我面临着 ui 对齐问题 以下代码从图像顶部创建一个空间 VStack alignment leading Image item imageUrl resizable aspectRatio contentM
  • 如何在android中的数组适配器中设置文本颜色

    在我的应用程序中 我创建了一个列表视图并在列表中添加了一些文本 在我的编码部分中 文本被添加为数组适配器以具有复选框 在布局中 我为列表视图指定了白色 因此文本看起来非常暗淡 如何将文本颜色设置为黑色 我已将列表视图放置在布局文件中 并将文
  • 无需解码图像即可获取 JPEG 分辨率

    我试图在不解码文件的情况下获取 JPEG 图像的分辨率 我从互联网上获得了几个样本 但没有一个可以正常工作 似乎是这样因为manyJPEG 文件不是标准文件 但任何图形应用程序 Irfan PSP Firefox 等 都可以打开它们 JPE
  • 将承运人邮寄至 Bigcommerce Shipments API

    当物品通过 FedEx 运送时 我希望最终客户看到 FedEx 追踪号码 如何指明承运商 我是否需要在 Shipping method 字段中输入 FedEx 运输承运商只能在初始 POST 期间设置 而不能在后续 PUT 请求中设置 创建
  • Facebook/Twitter 风格照片网格风格布局[关闭]

    Closed 这个问题正在寻求书籍 工具 软件库等的推荐 不满足堆栈溢出指南 help closed questions 目前不接受答案 我正在尝试实现 facebook 风格的照片网格布局 我为此使用 angularjs 和 bootst