飞流直下三千尺!
文章目录
- 【Java】表白墙终章-飞流直下的“甜言蜜语”-瀑布流式布局
- 1. 效果前后对比
- 2. 瀑布流式布局原理思想
- 3. 约定前后端接口
- 4. 后端代码
- 4.1 修改Love类的定义
- 4.2 修改doPost方法
- 4.3 修改save方法
- 4.4 修改doGet方法
- 4.5 修改load方法
- 5. 前端瀑布流实现
- 5.1 规定“甜言蜜语”的层级结构
- 5.2 css修饰
- 5.2.1 底板div:a
- 5.2.2 div:box
- 5.2.3 div:pic
- 5.2.4 div:T
- 5.2.5 div:Tleft
- 5.2.6 div:Tright
- 5.2.7 div:B
- 5.3 JS实现瀑布流式布局
- 5.3.1 修改getLoves函数
- 5.3.2 waterFall函数-瀑布流排列
- 5.4 定时刷新
- 6. 测试
改进之前:
- 丑
- 丑
- 用户之间无差别
- 消息先来先到
改进后:
- 美
- 美
- 添加导航栏左上角的身份显示(头像 + 用户名)
- 采用瀑布流的方式去排列消息
- 消息以卡片的形式显示(头像 + 用户名 + 日期 + 甜言蜜语)
- 先来后到
- 最新发布的排在最前
流程:
动图演示:
越界了怎么办?
细节:
原因:
之前的“甜言蜜语”只有简单的正文部分,而现在,它需要有更多的属性:
再原有基础上,应该增加三个属性
所以,我们需要数据库的一张新的表:
因此,我们需要更改后端代码关于json构造的部分!
查询的表
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;
}
}
注意:
当然,客户端那里发过来的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的空报文
}
}
然后就是调用save方法,将love对象载入数据库
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();
}
}
@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方法更改了
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;
}
接下来就是本文重点的前端的瀑布流布局实现了
T为信息头,头像显示、用户名显示以及日期显示
B为正文,“甜言蜜语”显示
T的左侧显示头像,右侧显示用户名和日期
在pursue.css里增加
#a {
height: calc(100% - 66.67px);
width: 100%;
/* overflow: auto; */
position: relative;
}
.box {
/* 用padding,而不是margin,因为我们计算的时候,我们不希望算上外边距(盒子大小,js获得高度的时候还得加上margin,复杂多了) */
padding: 15px 0 0 15px;
height: auto;
/* 浮动属性 */
float: left;
}
只设置上,与左的内边距为15px
设置浮动属性:float
.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);
}
.pic .T {
width: 150px;
height: 30px;
display: flex;
justify-content: space-between;
}
消息头的width撑破了限制无所谓~
设置为弹性布局,并使子元素space-between(根据实际留出间隙)
.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;
}
.Tright {
width: 110px;
height: 30px;
}
.Tright h4 {
line-height: 15px;
font-size: 15px;
color: rgb(128, 128, 128);
}
.B {
width: 150px;
height: auto;
word-wrap: break-word;
padding-right: 10px;
padding-top: 5px;
}
function getLoves() {
jQuery.ajax({
type: "GET",
url: "love",
success: function (body) {
var boxArray = [];
//body就是数组
for (var word of body) {
var result =
"
"
+
word.from +
"想对" +
word.to +
"说“" +
word.love +
"”";
var aB = jQuery("");
aB.attr("class", "B");
aB.append(result);
var aTleft = jQuery("");
aTleft.attr("class", "Tleft");
console.log(word.image);
aTleft.css("background-image", "url(" + word.image + ")");
var aTright = jQuery("");
aTright.attr("class", "Tright");
aTright.append(
""
+ word.username + ": "
+ word.date + ""
);
var aT = jQuery("");
aT.attr("class", "T");
aT.append(aTleft);
aT.append(aTright);
var aPic = jQuery("");
aPic.attr("class", "pic");
aPic.append(aT);
aPic.append(aB);
var aBox = jQuery("");
aBox.attr("class", "box");
aPic.appendTo(aBox);
aBox.appendTo(jQuery("#a"));
boxArray.push(aBox);
}
waterFall(boxArray);
},
});
}
构造box的时候要细心哦,要契合我们的层级结构!
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;
}
}
}
瀑布流布局完成!
function refresh() {
jQuery("#a").empty();
getLoves();
}
setInterval(refresh, 5000);//5000ms
现在我们自动刷新的时机为:
手机端:
电脑端:
文章到此结束!谢谢观看
可以叫我 小马,我可能写的不好或者有错误,但是一起加油鸭!表白墙完结啦啦啦啦啦✿✿ヽ(°▽°)ノ✿
可能以后会追加一系列的功能,敬请期待
不久后将上线!
本文代码位置:六月代码/showLove/src/main · 游离态/马拉圈2023年6月 - 码云 - 开源中国 (gitee.com)