最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

vue實(shí)現(xiàn)聊天框自動滾動的示例代碼

 更新時間:2023年05月30日 10:35:28   作者:一步一步往上爬的小蝸牛  
本文主要介紹了vue實(shí)現(xiàn)聊天框自動滾動的示例代碼,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧

需求   

1、聊天數(shù)據(jù)實(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一個固定高度 |  scroll-into-view: 值應(yīng)為某子元素id(id不能以數(shù)字開頭)。設(shè)置哪個方向可滾動,則在哪個方向滾動到該元素 -->
    <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的元素來說這個屬性一直是0,單位px,為只讀元素。

簡單來說就是——盒子的原始高度,具體可參考下圖:

(2)offsetHeight:包括padding、border、水平滾動條,但不包括margin的元素的高度。對于inline的元素來說這個屬性一直是0,單位px,為只讀元素。

簡單來說就是——盒子的原始高度+padding+border+滾動條,具體可參考下圖:

(3)scrollHeight 這個只讀屬性是一個元素內(nèi)容高度的度量,包括由于溢出導(dǎo)致的視圖中不可見內(nèi)容。

簡單來說就是——盒子里面包含的內(nèi)容的真實(shí)高度,具體可參考下圖:

(4)scrollTop: 代表在有滾動條時,滾動條向下滾動的距離也就是元素頂部被遮住部分的高度。在沒有滾動條時 scrollTop==0 恒成立。單位px,可讀可設(shè)置。

MDN解釋:一個元素的 scrollTop 值是這個元素的內(nèi)容頂部(被卷起來的)到它的視口可見內(nèi)容(的頂部)的距離的度量。當(dāng)一個元素的內(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一個固定高度 -->
    <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)建新的元素的過程,這個過程是屬于異步的,所以為了防止異步創(chuàng)建元素導(dǎo)致獲取高度不準(zhǔn)確,我們可以等待一段時間,等元素創(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

Taro 文檔

到此這篇關(guān)于vue實(shí)現(xiàn)聊天框自動滾動的示例代碼的文章就介紹到這了,更多相關(guān)vue 聊天框自動滾動內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 基于Vue+echarts編寫一個折線圖

    基于Vue+echarts編寫一個折線圖

    這篇文章主要為大家詳細(xì)介紹了如何利用Vue和Echarts實(shí)現(xiàn)繪制折線圖,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2023-05-05
  • Vue項目中如何運(yùn)用vuex的實(shí)戰(zhàn)記錄

    Vue項目中如何運(yùn)用vuex的實(shí)戰(zhàn)記錄

    如果說是JQuery是手工作坊,那么Vue.js就像是一座工廠,雖然Vue.js做的任何事情JQuery都可以做,但無論是代碼量還是流程規(guī)范性都是前者較優(yōu),下面這篇文章主要給大家介紹了關(guān)于Vue項目中如何運(yùn)用vuex的相關(guān)資料,需要的朋友可以參考下
    2021-09-09
  • Vue實(shí)現(xiàn)路由傳參的四種方式

    Vue實(shí)現(xiàn)路由傳參的四種方式

    在單頁應(yīng)用里,路由是連接頁面與數(shù)據(jù)的橋梁,Vue Router 提供了四種方式把「參數(shù)」從地址欄、內(nèi)存甚至編譯期注入到組件,理解它們的差異,才能在面試和線上故障中游刃有余,下面小編為大家詳細(xì)介紹一下
    2025-09-09
  • vue學(xué)習(xí)筆記之slot插槽基本用法實(shí)例分析

    vue學(xué)習(xí)筆記之slot插槽基本用法實(shí)例分析

    這篇文章主要介紹了vue學(xué)習(xí)筆記之slot插槽基本用法,結(jié)合實(shí)例形式分析了vue slot插槽基本使用方法與操作注意事項,需要的朋友可以參考下
    2020-02-02
  • vue中的el-form表單rule校驗問題(特殊字符、中文、數(shù)字等)

    vue中的el-form表單rule校驗問題(特殊字符、中文、數(shù)字等)

    這篇文章主要介紹了vue中的el-form表單rule校驗問題(特殊字符、中文、數(shù)字等),具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-05-05
  • 在vue中實(shí)現(xiàn)嵌套頁面(iframe)

    在vue中實(shí)現(xiàn)嵌套頁面(iframe)

    這篇文章主要介紹了在vue中實(shí)現(xiàn)嵌套頁面(iframe),具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-07-07
  • Vue項目中實(shí)現(xiàn)無感Token刷新的示例

    Vue項目中實(shí)現(xiàn)無感Token刷新的示例

    在前端項目中,Token用于用戶認(rèn)證和權(quán)限管理,文章介紹了在Vue項目中實(shí)現(xiàn)Token的無感刷新,包括Token刷新的原理、攔截器的應(yīng)用和處理Token過期的方法,感興趣的可以了解一下
    2024-11-11
  • 移動端Vue2.x Picker的全局調(diào)用實(shí)現(xiàn)

    移動端Vue2.x Picker的全局調(diào)用實(shí)現(xiàn)

    這篇文章主要介紹了移動端Vue2.x Picker的全局調(diào)用實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-03-03
  • vue+webpack 打包文件 404 頁面空白的解決方法

    vue+webpack 打包文件 404 頁面空白的解決方法

    下面小編就為大家分享一篇vue+webpack 打包文件 404 頁面空白的解決方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2018-02-02
  • vue插件開發(fā)之使用pdf.js實(shí)現(xiàn)手機(jī)端在線預(yù)覽pdf文檔的方法

    vue插件開發(fā)之使用pdf.js實(shí)現(xiàn)手機(jī)端在線預(yù)覽pdf文檔的方法

    這篇文章主要介紹了vue插件開發(fā)之使用pdf.js實(shí)現(xiàn)手機(jī)端在線預(yù)覽pdf文檔的方法,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-07-07

最新評論

榆社县| 宜兰县| 陕西省| 会理县| 行唐县| 南部县| 深水埗区| 曲沃县| 通江县| 林州市| 永修县| 印江| 江永县| 交口县| 永康市| 汉中市| 太湖县| 赞皇县| 兰溪市| 兰考县| 喀喇沁旗| 城口县| 昭通市| 崇信县| 五大连池市| 托克托县| 澎湖县| 三亚市| 洞口县| 孟州市| 长兴县| 竹山县| 界首市| 鄂伦春自治旗| 青岛市| 龙川县| 黎川县| 阿合奇县| 昌都县| 安新县| 阳山县|