【笔记】如何在Hexo里实现类似MomoTalk效果

网页版MomoTalk的测试页面,功能仍在制作中

项目简介

一个轻量化、设置简单、可定制、高度自由化的静态页面MomoTalk实现方案。

在静态页面上实现聊天气泡,并且可以仿照游戏中momotalk的样式制作类似的版本。

Github有类似的MMTK工程,但是都不适用在静态页面内部,而且不方便对页面优化,于是我又自己搓轮子做一个可以即开即用的小功能。

使用Tailwind CSSdaisyUi实现。

按照官网流程安装完之后,就可以实现网页聊天气泡的效果了(也可以用其他的样式)。

开始

添加气泡的CSS样式

首先我们需要写一份能用的CSS样式来供气泡使用。

./css中新建chat.css,然后把这些已经写好的CSS样式复制进去:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
body {
font-family: Arial, sans-serif;
background-color: #f0f0f0; /* 背景颜色 白色 */
}

/*聊天气泡容器*/
.chat-container {
max-width: 600px; /* 限制最大宽度 */
margin: 0 auto; /* 居中 */
}

.chat {
display: flex; /* 使用 flexbox */
align-items: flex-start; /* 让头像与气泡顶部对齐 */
margin-bottom: 15px; /* 添加底部间距 */
}

/*左边气泡*/
.chat-start {
justify-content: flex-start; /* 左侧对齐 */
}

/*右边气泡*/
.chat-end {
justify-content: flex-end; /* 右侧对齐 */
}

/*备用的解决方案,默认不使用*/
.chat-image {
margin-right: 10px; /* 头像和气泡之间的间距 */
}

.chat-bubble {
position: relative; /* 让伪元素相对于聊天气泡进行定位 */
}

.chat-bubble:before {
border-color: transparent #4b5a6f transparent transparent; /* 设置三角箭头的颜色 */
border-style: solid; /* 边框样式为实线 */
border-width: 6px; /* 边框宽度 */
content: ""; /* 设置伪元素内容为空 */
position: absolute; /* 绝对定位 */
left: -9px; /* 三角箭头位置在气泡左侧 */
top:10px; /* 根据缩放比例调整箭头垂直位置 */
}

/* 如果气泡在右侧的箭头 */
.chat-end .chat-bubble:before {
border-color: transparent transparent transparent #4a8ac6;
border-style: solid;
border-width: 6px;
content: "";
position: absolute;
top: 10px;
inset-inline-start: 99%;
}

/*聊天气泡*/
.chat-bubble {
color: white; /* 字体颜色 */
border-radius: 15px; /* 圆角 */
padding: 10px 15px; /* 内边距 */
max-width: 100%; /* 最大宽度 */
word-wrap: break-word; /* 自动换行 */
flex-grow: 0; /* 让聊天框占据更多空间 */
margin-top: 5px; /* 与昵称之间的间距 */
position: relative; /* 必须加上,确保伪元素定位在正确的位置 */
/* 保持气泡底部的圆角 */
border-bottom-left-radius: 15px;
border-bottom-right-radius: 15px;
}


.chat-start .chat-bubble {
background-color: #4b5a6f; /* 左侧气泡颜色 */
border-end-start-radius: 15px; /* 取消左下角圆角 */
}

.chat-end .chat-bubble {
background-color: #4a8ac6; /* 右侧气泡颜色 */
border-end-end-radius: 15px; /* 取消左下角圆角 */
}

/*表情*/
.emoji-image {
width: 200px; /* 表情图片的宽度 */
height: 200px; /* 表情图片的高度 */
display: inline-block; /* 让表情和文本在同一行显示 */
vertical-align: middle; /* 垂直对齐 */
}


.chat-image img {
width: 60px; /* 头像宽度 */
height: 60px; /* 头像高度 */
border-radius: 50%; /* 确保头像为圆形 */
object-fit: cover; /* 保证图片覆盖整个区域,不会变形 */
}

/*头像容器,用这个*/
.avatar-container {
width: 65px; /* 固定宽度 */
height: 65px; /* 固定高度 */
border-radius: 50%; /* 使其成为圆形 */
overflow: hidden; /* 确保图片不会溢出容器 */
}

.avatar-container img {
width: 100%; /* 图片填满容器 */
height: 100%; /* 图片填满容器 */
object-fit: cover; /* 保持图片比例并填满容器 */
}

/*昵称设置*/
.chat-header {
font-weight: bold; /* 粗体 */
color: black; /* 字体颜色 */
margin-bottom: 3px; /* 与气泡的间距减小 */
align-self: flex-start; /* 确保昵称和气泡左对齐 */
}

/*旁白气泡*/
.text-container {
display: flex; /* 使用 flexbox 布局 */
justify-content: center; /* 水平居中 */
align-items: center; /* 垂直居中 */
height: 30px; /* 可根据需要调整高度 */
}

/*旁白设置*/
.text-message {
color: #474f5d; /* 设置文本颜色 */
font-size: 15px; /* 设置字体大小 */
text-align: center; /* 文本居中对齐 */
background-color: #dce6e8; /* 设置色块的背景颜色 */
border: 0px solid #dce6e8; /* 设置边框 */
border-radius: 10px; /* 设置圆角 */
padding: 5px 10px; /* 添加内边距 */
font-weight: bold; /* 加粗字体 */
}

/*羁绊气泡*/
.text-bubble {
display: flex; /* 使用 flexbox 布局 */
flex-direction: column; /* 让内容垂直排列 */
align-items: flex-start; /* 左对齐 */
background-color: rgba(255, 255, 255, 0.9); /* 背景颜色,可以根据需要自定义 */
background-image: url('https://bu.dusays.com/2024/10/09/6705649e15f30.jpg'); /* 自定义背景图片 */
background-size: cover; /* 使背景图覆盖整个气泡 */
background-position: center; /* 确保背景图居中显示 */
border-radius: 15px; /* 圆角 */
padding: 15px; /* 内边距 */
max-width: 520px; /* 限制最大宽度 */
margin: 30px auto; /* 居中 */
box-sizing: border-box; /* 包括内边距和边框在内的宽度计算 */
box-shadow: 0px 3.2px 6.4px 0px rgba(0, 0, 0, 0.3);
}

/*羁绊按钮分割线*/
.separator {
margin: 5px 0; /* 上下间距 */
width: 100%; /* 横线宽度 */
border-top: 1px solid #fff; /* 横线样式 */
}

/*羁绊按钮文本*/
.text {
color: #474f5d; /* 文本颜色 */
font-size: 20px; /* 文本大小 */
text-align: left; /* 文本左对齐 */
font-weight: bold; /* 加粗字体 */
margin-bottom: 5px; /* 文本与分隔线之间的间距 */
}

/*羁绊按钮*/
.button {
background-color: #fc879a; /* 按钮颜色 */
color: white; /* 按钮文字颜色 */
font-weight: bold; /* 加粗字体 */
border: none; /* 去掉边框 */
border-radius: 5px; /* 圆角 */
padding: 15px; /* 内边距 */
cursor: pointer; /* 鼠标悬停变为手形 */
transition: background-color 0.3s; /* 动画效果 */
width: 100%; /* 按钮宽度适应气泡 */
box-sizing: border-box; /* 包括内边距和边框在内的宽度计算 */
box-shadow: 0px 0.6px 1.6px 0px rgba(0, 0, 0, 0.4);
}

/*羁绊按钮点击效果*/
.button:hover {
background-color: #fc748a; /* 鼠标悬停时按钮颜色 */
}

.button:active {
background-color: #f1304f;
}

引用自定义css

然后我们需要在主题的head文件中引用这段自定义的css,找到./themes/anzhiyu/layout/**/head.pug (我的主题文件用的是pug格式),找到合适的位置引用

1
2
3
4
5
6
《此处省略上部分内容,只需要找到link那一块加上就可以了》
//- 在这里引入 Tailwind CSS
link(href='/css/output.css' rel='stylesheet')

//- 引入聊天气泡样式
link(href='/css/chat.css' rel='stylesheet')

如何使用?

整体结构

1
2
3
<div class="chat-container">
...
</div>

<div class="chat-container"> 这是整个聊天气泡的容器,用于包裹所有的聊天消息。它通常用于设置最大宽度、居中显示等样式。

左侧聊天消息

1
2
3
4
5
6
7
8
9
10
11
<div class="chat chat-start">
<div class="chat-image">
<img src="example.jpg" alt="头像">
</div>
<div>
<div class="chat-header">nickname</div> <!-- 昵称 -->
<div class="chat-bubble">
在这里填写内容~
</div>
</div>
</div>

敲黑板,一定要注意这里!

<div class="chat chat-start"> 这是左侧聊天消息的容器。

chat-start 表示这是左侧消息,通常用于显示用户发送的消息。

当你需要修改头像的时候只需要更改里面的example.jpg就行了。

如果对头像大小不满意可以调整 ./css/chat.css 文件中的.avatar-container选项,把固定宽度和高度改一下就好。

<img src="..."> : 头像图片,src 属性设置头像的 URL,alt属性用于描述头像的内容,便于 SEO 和可访问性。

同理,nickname也可以替换掉成你想要的昵称

使用过程中一定要注意格式!不然整个页面的排版都会崩掉!

聊天内容

小虾球shrimp
爱你哟~
1
2
3
4
5
6
<div>
<div class="chat-header">shrimp</div> <!-- 昵称 -->
<div class="chat-bubble">
I Love You.
</div>
</div>

这些是什么意思呢?

<div>: 用于包裹昵称和消息内容的容器。

<div class="chat-header">: 昵称的容器,这里是“shrimp”。

<div class="chat-bubble">: 聊天气泡的容器,这里显示的是消息内容“I Love You.”。

发送包含表情的信息

头像
小虾球shrimp
表情
1
2
3
4
5
6
7
8
9
10
11
<div class="chat chat-start">
<div class="avatar-container">
<img src="https://bu.dusays.com/2024/10/04/66ffaa05a654c.jpg" alt="头像">
</div>
<div>
<div class="chat-header">shrimp</div> <!-- 昵称 -->
<div class="chat-bubble">
<img src="https://bu.dusays.com/2024/10/08/670545dbc02a9.png" alt="表情" class="emoji-image">
</div>
</div>
</div>

这部分的结构与第一条消息相似,但它的 chat-bubble 中包含一个表情图片。
<img src="...">: 表情图片,通过 class=”emoji-image” 可以为这个图片应用特定的样式。

右侧聊天消息

呱!我也爱你口牙!也让我钻钻被窝!
头像
1
2
3
4
5
6
7
8
<div class="chat chat-end">
<div class="chat-bubble">
I Love you too.
</div>
<div class="avatar-container">
<img src="https://bu.dusays.com/2024/10/04/66ffaa05a654c.jpg" alt="头像">
</div>
</div>

<div class="chat chat-end">: 这是右侧聊天消息的容器,chat-end 表示这是右侧消息,通常用于显示接收的消息。

使用旁白描述

我是旁白君,来写点什么?
1
2
3
<div class="text-container">
<div class="text-message">在这里填旁白和描述文本</div>
</div>

使用羁绊剧情按钮

| 羁绊事件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<div class="text-bubble">
<div class="text">在这里改气泡的描述</div><!-- 气泡描述 -->
<div class="separator"></div> <!-- 横线,可以不用修改 -->
<button class="button" onclick="yourFunction()">在这里更改按钮文本</button> <!-- 按钮 -->
</div>

<script>
function yourFunction() {
alert('写你想要提示的文本'); // 在这里实现按钮功能,比如跳转或弹出提示
}
</script>

<!-- 如果你想要跳转其他网页可以这么写-->
<script>
function yourFunction() {
window.location.href = "https://example.com";
}
</script>

<script> 里面是使用JavaScript实现按钮点击的特殊事件,比如:提醒,跳转,弹出文本图片等。

当然你也可以根据需求删改相关的选项,比如说右边气泡可以去掉头像仿照MMTK效果

通过上述方式实现的所有小功能

似乎过了很久很久...
头像
时雨
我爱你。
头像
时雨
表情
我也爱你口牙!
头像
| 羁绊事件

然后就可以利用这个功能来实现沉浸式故事啦~

目前这个功能可以用在很多地方,比如说作为故事的引子,通过mmtk引导读者前往另一篇故事来实现更强的沉浸感。

当然,你也可以把他扔到其他文本里单独当装饰,也是非常不错的选择。

关于能不能通过动画来播放?目前来说有点复杂,而且排版会错位,后期有精力我会研究下并更新这个文章。

完整代码

你可以通过这段代码实现默认效果,或者复制其中的一部分来省事,但一定要注意结构!

如果发现文本是往上走的记得把头像class换成avatar-container

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
<div class="text-container">
<div class="text-message">似乎过了很久很久...</div>
</div>


<div class="chat-container">
<div class="chat chat-start">
<div class="avatar-container">
<img src="https://bu.dusays.com/2024/10/09/67064a3a44cab.webp" alt="头像">
</div>
<div>
<div class="chat-header">时雨</div> <!-- 昵称 -->
<div class="chat-bubble">
我爱你。
</div>
</div>
</div>

<div class="chat chat-start">
<div class="avatar-container">
<img src="https://bu.dusays.com/2024/10/09/67064a3a44cab.webp" alt="头像">
</div>
<div>
<div class="chat-header">时雨</div> <!-- 昵称 -->
<div class="chat-bubble">
<img src="https://bu.dusays.com/2024/10/08/670545dbc02a9.png" alt="表情" class="emoji-image">
</div>
</div>
</div>

<div class="chat chat-end">
<div class="chat-bubble">
我也爱你口牙!
</div>
<div class="avatar-container">
<img src="https://bu.dusays.com/2024/10/04/66ffaa05a654c.jpg" alt="头像">
</div>
</div>
</div>

<div class="text-bubble">
<div class="text">| 羁绊事件</div>
<div class="separator"></div> <!-- 横线 -->
<button class="button" onclick="yourFunction()">时雨的羁绊剧情</button> <!-- 按钮 -->
</div>

<script>
function yourFunction() {
alert('诶诶,剧情去哪里啦?还没写呢~'); // 在这里实现按钮功能,比如跳转或弹出提示
}
</script>
By TLSLime 2024.10.9