用vue做一个简单的emoji表情组件
大家好,我是南宫。最近在项目里,发文字的时候有配上表情的需求,这个需求令我一头雾水,后来通过查资料和参考别人的代码,我做了一个表情组件,下面来分享一下我的思路和过程。效果大概是这样的。一、想要显示表情图标,就需要表情素材这个非常重要,有了素材才有东西展示嘛,我找到的这个素材是json格式的文件,里面的内容大概长这个样子:[{"codes": "1F600","char": "????","name
大家好,我是南宫。最近在项目里,发文字的时候有配上表情的需求,这个需求令我一头雾水,后来通过查资料和参考别人的代码,我做了一个表情组件,下面来分享一下我的思路和过程。
效果大概是这样的。
一、想要显示表情图标,就需要表情素材
这个非常重要,有了素材才有东西展示嘛,我找到的这个素材是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>
感谢大家的阅读!
更多推荐
所有评论(0)