前言
自从我被换组后,工作也比之前清闲了不少,无非就是普通的业务逻辑,给的时间也没有很短;管理的前端项目的代码的质量也要好上不少,之前的项目完全就是史山。想想之前那个组的组长要我两天把首页+登录+注册+接口+交互效果全部写完,还需要我写适配,要有自己的想法,之前过的日子太苦了。因为我是实习生所以我觉得苦。
接口调用发现有问题,调不通,但是快下班了,就第二天来弄。好好好,一大早就被m怎么不早点跟后端说,问我怎么办,后端请假回家就一直没回来我能怎么办?还要我两天把接口都调完,你咋不上天呢?
算了,不讲废话。直奔主题,最讨厌画大饼,反正我不吃这一套。
前前言
有了前沿,我开始在做完工作后思考该怎么去发展自身,赚更多的马内。希望有大佬指条明路,上班是真的难上。 闲下来一下后,我就想起来之前渡一前端讲的打字机效果怎么做,现在也忘记在哪个视频。
以豆包为例,ai生成的内容在最后面一直有一个光标跟随,美其名曰“打字机效果”,
- 这个效果是用来提示用户内容还在加载生成,减少用户等待过程中的焦虑。
- 模拟真实人类沟通输入文字的效果。
但是我还是想吐槽豆包生成内容能不能流畅一点,就跟我检查网站,拖动被塞满请求的窗口样的,一卡一卡的。不否认是我电脑的问题。
技术思路
文字的来源是后端返回的内容,所以会一点一点出现,我们要做的就是如何控制加载效果一直跟随在文字后面,有同学可能会想到使用定位,使用伪元素,但是这样做还是有一个缺陷,怎么跟随在最后一个文字的后方,如果选择跟随在容器的最后面,那当容器变宽变高的话,加载效果会一直在右下,无法跟随文字。
所以这里实现靠两步:
- 使用js+定时器将文字添加到DOM,实现打字效果。
- 用css动画实时跟随当前渲染位置,形成视觉引导。
代码
<div class="chat-container">
<h3>豆包聊天界面</h3>
<!-- 消息显示区域 -->
<div id="message-area" class="conversation-container" style="min-height: 200px;"></div>
<!-- 输入区域 -->
<div class="send-area">
<input type="text" id="input-message" placeholder="输入消息...">
<button id="send-button">发送</button>
</div>
</div>
<script>
const messageArea = document.getElementById('message-area');
const inputMessage = document.getElementById('input-message');
const sendButton = document.getElementById('send-button');
// 豆包的回答示例
const doubaoReplies = [
"你好呀!我是豆包,很高兴和你聊天~有什么想了解的都可以问我哦~",
"这个问题很有意思呢!让我想想该怎么回答...首先,我们需要从几个方面来分析...",
"哈哈,你的想法很有创意~关于这个话题,我可以给你分享一些相关的知识和案例~",
"不用担心,这个问题不难解决~我来告诉你几个简单的方法,你可以试试看~"
];
// 发送按钮点击事件
sendButton.addEventListener('click', sendMessage);
inputMessage.addEventListener('keydown', (e) => {
if (e.key === 'Enter') sendMessage();
});
// 发送用户消息并获取豆包回答
function sendMessage() {
const userMessage = inputMessage.value.trim();
if (!userMessage) return;
// 添加用户消息
addMessage(userMessage, 'user');
inputMessage.value = '';
// 模拟网络请求延迟,获取豆包回答
setTimeout(() => {
const reply = doubaoReplies[Math.floor(Math.random() * doubaoReplies.length)];
showTypingEffect(reply);
}, 500);
}
// 添加消息到聊天区域
function addMessage(text, type) {
const messageEl = document.createElement('div');
messageEl.className = `message ${type === 'user' ? 'user-message' : 'doubao-message'}`;
messageEl.innerHTML = text;
messageArea.appendChild(messageEl);
scrollToBottom();
}
// 显示打字机效果和加载符
function showTypingEffect(text) {
// 创建豆包消息容器
const doubaoMessage = document.createElement('div');
doubaoMessage.className = 'message doubao-message';
messageArea.appendChild(doubaoMessage);
scrollToBottom();
const textElement = document.createElement('span');
doubaoMessage.appendChild(textElement);
// 创建加载效果
const loader = document.createElement('span');
loader.className = 'loader';
doubaoMessage.appendChild(loader);
let index = 0;
const speed = 100; // 打字速度(毫秒/字符)
const timer = setInterval(() => {
// 添加字符到文本
if (index < text.length) {
textElement.textContent += text.charAt(index);
index++;
} else {
// 所有字符显示完毕,移除加载效果
clearInterval(timer);
doubaoMessage.removeChild(loader);
}
}, speed);
}
// 滚动到聊天区域底部
function scrollToBottom() {
messageArea.scrollTop = messageArea.scrollHeight;
}
// 页面加载后自动显示一个豆包消息作为示例
window.addEventListener('load', () => {
setTimeout(() => {
showTypingEffect("欢迎使用豆包~我可以回答你的各种问题,比如技术、生活、知识等等,快来和我聊天吧~");
}, 500);
});
</script>
<style>
/* 聊天容器样式 */
.chat-container {
max-width: 600px;
margin: 30px auto;
font-family: "Microsoft YaHei", sans-serif;
}
/* 消息对聊容器样式 */
.conversation-container {
display: flex;
flex-direction: column;
gap: 15px;
}
/* 消息气泡样式 */
.message {
border-radius: 16px;
padding: 12px 16px;
margin-bottom: 15px;
position: relative;
width: fit-content;
max-width: 80%;
}
.user-message {
background-color: #f0f9ff;
margin-left: auto;
text-align: right;
}
.doubao-message {
background-color: #e6f7ff;
margin-right: auto;
text-align: left;
}
/* 加载效果样式(光标) */
.loader {
display: inline-block;
width: 8px;
height: 16px;
background-color: #409EFF;
border-radius: 4px;
margin-left: 4px;
animation: blink 1.2s infinite;
}
/* 光标闪烁动画 */
@keyframes blink {
0%,
100% {
opacity: 1;
}
50% {
opacity: 0;
}
}
/* 发送按钮样式 */
.send-area {
display: flex;
margin-top: 20px;
}
.send-area input {
flex: 1;
padding: 10px 14px;
border: 1px solid #d9d9d9;
border-radius: 20px 0 0 20px;
outline: none;
}
.send-area button {
padding: 0 20px;
background-color: #409EFF;
color: white;
border: none;
border-radius: 0 20px 20px 0;
cursor: pointer;
}
</style>
这里写上一点小优化:当文字内容过长,把时间缩短。
const speed = Math.max(50, 150 - text.length * 2); // 长文本自动加速
解析带有html标签的文本内容:
function parseHtml(html) {
const temp = document.createElement('div');
temp.innerHTML = html;
return Array.from(temp.childNodes);
}
function showRichTextEffect(htmlText) {
const nodes = parseHtml(htmlText);
let index = 0;
setInterval(() => {
if (index < nodes.length) {
message.appendChild(nodes[index].cloneNode(true));
index++;
}
}, 150);
}
最后,这其实是一篇技术分享文章,其余的内容是进行铺垫,讲述为什么我能发这篇文章。