vue基于websocket實現(xiàn)智能聊天及吸附動畫效果
前言:
發(fā)現(xiàn)這篇文章寫的有點多,我總結(jié)一下整體思路:
首先這個功能市面上挺多的,我是參考了幾家公司的功能實現(xiàn),發(fā)現(xiàn)他們的整體功能實現(xiàn)和下面我的截圖類似。
首先核心功能是基于websocket實現(xiàn)用戶輸入文字服務(wù)器根據(jù)用戶輸入返回數(shù)據(jù)渲染在頁面上,而這個功能我不推薦直接使用原生,而是使用封裝好的,就像文章里封裝的socket.js,這個文件很簡單就對外提供三個方法,一個是和后端通信連接,再一個是接收后端的數(shù)據(jù),最后一個是發(fā)送數(shù)據(jù)給后端。
其次,我們需要一個聊天框,我使用的Jwchat插件,優(yōu)點是功能比較全類似QQ聊天,使用方法也很簡單,但是話說回來,這個插件的很多樣式需要修改,尤其對于要做頁面適配的項目,所以這個插件不是特別推薦,還有一個點就是他沒有關(guān)閉按鈕,做的比較粗糙,我是通過在生命周期中使用原生JS添加的關(guān)閉圖標,然后再通過watch動態(tài)的選擇要不要創(chuàng)建刪除按鈕。
最后說一下動畫,其實就是一個聊天框,一個小框,通過點擊使v-show綁定的變量來實現(xiàn)切換效果。這個吸附效果是我用CSS動畫將原本的一個長方形框框翻轉(zhuǎn)180度后在平移模擬吸附效果,其實正兒八經(jīng)做的話需要計算頁面寬度和元素位置計算出一個比例然后再設(shè)置一個動畫效果,這個GitHub上還是有挺多的,注意的是很多實現(xiàn)都是移動端的,PC端用不了,是個坑。再一個動畫就是打開關(guān)閉時的效果,使用的是elementUI自帶的動畫效果。
1.效果如下:
2.主要功能:
2.1.基于websocket實現(xiàn)聊天功能,封裝了一個socket.js文件
2.2使用Jwchat插件實現(xiàn)類似QQ、微信電腦端的功能
(其實并不是很好用,但考慮到后續(xù)可能會使用其功能就先用了)
2.3動畫效果(如關(guān)閉打開時動畫、吸附效果及其他效果)
3.實現(xiàn)步驟:
3.1.實現(xiàn)websocket聊天功能
首先封裝了一個socket.js文件;需要主要的是將socket.js中URL修改成自己的
封裝的websocke暴露三個接口
- sendSock用于發(fā)送數(shù)據(jù),發(fā)給后端
- createWebSocket用于創(chuàng)建連接、接收數(shù)據(jù)并進行處理
- closeSock 用于關(guān)閉連接
3.2.在頁面中的使用方法:
第一步:導(dǎo)入文件
import { sendSock, createWebSocket, closeSock } from "@/api/socket";
第二步:初始化時建立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ā)生錯誤的回調(diào)方法 websock.onerror = function () { console.log("WebSocket連接發(fā)生錯誤"); //createWebSocket();啊,發(fā)現(xiàn)這樣寫會創(chuàng)建多個連接,加延時也不行 }; } // 實際調(diào)用的方法 function sendSock(agentData ) { if (websock.readyState === websock.OPEN) { // 若是ws開啟狀態(tài) websocketsend(agentData); } else if (websock.readyState === websock.CONNECTING) { // 若是 正在開啟狀態(tài),則等待1s后重新調(diào)用 setTimeout(function () { sendSock(agentData); }, 1000); } else { // 若未開啟 ,則等待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類型時 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("連接打開"); } export { sendSock, createWebSocket, closeSock };
4.使用Jwchat插件實現(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 會話內(nèi)容 Array - [] toolConfig 工具欄配置 Object - {} width JwChat界面框?qū)挾?string - 750px height JwChat界面框高度 string - 570px config 組件配置 Object - {} scrollType 消息自動到低 String scroll noroll showRightBox 顯示右邊內(nèi)容 Boolean false true winBarConfig 多窗口配置 quickList 自動匹配快捷回復(fù) @enter 輸入框點擊就發(fā)送或者回車觸發(fā)的事件 輸入的原始數(shù)據(jù) @clickTalk 點擊聊天框列中的用戶和昵稱觸發(fā)事件 當前對話數(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: "猜您想問:", 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: "", // 會話內(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: "最簡單、最便捷", callback: this.bindCover, historyConfig: { show: true, tip: "滾動到頂時候顯示的提示", callback: this.bindLoadHistory, }, // 自動匹配快捷回復(fù) quickList: [ { text: "外面的煙花奮力的燃著,屋里的人激情的說著情話", id: 10 }, { text: "假如你是云,我就是雨,一生相伴,風風雨雨;", id: 11 }, { text: "即使淚水在眼中打轉(zhuǎn),我依舊可以笑的很美,這是你學(xué)不來的堅強。", id: 12, }, { text: " 因為不知來生來世會不會遇到你,所以今生今世我會加倍愛你。", id: 13, }, ], }, }; }, methods: { // 切換用戶窗口,加載對應(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; }, []); } }, // 點擊聊天框列中的用戶和昵稱觸發(fā)事件 talkEvent(play) { console.log(play); }, // 輸入框點擊就發(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à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 當前點擊的按鈕 * @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.動畫效果
吸附效果
使用v-show綁定變量控制顯示隱藏
// 吸附效果 xiFu () { setTimeout(() => { //10秒后自動隱藏小空間轉(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實現(xiàn)智能聊天及吸附動畫效果的文章就介紹到這了,更多相關(guān)vue websocket智能聊天吸附動畫內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Vue2.5學(xué)習(xí)筆記之如何在項目中使用和配置Vue
這篇文章主要介紹了Vue2.5學(xué)習(xí)筆記之如何在項目中使用和配置Vue的相關(guān)知識,非常不錯,具有一定的參考借鑒價值,需要的朋友可以參考下2018-09-09VUE Error: getaddrinfo ENOTFOUND localhost
這篇文章主要介紹了VUE Error: getaddrinfo ENOTFOUND localhost,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-05-05vue如何實現(xiàn)左右滑動tab(vue-touch)
這篇文章主要介紹了vue如何實現(xiàn)左右滑動tab(vue-touch),具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-07-07vue中使用file-saver導(dǎo)出文件的全過程記錄
這篇文章主要給大家介紹了關(guān)于vue中使用file-saver導(dǎo)出文件的相關(guān)資料,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2022-02-02Vue中判斷語句與循環(huán)語句基礎(chǔ)用法及v-if和v-for的注意事項詳解
在Vue指令中,最經(jīng)常被用于做邏輯操作的指令,下面這篇文章主要給大家介紹了關(guān)于Vue中判斷語句與循環(huán)語句基礎(chǔ)用法及v-if和v-for注意事項的相關(guān)資料,文中通過圖文介紹的非常詳細,需要的朋友可以參考下2022-08-08vue使用自定義指令實現(xiàn)按鈕權(quán)限展示功能
這篇文章主要介紹了vue中使用自定義指令實現(xiàn)按鈕權(quán)限展示功能,本文結(jié)合實例代碼給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2022-04-04