我做了个微信聊天模拟器,已开源

消失了两天,最近做了一个小项目。

起因是在网上看到一个微信聊天的模拟器,对于一些自媒体小编来说还是挺有实际意义的。

但是那个项目我个人感觉做的不是太好,而且微信经过这么多代的版本,和当初的样式已经有了较大差距。

所以我打算基于现在新版本的微信做个微信聊天的模拟器。

当前项目已经开源,但是还没部署到服务器上,所以暂时先放一下开源地址:

gitee.com/maple2133/v...

这里先打个"保护":

声明

  1. 版权归属:本项目中涉及的微信相关名称、图标、界面样式等所有相关知识产权,均归属腾讯公司及相关原作者所有,本项目不享有任何相关版权。
  2. 使用用途:本项目仅用于交流学习,旨在为开发者提供技术研究、功能调试的参考,不用于任何商业用途、盈利活动,不替代微信官方产品。
  3. 责任说明:使用者使用本项目产生的一切行为,均由使用者自行负责。若使用者因违规使用、滥用本项目,或利用本项目侵犯他人合法权益(含版权、隐私等),相关法律责任、赔偿责任均由使用者独立承担,与本项目作者无关。

技术架构

项目上了 Vite v8+,既然有新的我觉得还是上新的,跟上潮流嘛!

另外就是 Vue3 + Ts 的框架,UI用的是 Element-plus

还有就是 PiniaVueRouter,截图这里我没用 html2Canvas,而是用的 snapdom

个人觉得 snapdom 还是挺好用的,当然目前还没体会到速度的区别。

具体实现

页面分为左右两个部分,模拟两个手机窗口。

当前 v0.0.1 版本仅支持 文本消息语音消息时间消息,后期会逐渐更新其他消息格式,请大家持续关注这个项目,如果能点个 Start⭐ 那就非常感谢了。

手机部分进行了单独的封装:

xml 复制代码
<template>
    <div class="phone-container">
        <div class="phone-content">
            <div class="phone">
                <div class="phone-head">
                    <div class="phone-time">{{ setForm.hour }}:{{ setForm.minute }}</div>
                    <div class="phone-sigle" :class="phoneSigleClass"></div>
                    <div class="phone-wifi" :class="wifiClass"></div>
                    <div class="phone-battery">
                        <div class="battery-level" :style="{ width: setForm.batteryLevel + '%' }"></div>
                    </div>
                </div>
                <div class="phone-nav">
                    <div class="nav-left">
                        <div class="nav-back">返回</div>
                        <div class="unread-num">{{ setForm.msgCount }}</div>
                    </div>
                    <div class="nav-center">
                        <div class="chat-name">
                            {{ props.position === 'left' ? setForm.dialogTitle1 : setForm.dialogTitle2 }}
                        </div>
                    </div>
                    <div class="nav-right">
                        <div class="nav-more"></div>
                    </div>
                </div>

                <PhoneBody :position="props.position" :msgList="props.msgList" />

                <div class="phone-bottom">
                    <div class="bottom-chat">
                        <div class="chat-voice"></div>
                        <div class="chat-input"></div>
                        <div class="chat-emoji"></div>
                        <div class="chat-more"></div>
                    </div>
                    <div class="bottom-bar">
                        <span class="bar"></span>
                    </div>
                </div>
            </div>
        </div>
    </div>
</template>

<script setup lang="ts">
import { ref, computed } from 'vue'
import PhoneBody from './PhoneBody.vue'
import { useSetStore } from '@/store/useSetStore'

const setStore = useSetStore()

const setForm = ref(setStore.form);

const props = defineProps({
    position: {
        type: String,
        default: 'left'
    },
    msgList: {
        type: Array,
        default: () => []
    }
})

const phoneSigleClass = computed(() => {
    return 'phone-sigle-v' + setForm.value.mobileSignal
})

const wifiClass = computed(() => {
    return 'phone-wifi-s' + setForm.value.wifiSignal
})
</script>

这里因为手机上的设置是能进行更改的,所以将参数存在了 Pinia 中,方便全局调用。

而消息部分以消息类型进行划分,每种消息类型单独切割成组件。

xml 复制代码
<template>
    <div class="phone-body" ref="chatBoxRef">
        <div class="msg-content">
            <component v-for="item in messageList" :key="item.id" :is="item.component" :msgInfo="item" :position="props.position" />
        </div>
    </div>
</template>

<script setup lang="ts">
import { computed, onMounted, ref } from 'vue'
import MsgText from './MsgText.vue'
import MsgVoice from './MsgVoice.vue'
import MsgTime from './MsgTime.vue'

const components = [
    {
        msgType: 'text',
        component: MsgText
    },
    {
        msgType: 'voice',
        component: MsgVoice
    },
    {
        msgType: 'time',
        component: MsgTime
    }
]

const props = defineProps({
    position: {
        type: String,
        default: () => 'left'
    },
    msgList: {
        type: Array,
        default: () => []
    }
})

const chatBoxRef = ref<HTMLElement>()

const messageList = computed(() => {
    const list = props.msgList.map((item: any) => {
        return {
            ...item,
            // 根据消息类型确定使用哪种组件
            component: components.find((component: any) => component.msgType === item.msgType)?.component || MsgText
        }
    })
    return list
})

onMounted(() => {
    // 监听消息列表变化,滚动到底部
    const chatBox = chatBoxRef.value as HTMLElement;
    const scrollToBottom = () => {
        if (chatBox) {
            chatBox.scrollTop = chatBox.scrollHeight
        }
    }
    const observer = new MutationObserver(scrollToBottom);
    observer.observe(chatBox, {
        childList: true,
        subtree: true
    });
})
</script>

总结

其实这个项目技术上来说不复杂,最困难的地方其实是怎样100%复刻微信的UI样式,还有iPhone的头等等样式。

后面会继续维护这个项目,大家可以关注一波,如果能够点个Start⭐那就最为感谢了!

gitee.com/maple2133/v...

相关推荐
志尊宝3 分钟前
Vue3 零基础每日笔记(010):watchEffect——用到谁就自动听谁的“懒人侦听器“
前端·vue.js·笔记
狗哥哥8 分钟前
从“看对方向”到“做出行动”:投资决策卡
前端
拖孩13 分钟前
一个人 + AI 做的小程序,一个月赚了 36 块
前端·后端·微信小程序
weixin_4407305021 分钟前
playwright实战-渠道应用操作
开发语言·前端·python
lhldsg29 分钟前
从零构建智慧场馆解决方案小程序:开发全流程实战
java·前端·小程序
IMPYLH41 分钟前
HTML 的 <rp> 元素
前端·javascript·html
晴天161 小时前
npm install -f(--force)深度解析:作用原理、报错根源与风险避坑指南
前端·npm·node.js
IT_陈寒1 小时前
React的状态更新竟然不是同步的?!坑了我一整天
前端·人工智能·后端
科里 Coralyx1 小时前
CoT Toolformer ReAct:工具调用、推理链与 Agent 的起源
前端·react.js·前端框架
xy34531 小时前
谷歌Lighthouse安装指南(Chrome扩展程序可视化操作)
前端·chrome·测试工具·lighthouse