【开发记录】Vue3写一个气泡对话框,可随意配置文字换行、图标、箭头位置和方向

一、功能点

  1. 文字可以换行
  2. 可以随意这是重点加粗的文字
  3. 对话图标可以显示、隐藏
  4. 箭头可以调整左右方向、在对话框的上面、下面,以及相对中间偏移

二、具体功能能实现

功能都不复杂,这里简单说一下,哈哈,但有一个重要的点,还是先提前说一下: 对话框里的文字,是用v-html插入进去的,因为要实现内部文字的加粗,需要提前处理,将加粗的文字,用标签包起来。

1、文字换行

输入框回车后,所输入的文字内,会有\n,所以,把\n批量替换成

标签就可以了

js 复制代码
htmlText = htmlText.replaceAll("\n", `<br />`);

2、内部部分文字加粗

遍历要加粗的文字数组,将需要加粗的文字,用标签包起来,大概的逻辑如下:

js 复制代码
boldTexts.forEach(boldText => {
if (boldText) {
  htmlText = htmlText.replaceAll(boldText, `<>${boldText}</>`);
}
});
htmlText = htmlText.replaceAll("<>", `<span style="font-weight: 900;">`);
htmlText = htmlText.replaceAll("</>", `</span>`);

3、箭头的调整

这个就简单了,就用用css样式去控制下,这里就不细说了,下面有完整的代码,一看就知道了。

完整代码

哈哈,写技术分享,不贴出完整代码的,都是在耍流氓。

vue3 复制代码
<template>
  <div
    class="word-pop-container need-bubble"
    :class="{'has-horn': showHorn}"
    v-if="textHtml"
  >
    <div class="show-horn" v-if="showHorn" >
      <img src="./image/chat.png" />
    </div>
    <div>
      <span v-html="textHtml"></span>
    </div>
    <div
      class="arrow-box"
      :class="{
        flip: wordPopConfigData.arrowPosition,
        'no-filp ': !wordPopConfigData.arrowPosition,
      }"
    >
      <i
        class="arrow"
        :class="{ flip: wordPopConfigData.arrowFlip }"
        :style="{ left: wordPopConfigData.arrowOffsetX + 'px' }"
      ></i>
    </div>
  </div>
</template>

<script lang="ts">
import { computed, defineComponent, ref } from "vue";
export default defineComponent({
  name: "WordPop",
  props: {
    configData: {
      type: Object,
      require: true,
      default: (() => {}),
    }
  },
  setup(props) {
    const defaultConfigData = ref({
      text: "", // 对话文本
      boldTexts: [], // 加粗的文字
      showHorn: true, // 显示对话图标
      arrowPosition: false, // 箭头位置:fanse:下,true:上
      arrowFlip: false, // 箭头指向:false右,true:左
      arrowOffsetX: -40, // 箭头偏移,单位:像素,相对位置:对话框中间文职,正数向右偏移、负数向左偏移
    });

    const wordPopConfigData = computed(() => {
      return {
        ...defaultConfigData.value,
        ...props.configData,
      }
    });

    const textHtml = computed(() => {
      let htmlText = wordPopConfigData.value.text;
      wordPopConfigData.value.boldTexts.forEach(boldText => {
        if (boldText) {
          htmlText = htmlText.replaceAll(boldText, `<>${boldText}</>`);
        }
      });
      htmlText = htmlText.replaceAll("<>", `<span style="font-weight: 900;">`);
      htmlText = htmlText.replaceAll("</>", `</span>`);
      htmlText = htmlText.replaceAll("\n", `<br />`);
      return htmlText;
    });

    // 对话标识
    const showHorn = computed(() => {
      return Boolean(wordPopConfigData.value.showHorn || false);
    });


    return {
      wordPopConfigData,
      showHorn,
      textHtml,
    };
  },
});
</script>

<style lang="less" scoped>
.word-pop-container {
  width: auto;
  height: auto;
  position: relative;
  color: #000;
  letter-spacing: normal;
  text-align: left;
  white-space: nowrap;
  padding: 10px 18px;
  border-radius: 40px;
  display: inline-block;
  border: 3px solid #f2f2f2;
  &.has-horn {
    padding: 10px 18px 10px 43px;
  }
  .show-horn {
    position: absolute;
    width: 20px;
    height: 20px;
    top: 0px;
    bottom: 0px;
    left: 13px;
    margin: auto;
    img {
      width: 100%;
      height: 100%;
      border-radius: 15px;
    }
  }
  span {
    font-size: 16px;
    line-height: 1.4;
  }
  &.need-bubble {
    background-color: #fff;
  }
  .arrow-box {
    position: absolute;
    width: 30px;
    height: 20px;
    left: 50%;
    bottom: -20px;
    &.no-filp {
      transform: translateX(-50%);
    }
    &.flip {
      bottom: auto;
      top: 0;
      transform-origin: top center;
      transform: translateX(-50%) rotateX(180deg);
    }
  }
  .arrow {
    display: block;
    width: 100%;
    height: 100%;
    background-image: url(./image/sweepWordArrow.png);
    background-size: 100% 100%;
    background-repeat: no-repeat;
    position: absolute;
    bottom: 2.3px;
    &.flip {
      transform-origin: center;
      transform: rotateY(180deg);
    }
  }
}
</style>

四、最后

欢迎各位来用下我用了近2年,搞的一款网页编辑器,一定会让你感到惊讶的,哈哈!

之后,我会继续总结,把这个工具里的一些功能模块,抽离出来,放在github.com/lcl6659/vue... 这里,感兴趣的朋友可以看看,顺手给个star,也算是支持一下我了,在此感谢!!

相关推荐
卸任6 小时前
Vibe coding工具使用心得
前端·ai编程
brien97 小时前
为什么模型标称 1M,Google Antigravity 聊到 256K 却突然失忆?我们给它做了个原生行内监控圆环
前端
修炼的dance7 小时前
录屏关键帧间隔多少合适?六档实测,2秒到10秒小了75.6%
前端
Lank_M7 小时前
关键帧从1秒拉到10秒,录屏小87.3%,摄像头小10.8%
前端
补码缩写7 小时前
用 ffprobe 拆录屏,八成字节在 29 个关键帧上
前端
百思不得小李7 小时前
一个复杂的动态表单是如何实现的
前端·设计模式
陈随易7 小时前
Cmder 停更 17 个月,2.0 支持 Windows Terminal
前端·后端·程序员
风骏时光牛马7 小时前
前沿视界
前端
kyriewen8 小时前
我扫了 814 个依赖,97 个还敢要 Node 10
前端·javascript·程序员
陈随易8 小时前
3000 万人在用的翻译插件,重写一遍就是一门生意
前端·后端·程序员