实现效果
多段打字,每段之间延迟一点时间,末尾有光标
页面结构
所有的“打字”的效果都在typedText里面展现,打印完了就把这一段加入displayedParagraphs中,同时清空typedText。
displayedParagraphs就是正常v-for渲染
数据结构
data() {return {typingSpeed: 100, // 打字速度paragraphGap: 1000, // 每段间隔的时间(ms)currentIndex: 0, // 当前段落字符索引currentParagraph: 0, // 当前段落索引typedText: "", // 当前正在打字的内容displayedParagraphs: [], // 已完成显示的段落typingComplete: false, // 是否完成}
},
方法解析
typeWriterEffect的作用是打印一段话,
组件示例
<template><div><div class="section1"><div class="section1-word"><!-- 动态渲染每个段落 --><div v-for="(paragraph, index) in displayedParagraphs" :key="index" class="paragraph"><span>{{ paragraph }}</span></div><!-- 当前正在打字的段落 --><div v-if="!typingComplete" class="paragraph"><span>{{ typedText }}</span><span class="cursor"></span> <!-- 光标 --></div></div></div></div>
</template><script>import {mapState} from "vuex";import axios from 'axios';import {nextTick} from "vue";export default {name: "stepDetail",data() {return {step: 0, // 步数// 剩下的由gpt生成的段落gptFullText: [],typingSpeed: 100, // 打字速度paragraphGap: 1000, // 每段间隔的时间(ms)currentIndex: 0, // 当前段落字符索引currentParagraph: 0, // 当前段落索引typedText: "", // 当前正在打字的内容displayedParagraphs: [], // 已完成显示的段落typingComplete: false, // 是否完成}},computed: {...mapState(['currentUserId', 'userData']),fullText() {// 所有段落的内容return [`从上次统计开始,你步行了${this.step}步,相当于减少了约${this.step / 1000 * 200}g的碳排放。这看似微小的行动,却为地球注入了源源不断的生命之力。`,...this.gptFullText]}},methods: {// 打字效果/*** 模拟打字机效果,逐个字符地显示文本,并在段落之间添加延迟。** @method typeWriterEffect* @returns {void} - 无返回值。*/typeWriterEffect() {const interval = setInterval(() => {const currentText = this.fullText[this.currentParagraph];if (this.currentIndex < currentText.length) {this.typedText += currentText.charAt(this.currentIndex);this.currentIndex++;} else {clearInterval(interval);this.displayedParagraphs.push(this.typedText);this.typedText = "";this.currentIndex = 0;this.currentParagraph++;if (this.currentParagraph < this.fullText.length) {// 延迟显示下一段setTimeout(this.typeWriterEffect, this.paragraphGap);} else {this.typingComplete = true;}}}, this.typingSpeed);},async getGptText() {this.gptFullText = gpt返回的文本内容;}},created() {this.step = this.userData[this.currentUserId].step;},mounted() {this.$nextTick(() => {this.getGptText();this.typeWriterEffect();});}
}
</script><style scoped lang="scss">
.section1 {width: 90%;height: calc(100vh - 200px);margin: auto;padding-top: 20px;overflow: auto;.section1-word {color: white;font-size: 30px;position: relative;display: flex;flex-direction: column; /* 垂直排列段落 */gap: 20px; /* 段落间距 */font-family: 'SanJinSong-Xi', serif;}.cursor {display: inline-block;width: 2px;height: 1em;background-color: white;margin-left: 5px;animation: blink 1s step-start infinite;}@keyframes blink {50% {background-color: transparent;}}
}
</style>