直接上代码
本次采用 vue3.3.8 markdown-it:13.0.1 highlight.js:11.8.0 版本
npm i markdown-it highlight.js
// markdown-it 内置了一些字符转译,但是需要注意一些比较少字符转译方法是没有接入,使用的时候需要注意下,具体根据官方网站进行查看
<!-- Marked.vue -->
<!--
showCursor:是否现象光标
content: 传输内容
-->
<chatContent :showCursor="showCursor" :content="content"></chatContent>
<!-- chatContent .vue -->
<script setup>
import MarkdownIt from 'markdown-it'
import hljs from 'highlight.js'
import 'highlight.js/styles/androidstudio.css'
const props = defineProps({
content:{
type:String,
required:true
},
showCursor:{
type:Boolean,
default:false
}
})
const md = new MarkdownIt({
highlight: function (code, lang) {
if(lang && hljs.getLanguage(lang)){
return hljs.highlight(code,{language:lang}).value
}
return hljs.highlightAuto(code).value
}
});
const markdownContent = computed(()=>{
return md.render(props.content)
})
const pos = reactive({x:0,y:0})
const constentRef = ref(null)
function getLastTextNode(dom){
const children = dom.childNodes
for(let i = children.length-1; i >= 0;i--){
const node = children[i];
if(node.nodeType === Node.TEXT_NODE && /\S/.test(node.nodeValue)){
node.nodeValue = node.nodeValue.replace(/\s+$/,'')
return node
}else if(node.nodeType === Node.ELEMENT_NODE){
const last = getLastTextNode(node)
if(last){
return last
}
}
}
return null
}
function updateCursor(){
const contentDom = constentRef.value
const lastText = getLastTextNode(contentDom)
const txtNode = document.createTextNode('\u200b')
if(lastText){
lastText.parentElement.appendChild(txtNode)
}else{
contentDom.appendChild(txtNode)
}
const domRect = contentDom.getBoundingClientRect()
const range = document.createRange()
range.setStart(txtNode,0)
range.setEnd(txtNode,0)
const rect = range.getBoundingClientRect()
pos.x = rect.left - domRect.left
pos.y = rect.top - domRect.top
txtNode.remove()
}
onMounted(updateCursor)
onUpdated(updateCursor)
</script>
<template>
<div class="container">
<div class="markdown-body" ref="constentRef" v-html="markdownContent"></div>
<div v-show="showCursor" class="cursor"></div>
</div>
</template>
<style lang="scss" scoped>
.markdown-body{
background: inherit;
line-height: 2;
}
.container{
position: relative;
.cursor{
content:'';
position: absolute;
width: 8px;
height: 20px;
background: #3d3d3d;
animation: toggle .6s infinite;
opacity: 0;
transform: translateY(3px);
left: calc( v-bind('pos.x') * 1px );
top: calc( v-bind('pos.y') * 1px );
}
}
@keyframes toggle {
30%{
opacity: 1;
}
}
</style>
本次代码参考于 bilibili 视频