大家好,我是南宫。最近在项目里,发文字的时候有配上表情的需求,这个需求令我一头雾水,后来通过查资料和参考别人的代码,我做了一个表情组件,下面来分享一下我的思路和过程。

效果大概是这样的。

 

一、想要显示表情图标,就需要表情素材

这个非常重要,有了素材才有东西展示嘛,我找到的这个素材是json格式的文件,里面的内容大概长这个样子:

[
    {
        "codes": "1F600",
        "char": "😀",
        "name": "grinning face",
        "category": "Smileys & Emotion (face-smiling)",
        "group": "Smileys & Emotion",
        "subgroup": "face-smiling"
    },
    {
        "codes": "1F603",
        "char": "😃",
        "name": "grinning face with big eyes",
        "category": "Smileys & Emotion (face-smiling)",
        "group": "Smileys & Emotion",
        "subgroup": "face-smiling"
    },
    ...
]

显然,这是一个数组,数组的元素是对象,对象里的char属性是我们要显示出来的表情。表情就像一个字符一样,可以用font-size来控制字体的大小。我删掉了显示不正常的图标。

二、把素材放入项目中使用

我是把这个文件放到项目的src目录下的assets目录里去了,在里面新建一个用来存储表情的文件夹。

然后,在main.js中导入该json文件,得到一个数组,把这个数组作为Vue.prototype的一个属性$emoji,就可以在组件里通过this.$emoji拿到它了。

三、在项目里写一个表情组件

表情组件的使用流程一般是这样的:我们点击“表情按钮”,然后看到一个显示表情的白色大框,里面有一个一个的小格子,里面是一个个表情供我们选择,点击表情以后,大框收起或不收起,表情被加到文字内容的最后。

根据以上的流程,我们的组件里要有一个小按钮,一个显示表情的白色大框。小按钮作为入口,要占据正常的空间且一直显示;显示表情的框是可以控制显示隐藏的,并且显示的时候不额外占空间,而是盖在其他东西上显示,里面的东西划分成统一大小的格子,各自加上边框,内容多的时候还可以滚动。

根据以上的分析,就知道我们组件里要有哪些元素,以及样式大致是什么样子。

这里再来提一个点,那就是“每一个表情的显示”。

首先,确定一个正方形的小格子大小,比如40*40。根据一行摆多少个格子、看到几行来确定白框的宽高。

然后,让表情水平垂直居中显示在格子里面。因为表情相对于文字,所以最简单的方法就是“text-align: center; line-height: 40px;”,字体大小要稍微小于40,可以设为30。

最后,隔离每个小格子要使用边框。我说一下我对于边框的思路

如果你直接给每个格子加上4个方向的边框,那么相邻格子的边框一靠近,就变成粗粗的两条线了;所以边框只能加单边,比如只给格子加上右边框而不加左边框。

所以总体思路是:我们可以给外面的白框加上上边框和左边框,然后给每个小盒子(不包括每行最后一个,用:nth-child(10n)来选择每行最后一个)加上右边框,给除了第一行的小盒子(用:nth-child(n + 11)来选择除了第一行的盒子)加上上边框。

现在来探讨一下组件里的元素的点击逻辑:

小按钮被点击后,要控制白框的显示和隐藏。由于它是组件,我们希望由父组件来控制白框的显示隐藏,所以可以用this.$emit传递一个自定义事件出去,父组件接收后来隐藏掉白框。

表情图标被点击以后,也要把对应的对象数据通过自定义事件传递给父组件,让父组件拿到数据,往对应的文字后面新增一个字符。

四、为了让组件的样式更灵活

经过以上三步,这个表情组件已经可以基本使用了,虽然样式略丑。但是为了让它在一些细节上更灵活,我来提出一些优化思路。

1.显示表情的白框显示位置不确定。

表情的白框可能出现在按钮的左上方、左下方、右上方和右下方。根据实际情况而定,因为出框的部分可以会被裁掉。

为了解决这个问题,在设置了默认样式的前提上,我在props这里加上了4个属性,来接收用户想要的四边偏移量。把这些值接收到以后,动态绑定到白框的元素上。

2.表情按钮的样式不确定。

之前没有给我设计稿,所以我只能自己写组件的样式,使用的图标也是从以前的图标里拿的。结果我看到有一个需要使用表情的地方已经有了现成的按钮,并且样式跟我的不一样时,我感到很无奈。

解决这个问题,我使用的办法是插槽。把按钮的部分做成插槽,原来的按钮作为插槽的后备内容。插槽属于父组件的内容,所以在插入插槽的按钮中直接使用父组件的数据就好了。

五、完整代码

最后,把做完了以上步骤的组件完整代码贴出来,根据里面的prop来使用就好了。

<!--
 * @Author: Betty
 * @Date: 2021-08-11 17:34:57
 * @LastEditors: Betty
 * @LastEditTime: 2021-08-13 11:48:33
 * @Description: 表情组件
-->
<template>
  <div class="expression-btn-box">
    <!-- 表情盒子 -->
    <div
      class="expression-box"
      v-if="isShow"
      :style="{ top: top, left: left, right: right, bottom: bottom }"
    >
      <div class="flex flex-wrap">
        <div
          v-for="(item, index) in expressList"
          :key="index"
          :title="item.name"
          class="express-item"
          @click="addExpress(item)"
        >
          {{ item.char }}
        </div>
      </div>
    </div>
    <!-- 插槽,里面的东西是表情按钮,有默认样式,点击控制白框的显示隐藏 -->
    <slot>
      <button class="express-btn" @click.stop="toggleCommentExpression">
        <svg class="icon" aria-hidden="true">
          <use xlink:href="#icon-icon"></use>
        </svg>
      </button>
    </slot>
  </div>
</template>

<script>
export default {
  name: 'expresssion-box',
  props: {
    // 是否展示弹窗
    isShow: {
      type: Boolean,
      default: false
    },
    // 设置框的定位
    top: {
      type: String,
      default: ''
    },
    right: {
      type: String,
      default: ''
    },
    bottom: {
      type: String,
      default: ''
    },
    left: {
      type: String,
      default: ''
    },
    // 表情数据
    expressList: {
      type: Array
    }
  },
  methods: {
    // 输入一个表情
    addExpress(obj) {
      this.$emit('add-express', obj)
    },
    // 显示隐藏表情盒子
    toggleCommentExpression() {
      this.$emit('toggle-express-box', this.isShow)
    }
  }
}
</script>

<style lang="scss" scoped>
.expression-btn-box {
  position: relative;
}
// 白框
.expression-box {
  position: absolute;
  width: 400px;
  height: 200px;
  overflow: auto;
  bottom: 40px;
  border: 1px solid #666;
  background-color: #fff;
  right: 0;
  // 里面的每一个小格子
  .express-item {
    width: 40px;
    height: 40px;
    text-align: center;
    line-height: 40px;
    font-size: 18px;
    cursor: pointer;
    border-right: 1px solid #666;
    background: #fff;
    box-sizing: border-box;
    &:nth-child(10n) {
      border-right: none;
    }
    &:nth-child(n + 11) {
      border-top: 1px solid #666;
    }
  }
}

// 表情按钮
.express-btn {
  width: 30px;
  height: 30px;
  margin-right: 10px;
  cursor: pointer;
  font-size: 20px;
  border: 1px solid #eee;
  border-radius: 5px;
  .icon {
    width: 24px;
    height: 24px;
  }
}
</style>

感谢大家的阅读!

Logo

前往低代码交流专区

更多推荐