配合event-source实现打印机打字效果
基本结构
<p class="content-p"
ref="contentpRef"
v-html="displayedText"
></p>
export default {
props: {
message: {
type: String,
default() {
return "";
}
}
},
setup(props, { emit }) {
const {
displayedText
} = useTypeText(props, emit);
return {
displayedText
}
}
}
2024-6-25更新,在每轮打印结束后,判断source是否关闭,如果关闭了那么代表此时的message就是完整的内容,这时候再发送结束事件。
import { ref, onUnmounted, watch } from 'vue';
import { browserAutoScroll } from "@/common/js/fns";
import { useStore } from 'vuex';
import { isMobile } from "@/common/js/const";
export default function useTypeText(props, emit) {
const store = useStore();
// 用于输出的文案
const displayedText = ref('');
let typingInterval = null;
let typingIndex = 0;
const typingTime = 20;
watch(
() => props.message,
(newval) => {
if (newval) {
// 只有当没有开启定时器的时候 才执行
if(typingInterval) {
return;
}
typeText();
}
}
);
function typeText() {
if (typingIndex < props.message.length) {
displayedText.value += props.message[typingIndex];
displayedText.value = fotmat(displayedText.value);
typingIndex++;
// 打字速度
typingInterval = setTimeout(typeText, typingTime);
} else {
// 打字完成后,清除定时器
clearTimeout(typingInterval);
typingInterval = null;
// 当打印完并且检测推流是false(推流结束) 重置标记位
// 关键
if (!store.getters.isSourceing) {
typingIndex = 0;
emit('typeEnd', false);
}
}
if (props.onlyMessage && isMobile) {
return;
}
browserAutoScroll();
}
onUnmounted(() => {
reset();
});
function fotmat(text) {
return text.replace(/\n/g, '<br>');
}
function reset() {
clearTimeout(typingInterval);
typingIndex = 0;
typingInterval = null;
}
return {
displayedText
}
}
打印效果实现(旧版本,有bug-当剩余字数*单个字打印时间 求出的结果与实际打印剩余字数时间不相符的时候,比如计算的时间是1秒,而实际的是1.4秒,这时候1秒时候就会发送一个打印结束的请求,导致剩余内容输出不完全。)
import { ref, onUnmounted, watch } from 'vue';
import { useStore } from 'vuex';
export default function useTypeText(props, emit) {
const store = useStore();
const displayedText = ref('');
let typingInterval = null;
let typingIndex = 0;
const typingTime = 20;
watch(
() => props.message,
(newval) => {
if (newval) {
// 只有当没有开启定时器的时候 才执行
// 实现文本不断增加 而不让文本顺序混乱的关键
if(typingInterval) {
return;
}
typeText();
}
}
);
// 当推流eventsource关闭时,检测还有多少字没有打印完
// 与每个字打印时间相乘 得到打印剩余字数总时间 用于向外发送剩余字打印完后派发的事件
watch(
// 监听store中控制eventsource的变量
() => store.getters.isSourceing,
(newval) => {
if (newval === true) {
return;
}
const messageOrigin = fotmat(props.message);
// 获得此时还没有打印的文字 计算出剩余文字打印完需要的时间
const diffMessage = messageOrigin.replace(displayedText.value, '');
const typeDiffTime = diffMessage.length * typingTime;
// 向外发送打印完成事件
setTimeout(() => {
emit('派发所有字打印完毕的事件');
reset();
}, typeDiffTime);
}
);
function typeText() {
if (typingIndex < props.message.length) {
displayedText.value += props.message[typingIndex];
displayedText.value = fotmat(displayedText.value);
typingIndex++;
// 打字速度
typingInterval = setTimeout(typeText, typingTime);
} else {
// 打字完成后,清除定时器
clearTimeout(typingInterval);
typingInterval = null;
}
if (props.onlyMessage && isMobile) {
return;
}
browserAutoScroll();
}
onUnmounted(() => {
reset();
});
function fotmat(text) {
return text.replace(/\n/g, '<br>');
}
function reset() {
clearTimeout(typingInterval);
typingIndex = 0;
typingInterval = null;
}
return {
displayedText
}
}