vue實(shí)現(xiàn)聊天框自動滾動的示例代碼
需求
1、聊天數(shù)據(jù)實(shí)時(shí)更新渲染到頁面
2、頁面高度隨聊天數(shù)據(jù)增加而增加
3、豎向滾動
4、當(dāng)用戶輸入聊天內(nèi)容或者接口返回聊天內(nèi)容渲染在頁面后,自動滾動到底部
5、提供點(diǎn)擊事件操控滾動條上下翻動
環(huán)境依賴
- vue:@vue/cli 5.0.8
- taro:v3.4.1
實(shí)現(xiàn)方案
方案一:元素設(shè)置錨點(diǎn),使用scrollIntoView() 方法滑動
Element 接口的 scrollIntoView() 方法會滾動元素的父容器,使被調(diào)用 scrollIntoView() 的元素對用戶可見
1、語法
element.scrollIntoView(); // 等同于 element.scrollIntoView(true) element.scrollIntoView(alignToTop); // alignToTop為Boolean 型參數(shù),true/false element.scrollIntoView(scrollIntoViewOptions); // Object 型參數(shù)
2、參數(shù)
(1)alignToTop(可選)
類型:Boolean
- 如果為true,元素的頂端將和其所在滾動區(qū)的可視區(qū)域的頂端對齊。對應(yīng)的 scrollIntoViewOptions: {block: “start”, inline: “nearest”}。該參數(shù)的默認(rèn)值為true。
- 如果為false,元素的底端將和其所在滾動區(qū)的可視區(qū)域的底端對齊。對應(yīng)的scrollIntoViewOptions: {block: “end”, inline: “nearest”}。
(2)scrollIntoViewOptions (可選)
類型:對象
behavior 【可選】
定義動畫的過渡效果,取值為 auto/smooth。默認(rèn)為 “auto”。
block 【可選】
定義垂直方向的對齊, 取值為 start/center/end/nearest 。默認(rèn)為 “start”。
inline 【可選】
定義水平方向的對齊, 取值為 start/center/end/nearest。默認(rèn)為 “nearest”。
代碼實(shí)現(xiàn)如下:
<template> <view class="main" id="main"> <!-- scroll-y:允許縱向滾動 默認(rèn): false | 給scroll-view一個(gè)固定高度 | scroll-into-view: 值應(yīng)為某子元素id(id不能以數(shù)字開頭)。設(shè)置哪個(gè)方向可滾動,則在哪個(gè)方向滾動到該元素 --> <scroll-view class="mainbody" id="mainbody" scroll-with-animation :scroll-y="true" :scroll-into-view="scrollId" style="height:960px;" :enhanced=true scrollIntoViewAlignment="center" @scrolltoupper="upper" @scrolltolower="lower" @scroll="scroll" :scrollWithAnimation="true"> <view v-for="(item, index) in contentTypeit.arr" v-bind:key="index" :class="['info', 'content-questionBlock']"> <view :class="['content']" :id="item.id">{{ item.content }} </view> </view> <view @click="sendMsg" id="sendMsg"></view> <view @click="pageUp" id="pageUp" style="visibility: hidden;"></view> <view @click="pageDown" id="pageDown" style="visibility: hidden;"></view> </scroll-view> </view> </template> <script> import { ref, reactive, toRaw } from 'vue' export default { setup () { const contentTypeit = reactive({ arr: [] }) const scrollId = ref('id0') //scroll ID值 const scrollCursor = ref('id0') const number = ref(0) //https://blog.csdn.net/weixin_43398820/article/details/119963930 // 會話內(nèi)容 // 獲取對話結(jié)果 const sendMsg = function () { setContent( 'dfasdfsfsafdsafsafsdfsafsdfsdfdsfsafdsfsadfsafggfdhfhfjgfjhsdgdsfgasfsafdsafsagdhgfhfdhsgdsgdsgdgafsadfdsfdsfsadfhghsdfgsafdsaf') } // 設(shè)置對話內(nèi)容 const setContent = function (msg) { let idValue = 'id' + number.value const currentObjTypeit = { 'content': msg, 'id': idValue } let _arr = toRaw(contentTypeit.arr) let _arrTmp = _arr.concat(currentObjTypeit) contentTypeit.arr = _arrTmp number.value = number.value + 1; scrollCursor.value = idValue //https://blog.csdn.net/weixin_46511008/article/details/126629361 setTimeout(() => { if (number.value !== 0) { let idValueSlide = 'id' + (number.value - 1) document.getElementById(idValueSlide).scrollIntoView({ behavior: 'smooth', block: 'center', inline: 'end' }) } }, 100); } const scroll = function (e) { // console.log('scroll', e) } const upper = function (e) { // console.log('upper', e) } const lower = function (e) { // console.log('lower', e) } const pageUp = function (e) { console.log(scrollCursor.value) if (scrollCursor.value === undefined || scrollCursor.value === '' || scrollCursor.value.length < 3) { return; } let scrollCursorValue = scrollCursor.value.substring(2); console.log(scrollCursorValue); if (scrollCursorValue >= 1) { scrollCursorValue = scrollCursorValue - 1; scrollCursor.value = 'id' + scrollCursorValue; } setTimeout(function(){ if (document.querySelector('#'+ scrollCursor.value) === null) { return; } document.querySelector('#'+ scrollCursor.value).scrollIntoView() }, 200); } const pageDown = function (e) { console.log(scrollCursor.value) if (scrollCursor.value === undefined || scrollCursor.value === '' || scrollCursor.value.length < 3) { return; } let scrollCursorValue = scrollCursor.value.substring(2); console.log(scrollCursorValue); if (scrollCursorValue < contentTypeit.arr.length - 1) { scrollCursorValue = scrollCursorValue - (-1) scrollCursor.value = 'id' + scrollCursorValue; } if (scrollCursorValue === contentTypeit.arr.length - 1) { setTimeout(function(){ if (document.querySelector('#'+ scrollCursor.value) === null) { return; } document.querySelector('#'+ scrollCursor.value).scrollIntoView(false) }, 500); } else { setTimeout(function() { if (document.querySelector('#'+ scrollCursor.value) === null) { return; } document.querySelector('#'+ scrollCursor.value).scrollIntoView({ behavior: "smooth", // 平滑過渡 block: "end", // 上邊框與視窗頂部平齊。默認(rèn)值 }) }, 100); } } return { contentTypeit, scrollId, lower, upper, scroll, sendMsg, pageUp, pageDown, } } } </script> <style lang="scss"> .main { height: 100%; width: 100%; background-color: rgba(204, 204, 204, 0.32); overflow-x: hidden; overflow-y: auto; } .mainbody { max-width: 100%; background-size: contain; padding-bottom: 100px; } .info { display: flex; margin: 10px 3%; } .content-question { color: #0b4eb4; background-color: #ffffff; padding-left: 20px; } .content-questionBlock { align-items: center; } .content { background-color: #fff; border-radius: 16px; padding: 20px; margin-left: 20px; max-width: 82%; height: 100%; font-size: 36px; font-family: PingFangSC-Medium, PingFang SC; font-weight: 500; color: #0a0a27; line-height: 60px; word-break: break-all; } </style>
效果調(diào)試:
(1)打開瀏覽器,按下F12進(jìn)入調(diào)試模式;
(2)在console窗口,多次調(diào)用document.getElementById('sendMsg').click(),使得對話內(nèi)容超出界面高度,可觀察到自動滾動效果;
(3)在console窗口,調(diào)用document.getElementById('pageUp').click(),若沒有滾動,可調(diào)整代碼或者調(diào)用多次(取決于scrollIntoView()的參數(shù)),可觀察到向上滾動;接著調(diào)用document.getElementById('pageDown').click(),可觀察到向下滾動。
效果圖如下:
方案二: 更改scrollTop取值,進(jìn)行滾動
首先我們需要了解 clientHeight、offsetHeight、scrollHeight、scrollTop 的概念
簡單介紹:
- clientHeight:網(wǎng)頁可見區(qū)域高
- offsetHeight:網(wǎng)頁可見區(qū)域高(包括邊線的高)
- scrollHeight:網(wǎng)頁正文全文高
- scrollTop:網(wǎng)頁被卷去的高
具體說明:
(1)clientHeight:包括padding 但不包括 border、水平滾動條、margin的元素的高度。對于inline的元素來說這個(gè)屬性一直是0,單位px,為只讀元素。
簡單來說就是——盒子的原始高度,具體可參考下圖:
(2)offsetHeight:包括padding、border、水平滾動條,但不包括margin的元素的高度。對于inline的元素來說這個(gè)屬性一直是0,單位px,為只讀元素。
簡單來說就是——盒子的原始高度+padding+border+滾動條,具體可參考下圖:
(3)scrollHeight:
這個(gè)只讀屬性是一個(gè)元素內(nèi)容高度的度量,包括由于溢出導(dǎo)致的視圖中不可見內(nèi)容。
簡單來說就是——盒子里面包含的內(nèi)容的真實(shí)高度,具體可參考下圖:
(4)scrollTop: 代表在有滾動條時(shí),滾動條向下滾動的距離也就是元素頂部被遮住部分的高度。在沒有滾動條時(shí) scrollTop==0 恒成立。單位px,可讀可設(shè)置。
MDN解釋:一個(gè)元素的 scrollTop 值是這個(gè)元素的內(nèi)容頂部(被卷起來的)到它的視口可見內(nèi)容(的頂部)的距離的度量。當(dāng)一個(gè)元素的內(nèi)容沒有產(chǎn)生垂直方向的滾動條,那它的 scrollTop 值為0,具體可參考下圖:
實(shí)現(xiàn)算法:卷起的高度(scrollTop) = 總的內(nèi)容高度(scrollHeight) - 聊天區(qū)域盒子大小 (offsetHeight);
代碼實(shí)現(xiàn)如下:
<template> <view class="main" ref="scrollContainer" id="main"> <!-- scroll-y:允許縱向滾動 默認(rèn): false | 給scroll-view一個(gè)固定高度 --> <scroll-view class="mainbody" id="mainbody" scroll-with-animation :scroll-y="true" style="height:960px;" :enhanced=true scrollIntoViewAlignment="center" @scrolltoupper="upper" @scrolltolower="lower" @scroll="scroll" :scrollWithAnimation="true"> <view v-for="(item, index) in contentTypeit.arr" v-bind:key="index" :class="['info', 'content-questionBlock']"> <view :class="['content']" :id="item.id">{{ item.content }} </view> </view> <view @click="sendMsg" id="sendMsg"></view> <view @click="pageUp" id="pageUp" style="visibility: hidden;"></view> <view @click="pageDown" id="pageDown" style="visibility: hidden;"></view> </scroll-view> </view> </template> <script> import { ref, reactive, toRaw } from 'vue' import Taro from "@tarojs/taro"; export default { setup () { const contentTypeit = reactive({ arr: [] }) const scrollId = ref('id0') //scroll ID值 const scrollCursor = ref('id0') const scrollCursorStore = ref(0) // 自動 scrollTop //https://www.cnblogs.com/hmy-666/p/14717484.html 滾動原理與實(shí)現(xiàn) //由于插入新的消息屬于創(chuàng)建新的元素的過程,這個(gè)過程是屬于異步的,所以為了防止異步創(chuàng)建元素導(dǎo)致獲取高度不準(zhǔn)確,我們可以等待一段時(shí)間,等元素創(chuàng)建完畢之后再獲取元素高度 const scrollDownInterval = function () { let idDom = document.getElementById('mainbody') console.log("===================scrollTop,clientHeight,scrollHeight,offsetHeight", idDom.scrollTop, idDom.clientHeight, idDom.scrollHeight, idDom.offsetHeight) let currentScrollPosition = scrollCursorStore.value; Taro.nextTick(() => { console.log('scroll start...', idDom.scrollTop) let scrollInterval = setInterval(() => { if ( (idDom.scrollTop === idDom.scrollHeight - idDom.offsetHeight) || (idDom.scrollTop > idDom.scrollHeight - idDom.offsetHeight) ) { scrollCursorStore.value = idDom.scrollTop clearInterval(scrollInterval); console.log('scroll end...', idDom.scrollTop) } else { currentScrollPosition = currentScrollPosition + 100; idDom.scrollTop = currentScrollPosition; scrollCursorStore.value = idDom.scrollTop console.log('scrolling...', idDom.scrollTop) } }, 200) }) } const number = ref(0) //https://blog.csdn.net/weixin_43398820/article/details/119963930 // 會話內(nèi)容 // 獲取對話結(jié)果 const sendMsg = function () { setContent( 'dfasdfsfsafdsafsafsdfsafsdfsdfdsfsafdsfsadfsafggfdhfhfjgfjhsdgdsfgasfsafdsafsagdhgfhfdhsgdsgdsgdgafsadfdsfdsfsadfhghsdfgsafdsaf') } // 設(shè)置對話內(nèi)容 const setContent = function (msg) { let idValue = 'id' + number.value const currentObjTypeit = { 'content': msg, 'id': idValue } let _arr = toRaw(contentTypeit.arr) let _arrTmp = _arr.concat(currentObjTypeit) contentTypeit.arr = _arrTmp number.value = number.value + 1; scrollCursor.value = idValue //https://blog.csdn.net/weixin_46511008/article/details/126629361 scrollDownInterval(); } const scroll = function (e) { // console.log('scroll', e) } const upper = function (e) { // console.log('upper', e) } const lower = function (e) { // console.log('lower', e) } const pageUp = function (e) { let idDom = document.getElementById('mainbody') console.log("===================", idDom.scrollTop, idDom.clientHeight, idDom.scrollHeight, idDom.offsetHeight) let currentScrollPosition = scrollCursorStore.value; scrollCursorStore.value = scrollCursorStore.value - 400 if (scrollCursorStore.value < 0) { scrollCursorStore.value = 0; } Taro.nextTick(() => { console.log('scroll start...', idDom.scrollTop) let scrollInterval = setInterval(() => { if ( (idDom.scrollTop === scrollCursorStore.value) || (idDom.scrollTop < scrollCursorStore.value) ) { clearInterval(scrollInterval); console.log('scroll end...', idDom.scrollTop) } else { currentScrollPosition = currentScrollPosition - 50; idDom.scrollTop = currentScrollPosition; console.log('scrolling...', idDom.scrollTop) } }, 100) }) } const pageDown = function (e) { let idDom = document.getElementById('mainbody') console.log("===================", idDom.scrollTop, idDom.clientHeight, idDom.scrollHeight, idDom.offsetHeight) let currentScrollPosition = scrollCursorStore.value; scrollCursorStore.value = scrollCursorStore.value + 400 if (scrollCursorStore.value > (idDom.scrollHeight - idDom.offsetHeight )) { scrollCursorStore.value = idDom.scrollHeight - idDom.offsetHeight; } Taro.nextTick(() => { console.log('scroll start...', idDom.scrollTop) let scrollInterval = setInterval(() => { if ( (idDom.scrollTop === scrollCursorStore.value) || (idDom.scrollTop > scrollCursorStore.value) ) { clearInterval(scrollInterval); console.log('scroll end...', idDom.scrollTop) } else { currentScrollPosition = currentScrollPosition - (-50); idDom.scrollTop = currentScrollPosition; console.log('scrolling...', idDom.scrollTop) } }, 100) }) } return { contentTypeit, scrollId, lower, upper, scroll, sendMsg, pageUp, pageDown, } } } </script> <style lang="scss"> .main { height: 100%; width: 100%; background-color: rgba(204, 204, 204, 0.32); overflow-x: hidden; overflow-y: auto; } .mainbody { max-width: 100%; background-size: contain; padding-bottom: 100px; } .info { display: flex; margin: 10px 3%; } .content-question { color: #0b4eb4; background-color: #ffffff; padding-left: 20px; } .content-questionBlock { align-items: center; } .content { background-color: #fff; border-radius: 16px; padding: 20px; margin-left: 20px; max-width: 82%; height: 100%; font-size: 36px; font-family: PingFangSC-Medium, PingFang SC; font-weight: 500; color: #0a0a27; line-height: 60px; word-break: break-all; } </style>
效果調(diào)試:
(1)打開瀏覽器,按下F12進(jìn)入調(diào)試模式;
(2)在console窗口,多次調(diào)用document.getElementById('sendMsg').click(),使得對話內(nèi)容超出界面高度,可觀察到自動滾動效果;
(3)在console窗口,調(diào)用document.getElementById('pageUp').click(),可觀察到向上滾動;接著調(diào)用document.getElementById('pageDown').click(),可觀察到向下滾動。
效果圖如下:
建議
方案一由于接口支持,滑動效果更平滑,但是翻頁只能調(diào)到指定錨點(diǎn),滑動步長不可控,大部分場景不能滿足需求。
方案二可以自行調(diào)整翻頁的步長,按需滑動至指定高度,不過滑動動畫需要自行實(shí)現(xiàn),看起來卡頓感較強(qiáng)。
總體來說,建議使用方案二。
參考鏈接
https://blog.csdn.net/weixin_46511008/article/details/126629361
https://www.cnblogs.com/wq805/p/16399600.html
https://www.cnblogs.com/hmy-666/p/14717484.html
到此這篇關(guān)于vue實(shí)現(xiàn)聊天框自動滾動的示例代碼的文章就介紹到這了,更多相關(guān)vue 聊天框自動滾動內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
vue項(xiàng)目中跳轉(zhuǎn)到外部鏈接的實(shí)例講解
今天小編就為大家分享一篇vue項(xiàng)目中跳轉(zhuǎn)到外部鏈接的實(shí)例講解,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧2018-09-09Vue項(xiàng)目中實(shí)現(xiàn)帶參跳轉(zhuǎn)功能
最近做了一個(gè)手機(jī)端系統(tǒng),其中遇到了父頁面需要攜帶參數(shù)跳轉(zhuǎn)至子頁面的問題,現(xiàn)已解決,下面分享一下實(shí)現(xiàn)過程,感興趣的朋友一起看看吧2021-04-04element-plus 在vue3 中不生效的原因解決方法(element-plus引入)
這篇文章主要介紹了element-plus 在vue3 中不生效的原因解決方法(element-plus引入),本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-08-08使用Vue3和ApexCharts實(shí)現(xiàn)3D徑向條形圖的代碼
徑向條形圖是一種用于可視化單一數(shù)據(jù)點(diǎn)及其與目標(biāo)或理想值的關(guān)系的圖表類型,它在顯示進(jìn)度、完成率或其他類似度量時(shí)非常有用,本文給大家介紹了使用Vue3和ApexCharts實(shí)現(xiàn)3D徑向條形圖,感興趣的小伙伴可以參考閱讀下2024-06-06vue-cli3在main.js中console.log()會報(bào)錯(cuò)的解決
這篇文章主要介紹了vue-cli3在main.js中console.log()會報(bào)錯(cuò)的解決方案,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-04-04uniapp仿微信聊天界面效果實(shí)例(vue3組合式版本)
這篇文章主要介紹了uniapp仿微信聊天界面的相關(guān)資料,這里提及了一個(gè)時(shí)間工具包timeMethod.js,該工具包可能提供了一系列時(shí)間處理的功能,如格式化日期、計(jì)算時(shí)間差等,以便在消息格式中正確展示時(shí)間信息,使用此類工具包可以大大提高開發(fā)效率,需要的朋友可以參考下2024-10-10vue刪除html內(nèi)容的標(biāo)簽樣式實(shí)例
今天小編就為大家分享一篇vue刪除html內(nèi)容的標(biāo)簽樣式實(shí)例,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧2018-09-09vue 使用axios 數(shù)據(jù)請求第三方插件的使用教程詳解
這篇文章主要介紹了vue 使用axios 數(shù)據(jù)請求第三方插件的使用 ,本文給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2019-07-07詳解vue中使用vue-quill-editor富文本小結(jié)(圖片上傳)
這篇文章主要介紹了詳解vue中使用vue-quill-editor富文本小結(jié)(圖片上傳),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-04-04