小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。
原理
先看下效果图
原理:利用
<popover>组件进行弹出框,内容存放我们的表情组件。 为每一个表情的hover更换src,点击emit发送表情的信息即可。
利用之前我们封装的<Scroller />组件,可以更好的管理组件的布局,滑动。
<Scroller />组件 详情见 # 基于better-scroll简单封装一个滚动的组件
封装use工具函数,存放我们的表情数据源
import emojis from '@/assets/json/emoji.json'
function useData() {
const host = 'http://chat.chrisying.cn/' //这是oss地址
const emojiData: Emoji[] = emojis.map((it: Emoji) => {
return {
...it,
src: `${host}${it.src}`,
hover: `${host}${it.hover}`,
show: false,
display: `${host}${it.src}`
}
})
const datas = reactive(emojiData)
return { datas }
}
处理我们的表情点击选择
const handleSelect = (item: Emoji) => {
console.log(item)
item.show = false
showEmojiList.value = false
ctx.emit('select', item)
}
完整代码
<template>
<a-popover v-model:visible="showEmojiList"
overlayClassName="customEmoji"
placement="top"
trigger="click">
<template #content>
<Scroller :data="datas"
class="wrapper">
<div class="emoji-list"
:style="{width:`${col * (2.8 + 0.4)}rem`}">
<template v-if="type === 'II'">
<a-popover v-model:visible="item.show"
v-for="item in datas"
:key="item.name"
overlayClassName="customEmoji"
placement="top">
<template #content>
<div @click="handleSelect(item)"
class="emoji-item"
:style="{ backgroundImage: `url(${item.hover})` }"></div>
<div class="emoji-info">{{item.info}}</div>
</template>
<div @click="handleSelect(item)"
@touchstart="item.show=true"
@touchend="item.show=false"
class="emoji-item"
:style="{ backgroundImage: `url(${item.src})` }"></div>
</a-popover>
</template>
<template v-else
v-for="item in datas"
:key="item.name">
<div @click="handleSelect(item)"
@mouseenter="item.display = item.hover"
@mouseleave="item.display = item.src"
@touchstart="item.display = item.hover"
@touchend="item.display = item.src"
class="emoji-item"
:style="{ 'background-image': `url(${item.display})` }"></div>
</template>
</div>
</Scroller>
</template>
<slot>
<span class="face">
<svg xmlns="http://www.w3.org/2000/svg"
width="1em"
height="1em"
fill="currentColor"
class="bi bi-emoji-smile"
viewBox="0 0 16 16">
<path fill-rule="evenodd"
d="M8 15A7 7 0 1 0 8 1a7 7 0 0 0 0 14zm0 1A8 8 0 1 0 8 0a8 8 0 0 0 0 16z" />
<path fill-rule="evenodd"
d="M4.285 9.567a.5.5 0 0 1 .683.183A3.498 3.498 0 0 0 8 11.5a3.498 3.498 0 0 0 3.032-1.75.5.5 0 1 1 .866.5A4.498 4.498 0 0 1 8 12.5a4.498 4.498 0 0 1-3.898-2.25.5.5 0 0 1 .183-.683z" />
<path d="M7 6.5C7 7.328 6.552 8 6 8s-1-.672-1-1.5S5.448 5 6 5s1 .672 1 1.5zm4 0c0 .828-.448 1.5-1 1.5s-1-.672-1-1.5S9.448 5 10 5s1 .672 1 1.5z" />
</svg>
</span>
</slot>
</a-popover>
</template>
<script lang='ts'>
import { defineComponent, ref, reactive } from 'vue'
import emojis from '@/assets/json/emoji.json'
import Scroller from '@c/Scroller/index.vue'
interface Emoji {
name: string
src: string
info: string
hover: string
value: string
show: boolean
display: string
}
function useData() {
const host = 'http://chat.chrisying.cn/'
const emojiData: Emoji[] = emojis.map((it: Emoji) => {
return {
...it,
src: `${host}${it.src}`,
hover: `${host}${it.hover}`,
show: false,
display: `${host}${it.src}`
}
})
const datas = reactive(emojiData)
return { datas }
}
function useOutsidePopover() {
const showEmojiList = ref(false)
return { showEmojiList }
}
export default defineComponent({
name: 'Emoji',
components: { Scroller },
props: {
col: {
type: Number,
default: 8
},
type: {
type: String,
default: 'II' // I | II
}
},
setup(props, ctx) {
const { showEmojiList } = useOutsidePopover()
const handleSelect = (item: Emoji) => {
console.log(item)
item.show = false
showEmojiList.value = false
ctx.emit('select', item)
}
return { ...useData(), showEmojiList, handleSelect }
}
})
</script>
<style lang='scss' scoped>
.face {
font-size: 2.2rem;
color: #aab0b7;
&:hover {
cursor: pointer;
color: rgba(33, 37, 41, 0.5);
}
}
.wrapper {
height: 250px;
overflow: hidden;
}
.emoji-list {
display: flex;
flex-wrap: wrap;
}
.emoji-item {
width: 2.8rem;
height: 2.8rem;
margin: 0.2rem;
background-repeat: no-repeat;
background-position: center center;
background-size: cover;
&:hover {
cursor: pointer;
}
// @function col() {
// @return var(--col);
// }
// order: var(--col);
// $nth: #{nth-of-type(col())};
// &:#{$nth} {
// margin-right: 0;
// }
}
.emoji-info {
width: 100%;
text-align: center;
font-size: 1.2rem;
margin-top: 0.4rem;
}
@media (max-width: 768px) {
.wrapper {
height: 160px;
}
}
</style>