vue基于websocket實(shí)現(xiàn)智能聊天及吸附動(dòng)畫(huà)效果
前言:
發(fā)現(xiàn)這篇文章寫(xiě)的有點(diǎn)多,我總結(jié)一下整體思路:
首先這個(gè)功能市面上挺多的,我是參考了幾家公司的功能實(shí)現(xiàn),發(fā)現(xiàn)他們的整體功能實(shí)現(xiàn)和下面我的截圖類似。
首先核心功能是基于websocket實(shí)現(xiàn)用戶輸入文字服務(wù)器根據(jù)用戶輸入返回?cái)?shù)據(jù)渲染在頁(yè)面上,而這個(gè)功能我不推薦直接使用原生,而是使用封裝好的,就像文章里封裝的socket.js,這個(gè)文件很簡(jiǎn)單就對(duì)外提供三個(gè)方法,一個(gè)是和后端通信連接,再一個(gè)是接收后端的數(shù)據(jù),最后一個(gè)是發(fā)送數(shù)據(jù)給后端。
其次,我們需要一個(gè)聊天框,我使用的Jwchat插件,優(yōu)點(diǎn)是功能比較全類似QQ聊天,使用方法也很簡(jiǎn)單,但是話說(shuō)回來(lái),這個(gè)插件的很多樣式需要修改,尤其對(duì)于要做頁(yè)面適配的項(xiàng)目,所以這個(gè)插件不是特別推薦,還有一個(gè)點(diǎn)就是他沒(méi)有關(guān)閉按鈕,做的比較粗糙,我是通過(guò)在生命周期中使用原生JS添加的關(guān)閉圖標(biāo),然后再通過(guò)watch動(dòng)態(tài)的選擇要不要?jiǎng)?chuàng)建刪除按鈕。
最后說(shuō)一下動(dòng)畫(huà),其實(shí)就是一個(gè)聊天框,一個(gè)小框,通過(guò)點(diǎn)擊使v-show綁定的變量來(lái)實(shí)現(xiàn)切換效果。這個(gè)吸附效果是我用CSS動(dòng)畫(huà)將原本的一個(gè)長(zhǎng)方形框框翻轉(zhuǎn)180度后在平移模擬吸附效果,其實(shí)正兒八經(jīng)做的話需要計(jì)算頁(yè)面寬度和元素位置計(jì)算出一個(gè)比例然后再設(shè)置一個(gè)動(dòng)畫(huà)效果,這個(gè)GitHub上還是有挺多的,注意的是很多實(shí)現(xiàn)都是移動(dòng)端的,PC端用不了,是個(gè)坑。再一個(gè)動(dòng)畫(huà)就是打開(kāi)關(guān)閉時(shí)的效果,使用的是elementUI自帶的動(dòng)畫(huà)效果。
1.效果如下:

2.主要功能:
2.1.基于websocket實(shí)現(xiàn)聊天功能,封裝了一個(gè)socket.js文件
2.2使用Jwchat插件實(shí)現(xiàn)類似QQ、微信電腦端的功能
(其實(shí)并不是很好用,但考慮到后續(xù)可能會(huì)使用其功能就先用了)
2.3動(dòng)畫(huà)效果(如關(guān)閉打開(kāi)時(shí)動(dòng)畫(huà)、吸附效果及其他效果)
3.實(shí)現(xiàn)步驟:
3.1.實(shí)現(xiàn)websocket聊天功能
首先封裝了一個(gè)socket.js文件;需要主要的是將socket.js中URL修改成自己的

封裝的websocke暴露三個(gè)接口
- sendSock用于發(fā)送數(shù)據(jù),發(fā)給后端
- createWebSocket用于創(chuàng)建連接、接收數(shù)據(jù)并進(jìn)行處理
- closeSock 用于關(guān)閉連接
3.2.在頁(yè)面中的使用方法:
第一步:導(dǎo)入文件
import { sendSock, createWebSocket, closeSock } from "@/api/socket";
第二步:初始化時(shí)建立websocket連接
created() {
this.init();
......
},
methods: {
init() {
createWebSocket(this.global_callback);
......
},
// websocket的回調(diào)函數(shù),msg表示收到的消息
global_callback(msg) {
console.log("收到服務(wù)器信息:" + msg);
},
},關(guān)閉連接
closeSock();
發(fā)送給后端的方法
sendSock(xxx)
var websock = null;
var global_callback = null;
var serverPort = "80"; // webSocket連接端口
var wsuri = "ws://" + window.location.hostname + ":" + serverPort;
function createWebSocket(callback) {
if (websock == null || typeof websock !== WebSocket) {
initWebSocket(callback);
}
}
function initWebSocket(callback) {
global_callback = callback;
// 初始化websocket
websock = new WebSocket(wsuri);
websock.onmessage = function (e) {
websocketonmessage(e);
};
websock.onclose = function (e) {
websocketclose(e);
};
websock.onopen = function () {
websocketOpen();
};
// 連接發(fā)生錯(cuò)誤的回調(diào)方法
websock.onerror = function () {
console.log("WebSocket連接發(fā)生錯(cuò)誤");
//createWebSocket();啊,發(fā)現(xiàn)這樣寫(xiě)會(huì)創(chuàng)建多個(gè)連接,加延時(shí)也不行
};
}
// 實(shí)際調(diào)用的方法
function sendSock(agentData ) {
if (websock.readyState === websock.OPEN) {
// 若是ws開(kāi)啟狀態(tài)
websocketsend(agentData);
} else if (websock.readyState === websock.CONNECTING) {
// 若是 正在開(kāi)啟狀態(tài),則等待1s后重新調(diào)用
setTimeout(function () {
sendSock(agentData);
}, 1000);
} else {
// 若未開(kāi)啟 ,則等待1s后重新調(diào)用
setTimeout(function () {
sendSock(agentData);
}, 1000);
}
}
function closeSock() {
websock.close();
}
// 數(shù)據(jù)接收
function websocketonmessage(msg) {
// console.log("收到數(shù)據(jù):"+JSON.parse(e.data));
// console.log("收到數(shù)據(jù):"+msg);
// global_callback(JSON.parse(msg.data));
// 收到信息為Blob類型時(shí)
let result = null;
// debugger
if (msg.data instanceof Blob) {
const reader = new FileReader();
reader.readAsText(msg.data, "UTF-8");
reader.onload = (e) => {
result = JSON.parse(reader.result);
//console.log("websocket收到", result);
global_callback(result);
};
} else {
result = JSON.parse(msg.data);
//console.log("websocket收到", result);
global_callback(result);
}
}
// 數(shù)據(jù)發(fā)送
function websocketsend(agentData) {
console.log("發(fā)送數(shù)據(jù):" + agentData);
websock.send(agentData);
}
// 關(guān)閉
function websocketclose(e) {
console.log("connection closed (" + e.code + ")");
}
function websocketOpen(e) {
console.log("連接打開(kāi)");
}
export { sendSock, createWebSocket, closeSock };
4.使用Jwchat插件實(shí)現(xiàn)類似QQ、微信電腦端的功能

4.1步驟
安裝依賴
npm i jwchat -S
main.js 引入配置
//element 必須引入 import ElementUI from 'element-ui'; import 'element-ui/lib/theme-chalk/index.css'; Vue.use(ElementUI); //聊天室-基于element import Chat from 'jwchat'; Vue.use(Chat)
組件中使用
<template>
<div class="jwchat">
<!--
v-model 輸入框中的文字 String - ""
taleList 會(huì)話內(nèi)容 Array - []
toolConfig 工具欄配置 Object - {}
width JwChat界面框?qū)挾?string - 750px
height JwChat界面框高度 string - 570px
config 組件配置 Object - {}
scrollType 消息自動(dòng)到低 String scroll noroll
showRightBox 顯示右邊內(nèi)容 Boolean false true
winBarConfig 多窗口配置
quickList 自動(dòng)匹配快捷回復(fù)
@enter 輸入框點(diǎn)擊就發(fā)送或者回車觸發(fā)的事件 輸入的原始數(shù)據(jù)
@clickTalk 點(diǎn)擊聊天框列中的用戶和昵稱觸發(fā)事件 當(dāng)前對(duì)話數(shù)據(jù)
-->
<JwChat-index
v-model="inputMsg"
:taleList="taleList"
:config="config"
:showRightBox="true"
scrollType="scroll"
:winBarConfig="winBarConfig"
:quickList="config.quickList"
@enter="bindEnter"
@clickTalk="talkEvent"
>
<!-- 窗口右邊欄 -->
<JwChat-rightbox :config="rightConfig" @click="rightClick" />
<!-- 快捷回復(fù) -->
<!-- <JwChat-talk :Talelist="talk" :config="quickConfig" @event="bindTalk" /> -->
<!-- 工具欄自定義插槽 -->
<template slot="tools">
<div style="width: 20rem; text-align: right" @click="toolEvent(12)">
<JwChat-icon type="icon-lishi" title="自定義" />
</div>
</template>
</JwChat-index>
</div>
</template>
<script>
const img = "https://www.baidu.com/img/flexible/logo/pc/result.png";
const listData = [
{
date: "2021/03/02 13:14:21",
mine: false,
name: "留戀人間不羨仙",
img: "https://img0.baidu.com/it/u=3066115177,3339701526&fm=26&fmt=auto&gp=0.jpg",
text: {
system: {
title: "在接入人工前,智能助手將為您首次應(yīng)答。",
subtitle: "猜您想問(wèn):",
content: [
{
id: `system1`,
text: "組件如何使用",
},
{
id: `system2`,
text: "組件參數(shù)在哪里查看",
},
{
id: "system",
text: "我可不可把組件用在商業(yè)",
},
],
},
},
},
];
function getListArr(size) {
const listSize = listData.length;
if (!size) {
size = listSize;
}
let result = [];
for (let i = 0; i < size; i++) {
const item = listData[(Math.random() * listSize) >> 0];
item.id = Math.random().toString(16).substr(-6);
result.push(item);
}
return result;
}
export default {
components: {},
data() {
return {
// 輸入框中的文字
inputMsg: "",
// 會(huì)話內(nèi)容
taleList: [],
// 工具欄配置
tool: {
// show: ['file', 'history', 'img', ['文件1', '', '美圖']],
// showEmoji: false,
callback: this.toolEvent,
},
// 組件配置
config: {
img: "https://img1.baidu.com/it/u=2109725846,3376113789&fm=26&fmt=auto&gp=0.jpg",
name: "JwChat",
dept: "最簡(jiǎn)單、最便捷",
callback: this.bindCover,
historyConfig: {
show: true,
tip: "滾動(dòng)到頂時(shí)候顯示的提示",
callback: this.bindLoadHistory,
},
// 自動(dòng)匹配快捷回復(fù)
quickList: [
{ text: "外面的煙花奮力的燃著,屋里的人激情的說(shuō)著情話", id: 10 },
{ text: "假如你是云,我就是雨,一生相伴,風(fēng)風(fēng)雨雨;", id: 11 },
{
text: "即使淚水在眼中打轉(zhuǎn),我依舊可以笑的很美,這是你學(xué)不來(lái)的堅(jiān)強(qiáng)。",
id: 12,
},
{
text: " 因?yàn)椴恢獊?lái)生來(lái)世會(huì)不會(huì)遇到你,所以今生今世我會(huì)加倍愛(ài)你。",
id: 13,
},
],
},
};
},
methods: {
// 切換用戶窗口,加載對(duì)應(yīng)的歷史記錄
bindWinBar(play = {}) {
const { type, data = {} } = play;
console.log(play);
if (type === "winBar") {
const { id, dept, name, img } = data;
this.config = { ...this.config, id, dept, name, img };
this.winBarConfig.active = id;
if (id === "win00") {
this.taleList = getListArr();
} else this.taleList = getListArr((Math.random() * 4) >> 0);
}
if (type === "winBtn") {
const { target: { id } = {} } = data;
const { list } = this.winBarConfig;
this.winBarConfig.list = list.reduce((p, i) => {
if (id != i.id) p.push(i);
return p;
}, []);
}
},
// 點(diǎn)擊聊天框列中的用戶和昵稱觸發(fā)事件
talkEvent(play) {
console.log(play);
},
// 輸入框點(diǎn)擊就發(fā)送或者回車觸發(fā)的事件
bindEnter(e) {
console.log(e);
const msg = this.inputMsg;
if (!msg) return;
const msgObj = {
date: "2020/05/20 23:19:07",
text: { text: msg },
mine: true,
name: "JwChat",
img: "https://img1.baidu.com/it/u=31094377,222380373&fm=26&fmt=auto&gp=0.jpg",
};
this.taleList.push(msgObj);
},
/**
* @description: 點(diǎn)擊加載更多的回調(diào)函數(shù)
* @param {*}
* @return {*}
*/
bindLoadHistory() {
const history = new Array(3).fill().map((i, j) => {
return {
date: "2020/05/20 23:19:07",
text: { text: j + new Date() },
mine: false,
name: "JwChat",
img: "https://img1.baidu.com/it/u=31094377,222380373&fm=26&fmt=auto&gp=0.jpg",
};
});
let list = history.concat(this.list);
this.taleList = list;
console.log("加載歷史", list, history);
},
/**
* @description:
* @param {*} type 當(dāng)前點(diǎn)擊的按鈕
* @param {*} plyload 附加文件或者需要處理的數(shù)據(jù)
* @return {*}
*/
toolEvent(type, plyload) {
console.log("tools", type, plyload);
},
bindCover(event) {
console.log("header", event);
},
rightClick(type) {
console.log("rigth", type);
},
},
mounted() {
this.taleList = getListArr();
},
};
</script>
<style>
.jwchat {
height: 100vh;
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>5.動(dòng)畫(huà)效果
吸附效果
使用v-show綁定變量控制顯示隱藏
// 吸附效果
xiFu () {
setTimeout(() => {
//10秒后自動(dòng)隱藏小空間轉(zhuǎn)為吸附效果
this.isMouse = false
}, 5000)
},@keyframes move {
0% {
transform: translateX(0px) rotateY(20deg);
}
100% {
transform: translateX(1.0417rem) rotateY(180deg);
}
}
.cssDongHua {
animation: move 2s linear 1s 1 alternate forwards;
}
//template
:class="isMouse ? '' : 'cssDongHua'"
@click="isOpen = !isOpen"
v-show="!isOpen"
@mouseenter="isMouse = true"到此這篇關(guān)于vue基于websocket實(shí)現(xiàn)智能聊天及吸附動(dòng)畫(huà)效果的文章就介紹到這了,更多相關(guān)vue websocket智能聊天吸附動(dòng)畫(huà)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Vue2.5學(xué)習(xí)筆記之如何在項(xiàng)目中使用和配置Vue
這篇文章主要介紹了Vue2.5學(xué)習(xí)筆記之如何在項(xiàng)目中使用和配置Vue的相關(guān)知識(shí),非常不錯(cuò),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2018-09-09
VUE Error: getaddrinfo ENOTFOUND localhost
這篇文章主要介紹了VUE Error: getaddrinfo ENOTFOUND localhost,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-05-05
基于vue-resource jsonp跨域問(wèn)題的解決方法
下面小編就為大家分享一篇基于vue-resource jsonp跨域問(wèn)題的解決方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-02-02
Vue入門之?dāng)?shù)量加減運(yùn)算操作示例
這篇文章主要介紹了Vue入門之?dāng)?shù)量加減運(yùn)算操作,結(jié)合實(shí)例形式分析了vue.js基本數(shù)值運(yùn)算相關(guān)操作技巧,需要的朋友可以參考下2018-12-12
vue如何實(shí)現(xiàn)左右滑動(dòng)tab(vue-touch)
這篇文章主要介紹了vue如何實(shí)現(xiàn)左右滑動(dòng)tab(vue-touch),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-07-07
vue中使用file-saver導(dǎo)出文件的全過(guò)程記錄
這篇文章主要給大家介紹了關(guān)于vue中使用file-saver導(dǎo)出文件的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2022-02-02
Vue中判斷語(yǔ)句與循環(huán)語(yǔ)句基礎(chǔ)用法及v-if和v-for的注意事項(xiàng)詳解
在Vue指令中,最經(jīng)常被用于做邏輯操作的指令,下面這篇文章主要給大家介紹了關(guān)于Vue中判斷語(yǔ)句與循環(huán)語(yǔ)句基礎(chǔ)用法及v-if和v-for注意事項(xiàng)的相關(guān)資料,文中通過(guò)圖文介紹的非常詳細(xì),需要的朋友可以參考下2022-08-08
vue使用自定義指令實(shí)現(xiàn)按鈕權(quán)限展示功能
這篇文章主要介紹了vue中使用自定義指令實(shí)現(xiàn)按鈕權(quán)限展示功能,本文結(jié)合實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-04-04

