先给大家看一下实现效果图,通过一个按钮来触发表情选择面板,点击表情选择面板中的任意表情,实现把表情添加到指定输入框。本文就是讲怎样来实现这样的一个组件。

本文带大家实现一个QQ与emoji表情选择器组件,在之前有讲过如何制作一个npm组件,详情参考底部公众的文章《手摸手教你制作自己的vue插件(2)》。可以通过文章中介绍的方法按照步骤来制作npm插件,本次主要聚焦组件内部的实现。
目前组件已经上传到了npm仓库,大家可以通过下面命令来安装这个组件。
npm install vue-qqemoji-picker
git仓库在笔者的git仓库中,地址为
https://github.com/Peter1989/vue-qqemoji-picker
大家可以先从git仓库中下载下来代码,根据代码和本文的解析去理解。作者实现这个组件的思路也是借鉴了github上一位作者的代码:github.com/DCzajkowski… 。
背景介绍到此,下面进入正题。
首先看下组件如何使用:
1)在main.js中引入EmojiPicker组件。
import EmojiPick from 'vue-qqemoji-picker'
Vue.use(EmojiPick)
2)在模板中使用组件emoji-picker,在组件中需要定义一个具名插槽为emoji-invoker,emoji-invoker主要是为了激活表情选择框的一个按钮。
<template>
<div class="hello">
<emoji-pick
@emoji="insert"
:panelWidth="450"
:panelHeight="170">
<div slot="emoji-invoker" class="emojibutton" slot-scope="{ events: { click: clickEvent } }" @click.stop="clickEvent">
<button type="button">emoji</button>
</div>
</emoji-pick>
<div>
<textarea v-model="input"></textarea>
</div>
</div>
</template>
<script>
export default {
data(){
return {
input: ''
}
},
methods: {
insert(emoji) {
this.input += emoji
}
}
}
</script>
a.作用域插槽:
如果不了解作用域插槽的概念可以先看这篇文章:segmentfault.com/a/119000001…
1.emoji-picker组件中,定义了emoji事件,进而调用了父组件的insert方法。父组件中insert方法就是在消息输入框中加上emoji表情符号。在组件中通过参数来规定选择面板的宽和高。
2.具名插槽emoji-invoker中通过作用域插槽从子组件往父组件中传递了clickEvent函数。当父组件点击emoji-invoker的时候就会调用子组件的clickEvent函数,这样子组件就可以将内部的状态变量和父组件解耦,方便自己控制状态。
下面我们来看下组件的内部实现:
<template>
<div class="wrap">
<slot
name="emoji-invoker"
:events="{ click: (e) => toggle(e) }"
></slot>
<div
v-if="display.visible"
v-click-outside="hide">
<div class="emojipanel" :style="`width:${panelWidth}px;height:${panelHeight}px;bottom:${display.bottom}px;left:${display.left}px`">
<div
v-for="(emojiGroup, category) in emojis"
:key="category">
<div v-if="category == 'QQ'">
<span
v-for="(item, index) in emojiGroup"
:key="index"
:style="`${item.position}`"
class="qqface-item"
@click="insert(item.code)"/>
</div>
<div v-if="category == 'emoji'">
<span
v-for="(emoji, emojiName) in emojiGroup"
:key="emojiName"
:title="emojiName"
style="margin:0 4px 0 0;font-size: 18px;"
@click="insert(emoji)"
>{{ emoji }}</span>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import emojis from './emojis'
export default {
name: 'emoji-pick',
props: {
panelWidth:{
type: Number,
required:false,
default(){
return 400
}
},
panelHeight:{
type: Number,
required: false,
default(){
return 170
}
}
},
data() {
return {
display: {
left: 0,
bottom: 0,
visible: false,
},
emojis: emojis
}
},
methods: {
insert(emoji) {
this.$emit('emoji', emoji)
},
toggle(e) {
this.display.visible = ! this.display.visible
this.display.left = e.toElement.offsetLeft
this.display.bottom = e.toElement.offsetHeight
},
hide() {
this.display.visible = false
}
}
}
</script>
首先可以看到作用域插槽中给父组件传的click事件,在methods中找到toggle方法,主要是控制表情选择面板的显示与否,并且获取激活按钮的横纵坐标,来控制选择面板的位置。 其中emojis数组分为两部分,即为QQ表情部分和系统emoji表情部分,这两种都是微信消息中所支持的。QQ表情的显示方式的通过传入的item的positioin,在雪碧图中定位,达到显示特定表情的效果。而系统emoji表情是直接通过系统表情显示出来的。 当用户点击表情的时候,就会调用子组件的insert函数,向父组件发送emoji事件,父组件中insert方法就是在消息输入框中加上emoji表情符号。
b.自定义指令:
官网介绍:cn.vuejs.org/v2/guide/cu…
在组件中定义了click-outside自定义指令,在bind和unbind钩子中通过给document绑定/解绑click事件的handler函数,如果点击emoji选择面板之外的元素,就触发自定义指令绑定的在上文的hide函数,来隐藏emoji选择面板。
directives: {
'click-outside': {
bind(el, binding) {
if (typeof binding.value !== 'function') {
return
}
const bubble = binding.modifiers.bubble
const handler = (e) => {
if (bubble || (! el.contains(e.target) && el !== e.target)) {
binding.value(e)
}
}
el.__vueClickOutside__ = handler
document.addEventListener('click', handler)
},
unbind(el) {
document.removeEventListener('click', el.__vueClickOutside__)
el.__vueClickOutside__ = null
},
},
},
更多精彩文章请关注:
