html中的瀑布流是什么
html中的瀑布流是什么
一、总结
1、瀑布流: 从左往右排列,哪一列现在的总高度最小,就优先排序把item(单元格)放在这一列.这样排完所有的单元格后,可以保证每一列的总高度都相差不大
2、看效果图:
3、瀑布流使用前置性条件:瀑布流很多有特定的使用前置性条件,不要乱用
二、瀑布流应用场景
首先,我们得明确一点,瀑布流肯定是有好处的,不然pinterest不可能那么火,蘑菇街在加入瀑布流元素之后PV暴涨。
我觉得瀑布流的好处大致有:提高发现好图的效率以及图片列表页极强的视觉感染力。
享受这些好处,就得考虑若干条前置性条件:
- 浏览行为缺乏特别明确的目的性,以“逛逛”“扫街”的心态为主
- 对复杂的信息索引无依赖性
- 用户以图片为首要检索对象,瀑布流页面的配文只是相当次要的辅助信息
- 竖图比例较高
- 图片平均质量较高
- 图片的风格气质趋于相似(也是Pinterest始终采取邀请制的原因)
单从以上六点来看,你们不喜欢瀑布流是对的,因为你们根本就不是这些网站的使用者。
三、html中的瀑布流是什么
-.什么是瀑布流?
瀑布流视图与UITableView类似,但是相对复杂一点.UITableView只有一列,可以有多个小节(section),每一个小节(section)可以有多行(row).
瀑布流呢,可以有多列,每一个item(单元格)的高度可以不相同,但是宽度必须一样.排列的方式是,从左往右排列,哪一列现在的总高度最小,就优先排序把item(单元格)放在这一列.这样排完所有的单元格后,可以保证每一列的总高度都相差不大,不至于,有的列很矮,有的列很高.这样就很难看了.
上面的数字,就是每个单元格的序号,可以看到item的排列顺序是个什么情况.
二.怎么实现一个瀑布流呢?
仿照UITableView的设计,我们要知道有多少个单元格,我们得问我们的数据源.有几列,问数据源.在某一个序号上是怎样的cell,问数据源.
某一个序号单元格的高度,问代理.单元格的列边距,行边距,整体的瀑布流视图的上下左右距离瀑布流视图所在的父视图的边距.这些,都问代理.
同时,我们也要在接口处对外提供方法,reloadData,当瀑布流视图要更新的时候可以调用.我们还要对外提供方法cellWidth,让外界可以直接知道每个单元格的高度是怎样的.同时,我们也要提供一个类似于UITableView的用来从缓存池取cell的方法.不然的话,屏幕每滑动到新的单元格地方,就要重新新建一个cell,这样当瀑布流总单元格多了之后,有多少单元格需要显示就创建多少次,这样是相当消耗性能的.所以要有缓存池,让外界在取cell时优先从缓存池取,缓存池取不到了,再来新建一个cell也不迟.UITableView就是这么做的.我们也要这么做.所以对外提供一个方法 -(WaterfallsViewCell *)dequeueReusableCellWithIdentifier:(NSString *)identifier;
仿照UITableView每个单元格是一种UITableViewCell,我们也可以定义一个瀑布流cell,继承于UIView即可,后期调用时可以随意定制cell的内容.
因为要实现的瀑布流,需要上下滚动,实际上是一个UIScrollView.所以,直接继承于UIScrollView.
所以有如下的接口定义
1.这个是瀑布流视图 WaterfallsView
// Copyright © 2015年 penglang. All rights reserved.
//
#import <UIKit/UIKit.h>
@class WaterfallsView,WaterfallsViewCell;
typedef enum {
WaterfallsViewMarginTypeTop,
WaterfallsViewMarginTypeLeft,
WaterfallsViewMarginTypeBottom,
WaterfallsViewMarginTypeRight,
WaterfallsViewMarginTypeColumn,
WaterfallsViewMarginTypeRow
}WaterfallsViewMarginType;
@protocol WaterfallsViewDataSource <NSObject>
@required
//- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section;
/**
* 有多少个cell
*/
-(NSUInteger)numberOfCells;
/**
* 在某一个序号的cell
*/
-(WaterfallsViewCell *)waterfallsView:(WaterfallsView *)waterfallsView cellAtIndex:(NSUInteger)index;
@optional
/**
* 有多少列
*/
-(NSUInteger)numberOfColumns;
@end
@protocol WaterfallsViewDelegate <UIScrollViewDelegate>
@optional
/**
* 某一个序号的单元格的高度
*/
-(CGFloat)waterfallsView:(WaterfallsView *)waterfallsView heightAtIndex:(NSUInteger)index;
/**
* 单元格与瀑布流视图的边界
*/
-(CGFloat)waterfallsView:(WaterfallsView *)waterfallsView margins:(WaterfallsViewMarginType)marginType;
/**
* 点击了某一个序号的单元格,怎么处理
*/
-(void)waterfallsView:(WaterfallsView *)waterfallsView didSelectCellAtIndex:(NSUInteger)index;
@end
@interface WaterfallsView : UIScrollView
@property (nonatomic, assign) id<WaterfallsViewDataSource> dataSource;
@property (nonatomic, assign) id<WaterfallsViewDelegate> delegate;
-(void)reloadData;
-(WaterfallsViewCell *)dequeueReusableCellWithIdentifier:(NSString *)identifier;
-(CGFloat)cellWidth;
@end
//这个是瀑布流视图的单元格视图 WaterfallsViewCell
// Copyright © 2015年 penglang. All rights reserved.
//
#import <UIKit/UIKit.h>
@interface WaterfallsViewCell : UIView
@property (nonatomic,copy, readonly) NSString *reuseIdentifier;
-(instancetype)initWithReuseIdentifier:(NSString *)reuseIdentifier;
@end
//cell的定义与实现
// WaterfallsViewCell.h
// Copyright © 2015年 penglang. All rights reserved.
//
#import <UIKit/UIKit.h>
@interface WaterfallsViewCell : UIView
@property (nonatomic,copy, readonly) NSString *reuseIdentifier;
-(instancetype)initWithReuseIdentifier:(NSString *)reuseIdentifier;
@end
// Copyright © 2015年 penglang. All rights reserved.
//
#import "WaterfallsViewCell.h"
//static NSUInteger count = 0;
@interface WaterfallsViewCell ()
@property (nonatomic,copy, readwrite) NSString *reuseIdentifier;
@end
// WaterfallsViewCell.m
@implementation WaterfallsViewCell
-(instancetype)initWithReuseIdentifier:(NSString *)reuseIdentifier{
if (self = [super init]) {
self.reuseIdentifier = reuseIdentifier;
}
//NSLog(@"创建cell%lu",(unsigned long)++count);
return self;
}
@end
然后,我们在控制器中就可以看该怎么使用定义的数据源方法
#import "ViewController.h"
#import "WaterfallsView.h"
#import "WaterfallsViewCell.h"
#define MyColor(r,g,b) [UIColor colorWithRed:(r)/255.0 green:(g)/255.0 blue:(b)/255.0 alpha:1.0]
#define MyColorA(r,g,b,a) [UIColor colorWithRed:(r)/255.0 green:(g)/255.0 blue:(b)/255.0 alpha:a]
@interface ViewController ()<WaterfallsViewDataSource,WaterfallsViewDelegate>
@property (nonatomic, weak) WaterfallsView *waterfallsView;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
//初始化瀑布流
WaterfallsView *waterfallsView = [[WaterfallsView alloc] initWithFrame:self.view.bounds];
waterfallsView.dataSource = self;
waterfallsView.delegate = self;
[self.view addSubview:waterfallsView];
_waterfallsView = waterfallsView;
}
#pragma mark - WaterfallsViewDataSource 数据源方法实现
-(NSUInteger)numberOfCells{
return 16;
}
-(NSUInteger)numberOfColumns{
return 3;
}
-(WaterfallsViewCell *)waterfallsView:(WaterfallsView *)waterfallsView cellAtIndex:(NSUInteger)index{
static NSString *ID = @"waterfallsCell";
WaterfallsViewCell *cell = [waterfallsView dequeueReusableCellWithIdentifier:ID];
if (cell == nil) {
cell = [[WaterfallsViewCell alloc] initWithReuseIdentifier:ID];
cell.backgroundColor = MyColor(arc4random_uniform(256), arc4random_uniform(256), arc4random_uniform(256));
UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(10, 10, 100, 20)];
label.textColor = [UIColor whiteColor];
label.tag = 10;
[cell addSubview:label];
}
UILabel *label = (UILabel *)[cell viewWithTag:10];
label.text = [NSString stringWithFormat:@"%lu",(unsigned long)index];
return cell;
}
#pragma mark - WaterfallsViewDelegate 代理方法实现
-(CGFloat)waterfallsView:(WaterfallsView *)waterfallsView heightAtIndex:(NSUInteger)index{
switch (index%3) {
case 0:
return 70.0;
break;
case 1:
return 90.0;
break;
case 2:
return 120.0;
break;
default:
return 150.0;
break;
}
}
-(CGFloat)waterfallsView:(WaterfallsView *)waterfallsView margins:(WaterfallsViewMarginType)marginType{
switch (marginType) {
case WaterfallsViewMarginTypeTop:
return 30;
case WaterfallsViewMarginTypeLeft:
case WaterfallsViewMarginTypeBottom:
case WaterfallsViewMarginTypeRight:
return 10;
break;
default:
return 5;
break;
}
}
-(void)waterfallsView:(WaterfallsView *)waterfallsView didSelectCellAtIndex:(NSUInteger)index{
NSLog(@"点击了第%lu个cell",(unsigned long)index);
}
@end
在控制器中这样用当然写代码很舒服了.可是我们只是对外提供了这些方法,很好用,而我们并没有实现它.
现在就来实现它的方法.
核心需要实现的方法其实就是
-(void)reloadData
在调用reloadData之时,需要重新将cell在瀑布流上显示出来,我们要确定每个单元格的位置,在相应的位置显示相应的我们设置好的单元格.
所以,我们要知道瀑布流视图的上\左\下\右边距各是多少,这些可以问数据源方法,我们可以在瀑布流视图实现里面给一个默认的间距.如果控制器没有实现边距数据源方法,就用我们默认设置的边距.
所以写一个辅助的方法,供内部调用
-(CGFloat)marginForType:(WaterfallsViewMarginType)type{
CGFloat margin = 0;
if ([self.delegate respondsToSelector:@selector(waterfallsView:margins:)]) {
margin = [self.delegate waterfallsView:self margins:type];
}else{
margin = WaterfallsViewDefaultMargin;
}
return margin;
}
这样就可以知道各种间距了.
同时,要知道总共有多少个cell,问数据源方法的实现者
有几列,也可以问数据源实现者,如果外界没实现,可以预先设置一个默认的列数.
所以有如下一些方法供内部调用,还有各种高度,等等.
-(CGFloat)marginForType:(WaterfallsViewMarginType)type{
CGFloat margin = 0;
if ([self.delegate respondsToSelector:@selector(waterfallsView:margins:)]) {
margin = [self.delegate waterfallsView:self margins:type];
}else{
margin = WaterfallsViewDefaultMargin;
}
return margin;
}
-(NSUInteger)columnsCount{
if ([self.dataSource respondsToSelector:@selector(numberOfColumns)]) return [self.dataSource numberOfColumns];
return WaterfallsViewDefaultColumnCount;
}
-(CGFloat)cellHeightAtIndex:(NSUInteger)index{
if ([self.delegate respondsToSelector:@selector(waterfallsView:heightAtIndex:)]) return [self.delegatewaterfallsView:self heightAtIndex:index];
return WaterfallsViewDefaultCellHeight;
}
reloadData方法我就直接放出来了
-(void)reloadData{
[self.cellFrames removeAllObjects];
[self.displayingCells removeAllObjects];
CGFloat topM = [self marginForType:WaterfallsViewMarginTypeTop];
CGFloat leftM = [self marginForType:WaterfallsViewMarginTypeLeft];
CGFloat bottomM = [self marginForType:WaterfallsViewMarginTypeBottom];
CGFloat rowM = [self marginForType:WaterfallsViewMarginTypeRow];
CGFloat columnM = [self marginForType:WaterfallsViewMarginTypeColumn];
NSUInteger totalCellCount = [self.dataSource numberOfCells];
NSUInteger totalColumnCount = [self columnsCount];
CGFloat cellW = [self cellWidth];
//这个数组用来存放每一列的最大的高度
CGFloat maxYOfColumn[totalColumnCount];
for (int i = 0; i < totalColumnCount; i++) {
maxYOfColumn[i] = 0;
}
int cellColumn;
for (int i = 0; i < totalCellCount; i++) {
CGFloat cellH = [self cellHeightAtIndex:i];
cellColumn = 0;
for (int j = 1; j < totalColumnCount; j++) {
if (maxYOfColumn[j] < maxYOfColumn[cellColumn]) {
cellColumn = j;
}
}
CGFloat cellX = leftM + (cellW + columnM) * cellColumn;
CGFloat cellY;
if (maxYOfColumn[cellColumn] == 0) {
cellY = topM;
}else{
cellY = maxYOfColumn[cellColumn] + rowM;
}
CGRect cellFrame = CGRectMake(cellX, cellY, cellW, cellH);
[self.cellFrames addObject:[NSValue valueWithCGRect:cellFrame]];
maxYOfColumn[cellColumn] = CGRectGetMaxY(cellFrame);
}
CGFloat maxYOfWaterfallsView = 0;
for (int i = 0; i < totalColumnCount; i++) {
if (maxYOfColumn[i] > maxYOfWaterfallsView) maxYOfWaterfallsView = maxYOfColumn[i];
}
maxYOfWaterfallsView += bottomM;
self.contentSize = CGSizeMake(0, maxYOfWaterfallsView);
}
以上只是把所有cell的frame求出来了,用数组保存,这样就可以知道每一个cell放在瀑布流上的哪个地方.最终得到最大的cell的高度后,就可以设置瀑布流视图的contentSize.不然无法滚动.但是,这还不够的,
我们要将cell在瀑布流视图上显示出来.
这个操作,是在layoutSubviews方法中实现
-(void)layoutSubviews{
[super layoutSubviews];
//回滚,从后往前遍历cell
if (self.scrollDirection == WaterfallsViewScrollDirectionRollback) {
for (int i = (int)[self.dataSource numberOfCells] - 1; i >=0 ; i--) {
[self handleCellWithIndex:i];
}
}else{ //往前滑动更多的cell,一般情况,从前往后遍历cell
for (int i = 0; i < [self.dataSource numberOfCells]; i++) {
[self handleCellWithIndex:i];
}
}
lastContentOffsetY = self.contentOffset.y;
NSLog(@"displaying Cells Count :%lu",(unsigned long)self.displayingCells.count);
}
//因为向前滚,序号小的cell要先消失,在后面的序号大的cell要新显示出来.所以,从序号小的遍历起.不在屏幕上的cell可以先回收到缓存池中,后面要显示的的cell就可以从缓存池中去拿了.
//同理,回滚的话,序号大的cell要先消失,在前面的序号小的cell要新显示出来,所以,从序号大的遍历起,不在屏幕上的cell先回收,前面新显示的cell就可以从缓存池中去拿了.
滚动方向的枚举定义,以及怎么获取滚动方向,如下
//滚动方向的枚举定义
typedef enum{
WaterfallsViewScrollDirectionForward,
WaterfallsViewScrollDirectionRollback
}WaterfallsViewScrollDirection;
//获得当前的滚动方向
-(WaterfallsViewScrollDirection)scrollDirection{
if (self.contentOffset.y < lastContentOffsetY) return WaterfallsViewScrollDirectionRollback;
return WaterfallsViewScrollDirectionForward;
}
//处理某一个序号的cell,从保存的cellFrames数组中获得这个序号的cell的frame,先尝试看当前cell有没有在显示在屏幕上,在displayingCells字典中能不能拿到
//如果当前的cell有在屏幕上显示,如果cell在displayingCells字典中没有拿到,问数据源方法要.然后给cell设置我们事先就算好的frame,把它加入到displayingCells字典中,
//同时加入到瀑布流视图上.
//如若不在屏幕上,并且displayingCells字典中能够取到,说明刚刚它在屏幕上显示着呢,现在要从屏幕上离开了,那就要把它从displayingCells字典中移除,同时从瀑布流视图移除,
//可以加入缓存池中
-(void)handleCellWithIndex:(NSUInteger)index{
CGRect cellFrame = [self.cellFrames[index] CGRectValue];
WaterfallsViewCell *cell = self.displayingCells[@(index)];
if ([self isOnScreen:cellFrame] == YES) {
if (cell == nil) {
cell = [self.dataSource waterfallsView:self cellAtIndex:index];
cell.frame = cellFrame;
self.displayingCells[@(index)] = cell;
[self addSubview:cell];
}
}else{
if (cell != nil) {
[self.displayingCells removeObjectForKey:@(index)];
[cell removeFromSuperview];
[self.reusableCells addObject:cell];
}
}
}
//是否在屏幕上,如果cell的y值最大处,比瀑布流视图的contentOffset.y小,说明在显示区域的上部分.如果cell的y值最小处,比瀑布流视图显示的最大y值的地方还大,说明说明在显示区域的下部分.
//这两种都不在屏幕上呢.其他情况,都是在屏幕上的
-(BOOL)isOnScreen:(CGRect)cellFrame{
if (CGRectGetMaxY(cellFrame) <= self.contentOffset.y) return NO;
if (cellFrame.origin.y >= self.contentOffset.y + self.frame.size.height) return NO;
return YES;
}
/**
* 供外界调用取可重复利用cell
*/
//外界调用,当控制器中实现数据源方法
-(WaterfallsViewCell *)waterfallsView:(WaterfallsView *)waterfallsView cellAtIndex:(NSUInteger)index;
时,先调用这个方法,从缓存池中取cell,不同结构的cell可以用不同的identifier以示区别.从缓存池中取cell,也是根据cell的identifier来取.
当缓存池中取到cell了之后,要将cell从缓存池中移除,表示这个cell已经被利用了,取不到cell,说明这个identifier标示的cell已经被用完了.外面需要自己新建cell.这个就是cell的根据标识重复利用cell的原理.
/**
* 在某一个序号的cell
*/
-(WaterfallsViewCell *)dequeueReusableCellWithIdentifier:(NSString *)identifier{
__block WaterfallsViewCell *cell = nil;
[self.reusableCells enumerateObjectsUsingBlock:^(WaterfallsViewCell *reusableCell, BOOL *stop) {
if ([reusableCell.reuseIdentifier isEqualToString:identifier]) {
cell = reusableCell;
*stop = YES;
}
}];
if (cell != nil) {
[self.reusableCells removeObject:cell];
}
// NSLog(@"缓存池剩余的cell个数:%ld",self.reusableCells.count);
return cell;
}
//当点击了某个cell之后,需要有所响应.代理方法中拿到了点击的cell之后,即可做相应的处理.
所以这里实现了touchesBegan: withEvent:方法
//通过遍历displayingCells中的所有cell,如果触摸发生的地方正好在其中的某一个cell中,把cell的序号通过代理方法传出去,外界就知道了某个cell被点击了,自己实现相应的方法,即可做出相应的反应.
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
UITouch *touch = [touches anyObject];
CGPoint pointInView = [touch locationInView:self];
__block NSInteger selectedIndex = -1;
[self.displayingCells enumerateKeysAndObjectsUsingBlock:^(NSNumber *index, WaterfallsViewCell *cell, BOOL * stop) {
if (CGRectContainsPoint(cell.frame, pointInView) == YES) {
selectedIndex = [index unsignedIntegerValue];
*stop = YES;
}
}];
if (selectedIndex >= 0) {
if ([self.delegate respondsToSelector:@selector(waterfallsView:didSelectCellAtIndex:)]) {
[self.delegate waterfallsView:self didSelectCellAtIndex:selectedIndex];
}
}
}
演示图片如下:
源码下载地址:
https://github.com/GudTeach/WaterfallsView
html中的瀑布流是什么的更多相关文章
- 在 JQuery Mobile 中实现瀑布流图库布局
先来看在Windows系统的1080P显示器中显示的效果: 这个整合方式几乎没有现存的实例,是自己总结出来的方法,在此记录下来. 首先访问Masonry官网下载masonry.pkgd.min.js: ...
- 在asp.net中使用瀑布流,无限加载
页面中代码 <%@ Page Language="C#" AutoEventWireup="true" CodeBehind="WebForm1 ...
- Angular实现瀑布流的库angular-deckgrid
一. 安装 bower install --save angular-deckgrid 添加代码到你的HTML 添加到你的angular模块中: angular.module('your.module ...
- 用 jQuery Masonry 插件创建瀑布流式的页面(转)
瀑布流式的页面,最早我是在国外的一个叫 Pinterest 的网站上看到,这个网站爆发,后来国内的很多网站也使用了这种瀑布流方式来展示页面(我不太喜欢瀑布流这个名字). 我们可以使用 jQuery 的 ...
- 用 jQuery Masonry 插件创建瀑布流式的页面
瀑布流式的页面,最早我是在国外的一个叫 Pinterest 的网站上看到,这个网站爆发,后来国内的很多网站也使用了这种瀑布流方式来展示页面(我不太喜欢瀑布流这个名字). 我们可以使用 jQuery 的 ...
- jQuery实现瀑布流(pc、移动通用)
使用 jQuery 的 Masonry 插件来实现这种页面形式 1,分别下载 jQuery 与 Masonry ,然后把他们都加载到页面中使用. 加载代码: <script src=" ...
- jquery实现简单瀑布流代码
测试环境:ie8 ff13.0.1 chrome22 可以将分页获取的内容依次填入四个div中,瀑布流的分页可以以多页(比如5页)为单位二次分页,这样可以减少后台算法的复杂度 <!DOCTYP ...
- 前端之masonry(图片瀑布流插件)
加载代码: 1 2 <script src="http://libs.baidu.com/jquery/1.8.3/jquery.min.js"></script ...
- 飞流直下的精彩 -- 淘宝UWP中瀑布流列表的实现
在淘宝UWP中,搜索结果列表是用户了解宝贝的重要一环,其中的图片效果对吸引用户点击搜索结果,查看宝贝详情有比较大的影响.为此手机淘宝特意在搜索结果列表上采用了2种表现方式:一种就是普通的列表模式,而另 ...
随机推荐
- HIVE的几种优化
5 WAYS TO MAKE YOUR HIVE QUERIES RUN FASTER 今天看了一篇[文章] (http://zh.hortonworks.com/blog/5-ways-make-h ...
- P3507 [POI2010]GRA-The Minima Game
题目描述 Alice and Bob learned the minima game, which they like very much, recently. The rules of the ga ...
- intellij idea 13
mac版 http://pan.baidu.com/s/1c0zjWU8 intellij idea 编辑器之于程序员,犹如鞋之于女人.有的女人赤脚都漂亮,性感. 有的女人赤身都没人看.程序员亦如此. ...
- 简单STL笔记
想了好久,还是把自己了解的先整理一下吧,毕竟老是忘,这里主要简单介绍三种容器 set,queue,vector,以及栈 stack,队列queue 的简单用法.一.set 在set中,效率比vecto ...
- Ubuntu源配置
一.图形界面配置 新手推荐使用图形界面配置: 系统工具 -> 软件和更新-> Ubuntu软件-> 下载自:-> 其他站点 点击 选择最佳服务器(将通过连接测试确定最佳镜像) ...
- Flex XML/XMLList 常用操作
1 XML.XMLList操作 Flex对xml提供了很多强大而灵活的操作.相对于其他语言,flex对xml的格式要求不那么苛刻,只要符合基本格式语法的字符串,flex能非常简单的转换成x ...
- NumPy基础入门学习
对于习惯使用了MATLAB的用户而言,学习NumPy这个python工具包付出的成本应该是不大的. NumPy的基本的object是多维数组,是一个有同样类型的数字等构成的一张表格,能够通过元组进行索 ...
- 77.深入理解nodejs中Express的中间件
转自:https://blog.csdn.net/huang100qi/article/details/80220012 Express是一个基于Node.js平台的web应用开发框架,在Node.j ...
- 59.C++与正则表达式
regex_match 整个字符串是否匹配 (通过cmatch存储匹配的结果),match[0]代表整个匹配序列,match[1]代表第1个匹配后的子序列,match[2]代表第2个匹配后的子序列 代 ...
- js对象基础写法练习
<!doctype html> <html lang="en"> <head> <meta charset="UTF-8&quo ...