配合event-source实现打印机打字效果

2857次阅读 70人点赞 作者: WuBin 发布时间: 2024-06-17 09:15:00
扫码到手机查看

基本结构

 <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
    }
}
点赞 支持一下 觉得不错?客官您就稍微鼓励一下吧!
关键词:打印机
推荐阅读
  • uniapp实现被浏览器唤起的功能

    当用户打开h5链接时候,点击打开app若用户在已经安装过app的情况下直接打开app,若未安装过跳到应用市场下载安装这个功能在实现上主要分为两种场景,从普通浏览器唤醒以及从微信唤醒。

    8686次阅读 558人点赞 发布时间: 2022-12-14 16:34:53 立即查看
  • Vue

    盘点Vue2和Vue3的10种组件通信方式

    Vue中组件通信方式有很多,其中Vue2和Vue3实现起来也会有很多差异;本文将通过选项式API组合式API以及setup三种不同实现方式全面介绍Vue2和Vue3的组件通信方式。

    3624次阅读 271人点赞 发布时间: 2022-08-19 09:40:16 立即查看
  • JS

    几个高级前端常用的API

    推荐4个前端开发中常用的高端API,分别是MutationObserver、IntersectionObserver、getComputedstyle、getBoundingClientRect、requ...

    13644次阅读 881人点赞 发布时间: 2021-11-11 09:39:54 立即查看
  • PHP

    【正则】一些常用的正则表达式总结

    在日常开发中,正则表达式是非常有用的,正则表达式在每个语言中都是可以使用的,他就跟JSON一样,是通用的。了解一些常用的正则表达式,能大大提高你的工作效率。

    12457次阅读 411人点赞 发布时间: 2021-10-09 15:58:58 立即查看
  • 【中文】免费可商用字体下载与考证

    65款免费、可商用、无任何限制中文字体打包下载,这些字体都是经过长期验证,经得住市场考验的,让您规避被无良厂商起诉的风险。

    10879次阅读 873人点赞 发布时间: 2021-07-05 15:28:45 立即查看
  • Vue

    Vue3开发一个v-loading的自定义指令

    在vue3中实现一个自定义的指令,有助于我们简化开发,简化复用,通过一个指令的调用即可实现一些可高度复用的交互。

    14963次阅读 1192人点赞 发布时间: 2021-07-02 15:58:35 立即查看
  • JS

    关于手机上滚动穿透问题的解决

    当页面出现浮层的时候,滑动浮层的内容,正常情况下预期应该是浮层下边的内容不会滚动;然而事实并非如此。在PC上使用css即可解决,但是在手机端,情况就变的比较复杂,就需要禁止触摸事件才可以。

    14516次阅读 1182人点赞 发布时间: 2021-05-31 09:25:50 立即查看
  • Vue

    Vue+html2canvas截图空白的问题

    在使用vue做信网单页专题时,有海报生成的功能,这里推荐2个插件:一个是html2canvas,构造好DOM然后转canvas进行截图;另外使用vue-canvas-poster(这个截止到2021年3月...

    28147次阅读 2213人点赞 发布时间: 2021-03-02 09:04:51 立即查看
  • Vue

    vue-router4过度动画无效解决方案

    在初次使用vue3+vue-router4时候,先后遇到了过度动画transition进入和退出分别无效的情况,搜遍百度没没找到合适解决方法,包括vue-route4有一些API都进行了变化,以前的一些操...

    24329次阅读 1871人点赞 发布时间: 2021-02-23 13:37:20 立即查看
交流 收藏 目录