【JavaEE】表白墙终章-飞流直下的“甜言蜜语”-瀑布流式布局

本文涉及的产品
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS MySQL,高可用系列 2核4GB
简介: 瀑布流式布局原理思想

【JavaEE】表白墙终章-飞流直下的“甜言蜜语”-瀑布流式布局


1. 效果前后对比


改进之前:


用户之间无差别

消息先来先到

2a2b35b3795844a8b3601edb27800a0e.png


改进后:


添加导航栏左上角的身份显示(头像 + 用户名)

采用瀑布流的方式去排列消息

消息以卡片的形式显示(头像 + 用户名 + 日期 + 甜言蜜语)

先来后到

最新发布的排在最前

75829dc98e64450dbb1ed5ad5e7b16d5.png


2. 瀑布流式布局原理思想


瀑布流布局的前提就是,待排序的div元素都是等宽不限高的~


436ad69613054603a61a5482b2a5719a.png

流程:


获取一个div元素盒子的宽

获取底板(父元素)的宽

计算底板最多排列的列数:n

将前四个div元素排在首行

从已排列的每一列中找到最矮的一列,将接下来的元素安插上去(通过绝对定位,即通过提供的坐标,安插到底板的固定位置)

重复5操作,直到全部排序好

动图演示:


160784788e0b4d5c92c0d2b21e66b75e.gif


越界了怎么办?


加滚动条就行了~

细节:


滚动条不能加在底板上,应该加在底板的至少上一级元素

原因:


刚才在计算列数的时候,是没有减掉滚动条的宽度的,也就是说列数可能是多了1列,导致出现了“水平滚动条”,也就是说“竖直滚动条”挤压了原有元素,会导致一些排版重叠问题!


3. 约定前后端接口


之前的“甜言蜜语”只有简单的正文部分,而现在,它需要有更多的属性:

1fb5b35f74514b6da333ff1a2417fb4c.png


再原有基础上,应该增加三个属性


image,头像(与用户名绑定)

date,日期(可按时间逆排序)

username,用户名(确认用户信息)

所以,我们需要数据库的一张新的表:


d362157893cb4a31b2cc38ce101aab36.png


因此,我们需要更改后端代码关于json构造的部分!


4. 后端代码

7a16b51ef028454788f439e47c6c4a89.png


查询的表


message => vindicate

注意修改!


4.1 修改Love类的定义


class Love {
    public String from;
    public String to;
    public String love;
    public String username;
    public String image;
    public String date;
    public Love() {
        //没有这个一定不行!!!
        //因为后续json构建Love对象需要用到无参的构造方法
    }
    public Love(String from, String to, String love, String username, String image, Timestamp date) {
        this.from = from;
        this.to = to;
        this.love = love;
        this.username = username;
        this.image = image;
        String strn = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(date);
        this.date = strn;
    }
}


注意:


jdbc操作数据库,既可以将满足格式的 String类或者 Timestamp类载入数据库datetime类型字段中

jdbc操作数据库,将 datetime类型的字段提取出来的时候,只能提取为 Date或者 Timestamp类(本文选择后者)

145f4f260e714a39b84ce662aec7a694.png


这样子,我们在表白墙里看到的就是日期格式了~

当然,日期还有格式还有很多种,这个类还能满足“yyyy/MM/dd HH-mm-ss”、“HH/mm/ss yyyy.MM.dd”等等等…

但是这里,由于后续我将数据载入数据库是用的严格符合格式的字符串的方式,所以选择“yyyy-MM-dd HH:mm:ss”的格式~

还有更多的类表达日期的格式,感兴趣的可以去了解了解,但是我建议,用的时候查就行了


4.2 修改doPost方法


客户端发来的post请求,将客户端json格式的body转化为Love类,载入数据库

当然,客户端那里发过来的post正文是不包含,image、username和date的,所以构造的Love类对象的这三个属性的值为null~


@WebServlet("/love")
public class ShowLove extends HttpServlet {
    private ObjectMapper objectMapper = new ObjectMapper();
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        Love love = objectMapper.readValue(req.getInputStream(), Love.class);
        HttpSession session = req.getSession();
        Timestamp time = new Timestamp(System.currentTimeMillis());
        String strn = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(time);
        love.date = strn;
        love.username = (String)session.getAttribute("username");
        love.image = Save.getImage(love.username);
        save(love);
        //默认返回的就是200的空报文
    }
}

6630dc79f1174e4f9ae77116a1d9d429.png




然后就是调用save方法,将love对象载入数据库


4.3 修改save方法


private void save(Love love){
    DataSource dataSource = new MysqlDataSource();
    ((MysqlDataSource)dataSource).setUrl("jdbc:mysql://127.0.0.1:3306/Loves?characterEncoding=utf8&useSSL=false");
    ((MysqlDataSource)dataSource).setUser("root");
    ((MysqlDataSource)dataSource).setPassword("mmsszsd666");//这是俺的微信号,欢迎添加,相互学习!
    try {
        Connection connection = dataSource.getConnection();
        String sql = "insert into vindicate values(?, ?, ?, ?, ?, ?);";
        PreparedStatement preparedStatement = connection.prepareStatement(sql);
        preparedStatement.setString(1, love.from);
        preparedStatement.setString(2, love.to);
        preparedStatement.setString(3, love.love);
        preparedStatement.setString(4, love.image);
        preparedStatement.setString(5, love.date);
        preparedStatement.setString(6, love.username);
        preparedStatement.executeUpdate();
        preparedStatement.close();
        connection.close();
    } catch (SQLException e) {
        e.printStackTrace();
    }
}


41e36dae0faa4e41bfd072d0afda0808.png


4.4 修改doGet方法


@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    //如果有输入操作互动的操作,在这里refresh是不合理的,因为写着写着就刷新了,体验不好
    // 所以在这里设置refresh没用!
    //转换为json字符串!
    List<Love> list =  load();
    String result = objectMapper.writeValueAsString(list);
    resp.setContentType("application/json; charset=utf8");
    resp.getWriter().write(result);
}


这段方法没有更改,主要是load方法更改了


4.5 修改load方法


private List<Love> load(){
    DataSource dataSource = new MysqlDataSource();
    ((MysqlDataSource)dataSource).setUrl("jdbc:mysql://127.0.0.1:3306/Loves?characterEncoding=utf8&useSSL=false");
    ((MysqlDataSource)dataSource).setUser("root");
    ((MysqlDataSource)dataSource).setPassword("mmsszsd666");//这是俺的微信号,欢迎添加,相互学习!
    List<Love> list = new ArrayList<>();
    try {
        Connection connection = dataSource.getConnection();
        String sql = "select * from vindicate order by date desc;";
        PreparedStatement preparedStatement = connection.prepareStatement(sql);
        //这里的Set并不是,对象为Love的Set集合,而是一个迭代器!
        ResultSet set = preparedStatement.executeQuery();
        //迭代他(是next方法而不是hasnext)
        while(set.next()) {
            String from = set.getString("from");
            String to = set.getString("to");
            String love = set.getString("love");
            String username = set.getString("username");
            if(username.length() > 9) {
                username = username.substring(0, 9) + "...";
            }
            String image = set.getString("image");
            Timestamp date = set.getTimestamp("date");
            list.add(new Love(from, to, love, username, image, date));
        }
        set.close();
        preparedStatement.close();
        connection.close();
    } catch (SQLException e) {
        e.printStackTrace();
    }
    return list;
}


c676ee879edc47b1a7986b39ee9428da.png


接下来就是本文重点的前端的瀑布流布局实现了


5. 前端瀑布流实现

3ce43727269e4e039c4cfc1fb680dcac.png


5.1 规定“甜言蜜语”的层级结构


a22684ff75a14784b2904750a573b136.png

8ebd7de0dc98497ebf0f2e8a4e06d6b3.png


pic的div块,就是我们的后端数据载入的对象了~



0720d775a44e4815bd3e73691d741ef6.png

ed91c98f3f0840ed87a9bfe80ff76409.png


T为信息头,头像显示、用户名显示以及日期显示


B为正文,“甜言蜜语”显示


7ce3b93824f64f5a97ca1c098434057b.png


7775d20813c945bfaca09664a5516594.png



T的左侧显示头像,右侧显示用户名和日期


5.2 css修饰


在pursue.css里增加


5.2.1 底板div:a


#a {
    height: calc(100% - 66.67px);
    width: 100%;
    /* overflow: auto; */
    position: relative;
}


将滚动条注释掉,交给其上一级:article


bbf37c0aac1b4f67b8178c3554a5cab4.png

设置属性position为:relative,相对定位 => 与绝对定位对应,如果没有这个属性,我们提供的坐标,其是按照整个页面来定位的

而设置这个属性后,是以此div来定位的(坐标系平移,原点改变)


5.2.2 div:box


这个盒子代表了一条信息的占位空间,主要作用是设立内边距,与其他信息块分离

这个div极为主要

.box {
    /* 用padding,而不是margin,因为我们计算的时候,我们不希望算上外边距(盒子大小,js获得高度的时候还得加上margin,复杂多了) */
    padding: 15px 0 0 15px;
    height: auto;
    /* 浮动属性 */
    float: left;
}


只设置上,与左的内边距为15px


padding:top right bottom left(顺时针)

设置浮动属性:float


子元素div是块级元素,独占一行,设置这个属性后,取消块级性质,并且左排列,越界换行,但是这并不能起到瀑布流的效果

如果接下来的元素可以安插到其中一列,且总长度超过其原最长高,则会补充到对应列

若1不满足,则重起一行


5.2.3 div:pic


.pic {
    background-color: rgb(255, 255, 255);
    width: 150px;
    height: auto;
    /* 边距 */
    padding: 10px;
    border: 1px solid #ccc;
    border-radius: 5px;
    /* 阴影 */
    box-shadow: 0 0 5px rgb(0, 0, 0);
}


底色为白

宽度固定为150px

高不限制,根据子元素决定

设置内边距为10px,不会导致子元素紧贴边界

设置灰色边框和黑色阴影~


5.2.4 div:T


.pic .T {
    width: 150px;
    height: 30px;
    display: flex;
    justify-content: space-between;
}


消息头的width撑破了限制无所谓~


高度固定为30px => 头像为30px × 30px

设置为弹性布局,并使子元素space-between(根据实际留出间隙)


并且T的左和右是不换行的~


5.2.5 div:Tleft


.Tleft {
    width: 30px;
    height: 30px;
    background-image: url(https://img1.baidu.com/it/u=4205447136,2730860147&fm=253&fmt=auto&app=138&f=JPEG?w=300&h=300);
    border: 1px solid rgb(0, 0, 0);
    border-radius: 15px;
    background-repeat: no-repeat;
    background-position: center center;
    background-size: cover;
}


设置半径为15px的圆形

图片为默认头像

黑色边框

设置属性是的图片充满圆


5.2.6 div:Tright


.Tright {
    width: 110px;
    height: 30px;
}
.Tright h4 {
    line-height: 15px;
    font-size: 15px;
    color: rgb(128, 128, 128);
}


宽度设置为110px,不会导致文字和图片太紧凑

高度设置为30px~

h4标签设置为15px(因为有两个),刚好与div:T等高(字为灰色)

日期可能会超出限制,不过无所谓,在正文的一开始加个换行错开就行了~


5.2.7 div:B


.B {
    width: 150px;
    height: auto;
    word-wrap: break-word;
    padding-right: 10px;
    padding-top: 5px;
}


宽度为150px

高度由正文决定

根据单词超出限制换行

没有这个设置会导致B的高度始终为一个文字的高度

微调文字布局


5.3 JS实现瀑布流式布局


5.3.1 修改getLoves函数


由于前后端交互接口的改变,这里也需要做一些调整~

function getLoves() {
    jQuery.ajax({
        type: "GET",
        url: "love",
        success: function (body) {
            var boxArray = [];
            //body就是数组
            for (var word of body) {
                var result =
                    "<br/><h2>" +
                    word.from +
                    "想对" +
                    word.to +
                    "说“" +
                    word.love +
                    "”</h2>";
                var aB = jQuery("<div></div>");
                aB.attr("class", "B");
                aB.append(result);
                var aTleft = jQuery("<div></div>");
                aTleft.attr("class", "Tleft");
                console.log(word.image);
                aTleft.css("background-image", "url(" + word.image + ")");
                var aTright = jQuery("<div></div>");
                aTright.attr("class", "Tright");
                aTright.append(
                    "<h4>" + word.username + ":</h4> <h4>" + word.date + "</h4>"
                );
                var aT = jQuery("<div></div>");
                aT.attr("class", "T");
                aT.append(aTleft);
                aT.append(aTright);
                var aPic = jQuery("<div></div>");
                aPic.attr("class", "pic");
                aPic.append(aT);
                aPic.append(aB);
                var aBox = jQuery("<div></div>");
                aBox.attr("class", "box");
                aPic.appendTo(aBox);
                aBox.appendTo(jQuery("#a"));
                boxArray.push(aBox);
            }
            waterFall(boxArray);
        },
    });
}



be2f72bc77384c0a9484852f93ba2b1c.png

构造box的时候要细心哦,要契合我们的层级结构!


5.3.2 waterFall函数-瀑布流排列


function waterFall(boxes) {
    // 将a下的全部box取出来
    var oParent = jQuery("#a");
    // 计算显示的列数(页面的宽/box的宽)
    var oBoxWeight = boxes[0].outerWidth(); // console.log(oBoxWeight);
    var cols = Math.floor(jQuery("#a").width() / oBoxWeight);
    // 设置a的宽度
    oParent.css("width", cols * oBoxWeight + "px");
    oParent.css("margin", "0 auto");
    // 存放第该列(位置正确摆放的多个div高度)的高度的数组
    // 第一次,则是第一行每个div的高度
    var hArr = [];
    for (var i = 0; i < boxes.length; i++) {
        if (i < cols) {
            hArr.push(boxes[i].outerHeight());
        } else {
            var minH = Math.min.apply(null, hArr); //top
            var index = getMinhIndex(hArr, minH);
            boxes[i].attr(
                "style",
                "position: absolute; top: " +
                minH +
                "px; left: " +
                oBoxWeight * index +
                "px;"
            );
            //处理盒子重叠
            hArr[index] += boxes[i].outerHeight();
        }
    }
    // left -> 左边三个div的宽,top-> 最小高
}
function getMinhIndex(arr, val) {
    for (var i = 0; i < arr.length; i++) {
        if (arr[i] == val) {
            return i;
        }
    }
}


04d635e2fd4e4619926d4d6a8840227e.png


3319f1771d5c40f5bc15a14833ec3e9a.png

瀑布流布局完成!


5.4 定时刷新


我们可以通过setInterval函数,设置一个函数多久定时调用一次

function refresh() {
    jQuery("#a").empty();
    getLoves();
}
setInterval(refresh, 5000);//5000ms


现在我们自动刷新的时机为:


点击发送

定时刷新


6. 测试


手机端:

751b0ff7839e4a87ab7017e0ddb32beb.png

电脑端:

0163d6f44ad34547b3f23cd9a10e95de.gif

相关实践学习
如何在云端创建MySQL数据库
开始实验后,系统会自动创建一台自建MySQL的 源数据库 ECS 实例和一台 目标数据库 RDS。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
目录
相关文章
|
Java Python
星际争霸之小霸王之小蜜蜂(四)--事件监听-让小蜜蜂动起来
星际争霸之小霸王之小蜜蜂(四)--事件监听-让小蜜蜂动起来
|
3月前
|
前端开发 开发者 UED
Web前端布局的救赎:掌握清除浮动的艺术,告别布局混乱!
【8月更文挑战第23天】在Web前端开发中,浮动(float)是一种常用的CSS布局技术,但会导致父元素高度塌陷。清除浮动至关重要,常用方法包括:使用额外的清除元素、伪元素、`overflow`属性、`flexbox`布局、`grid`布局以及`clearfix`方法。每种方法各有优缺点,适用于不同场景。随着新技术的发展,开发者应持续学习,选择合适的方法以确保布局稳定性和提升用户体验。
41 0
|
3月前
|
前端开发 容器
揭秘Web前端布局秘籍:浮动,那个让你又爱又恨的布局神器,你真的了解它吗?
【8月更文挑战第23天】在Web前端设计中,浮动是一种关键布局技术,能让元素在文档流中灵活移动,实现文本环绕图片、多列布局等效果。元素通过CSS的 `float` 属性脱离正常文档流并移动到容器边缘,后续非浮动内容则围绕该元素排列。浮动可用于多列布局、导航菜单及图文混排。需注意清除浮动以避免布局问题,并处理可能导致的父元素高度塌陷。
51 0
|
5月前
鬼刀画风扁平化粒子炫动引导页美化源码
鬼刀画风扁平化粒子炫动引导页美化源码
42 5
鬼刀画风扁平化粒子炫动引导页美化源码
|
12月前
Axure如何做到屏幕自适应
Axure如何做到屏幕自适应
389 0
|
前端开发 程序员
第五节 关于浮动和清除浮动的解说,以及两个大坑不要踩
第五节 关于浮动和清除浮动的解说,以及两个大坑不要踩
114 0
|
JSON 缓存 前端开发
【JavaEE】简单前后端分离小项目-表白墙
【JavaEE】简单前后端分离小项目-表白墙
101 0
|
图形学
如何做出好看的粒子效果
嗨!大家好,我是小蚂蚁。 微信小游戏制作工具提供了简单的粒子插件,使用起来简单明了(如果你用过Unity的粒子组件就知道这个有多简单明了了),虽然功能相对简单,可设置的属性也有限,但是我们仍然能够用它在游戏中做出漂亮的效果。 比如说在彩虹星球大冒险中,所有的爆炸都是使用的粒子效果来实现的。
133 0
|
编解码 前端开发 JavaScript
手摸手带你实现一个时间轴组件
本文给大家带来一个时间轴的组件开发教程
725 0
|
前端开发 容器
#yyds干货盘点# 前端歌谣的刷题之路-第一百四十三题-双列布局-浮动
#yyds干货盘点# 前端歌谣的刷题之路-第一百四十三题-双列布局-浮动
77 0
#yyds干货盘点# 前端歌谣的刷题之路-第一百四十三题-双列布局-浮动