赞
踩
1、聊天数据实时更新渲染到页面
2、页面高度随聊天数据增加而增加
3、竖向滚动
4、当用户输入聊天内容或者接口返回聊天内容渲染在页面后,自动滚动到底部
5、提供点击事件操控滚动条上下翻动
vue:@vue/cli 5.0.8
taro:v3.4.1
Element 接口的 scrollIntoView() 方法会滚动元素的父容器,使被调用 scrollIntoView() 的元素对用户可见
1、语法
element.scrollIntoView(); // 等同于 element.scrollIntoView(true)
element.scrollIntoView(alignToTop); // alignToTop为Boolean 型参数,true/false
element.scrollIntoView(scrollIntoViewOptions); // Object 型参数
2、参数
(1)alignToTop(可选)
类型:Boolean
如果为true,元素的顶端将和其所在滚动区的可视区域的顶端对齐。对应的 scrollIntoViewOptions: {block: “start”, inline: “nearest”}。该参数的默认值为true。
如果为false,元素的底端将和其所在滚动区的可视区域的底端对齐。对应的scrollIntoViewOptions: {block: “end”, inline: “nearest”}。
(2)scrollIntoViewOptions (可选)
类型:对象
behavior 【可选】
定义动画的过渡效果,取值为 auto/smooth。默认为 “auto”。
block 【可选】
定义垂直方向的对齐, 取值为 start/center/end/nearest 。默认为 “start”。
inline 【可选】
定义水平方向的对齐, 取值为 start/center/end/nearest。默认为 “nearest”。
代码实现如下:
- <template>
- <view class="main" id="main">
- <!-- scroll-y:允许纵向滚动 默认: false | 给scroll-view一个固定高度 | scroll-into-view: 值应为某子元素id(id不能以数字开头)。设置哪个方向可滚动,则在哪个方向滚动到该元素 -->
- <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
-
- // 会话内容
- // 获取对话结果
- const sendMsg = function () {
- setContent( 'dfasdfsfsafdsafsafsdfsafsdfsdfdsfsafdsfsadfsafggfdhfhfjgfjhsdgdsfgasfsafdsafsagdhgfhfdhsgdsgdsgdgafsadfdsfdsfsadfhghsdfgsafdsaf')
- }
- // 设置对话内容
- 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", // 上边框与视窗顶部平齐。默认值
- })
- }, 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>
效果调试:
(1)打开浏览器,按下F12进入调试模式;
(2)在console窗口,多次调用document.getElementById('sendMsg').click(),使得对话内容超出界面高度,可观察到自动滚动效果;
(3)在console窗口,调用document.getElementById('pageUp').click(),若没有滚动,可调整代码或者调用多次(取决于scrollIntoView()的参数),可观察到向上滚动;接着调用document.getElementById('pageDown').click(),可观察到向下滚动。
效果图如下:
首先我们需要了解 clientHeight、offsetHeight、scrollHeight、scrollTop 的概念
简单介绍:
clientHeight:网页可见区域高
offsetHeight:网页可见区域高(包括边线的高)
scrollHeight:网页正文全文高
scrollTop:网页被卷去的高
具体说明:
(1)clientHeight:包括padding 但不包括 border、水平滚动条、margin的元素的高度。对于inline的元素来说这个属性一直是0,单位px,为只读元素。
简单来说就是——盒子的原始高度,具体可参考下图:
(2)offsetHeight:包括padding、border、水平滚动条,但不包括margin的元素的高度。对于inline的元素来说这个属性一直是0,单位px,为只读元素。
简单来说就是——盒子的原始高度+padding+border+滚动条,具体可参考下图:
(3)scrollHeight:
这个只读属性是一个元素内容高度的度量,包括由于溢出导致的视图中不可见内容。
简单来说就是——盒子里面包含的内容的真实高度,具体可参考下图:
(4)scrollTop: 代表在有滚动条时,滚动条向下滚动的距离也就是元素顶部被遮住部分的高度。在没有滚动条时 scrollTop==0 恒成立。单位px,可读可设置。
MDN解释:一个元素的 scrollTop 值是这个元素的内容顶部(被卷起来的)到它的视口可见内容(的顶部)的距离的度量。当一个元素的内容没有产生垂直方向的滚动条,那它的 scrollTop 值为0,具体可参考下图:
实现算法:卷起的高度(scrollTop) = 总的内容高度(scrollHeight) - 聊天区域盒子大小 (offsetHeight);
代码实现如下:
- <template>
- <view class="main" ref="scrollContainer" id="main">
- <!-- scroll-y:允许纵向滚动 默认: 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 滚动原理与实现
- //由于插入新的消息属于创建新的元素的过程,这个过程是属于异步的,所以为了防止异步创建元素导致获取高度不准确,我们可以等待一段时间,等元素创建完毕之后再获取元素高度
- 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
-
- // 会话内容
- // 获取对话结果
- const sendMsg = function () {
- setContent( 'dfasdfsfsafdsafsafsdfsafsdfsdfdsfsafdsfsadfsafggfdhfhfjgfjhsdgdsfgasfsafdsafsagdhgfhfdhsgdsgdsgdgafsadfdsfdsfsadfhghsdfgsafdsaf')
- }
- // 设置对话内容
- 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>
效果调试:
(1)打开浏览器,按下F12进入调试模式;
(2)在console窗口,多次调用document.getElementById('sendMsg').click(),使得对话内容超出界面高度,可观察到自动滚动效果;
(3)在console窗口,调用document.getElementById('pageUp').click(),可观察到向上滚动;接着调用document.getElementById('pageDown').click(),可观察到向下滚动。
效果图如下:
方案一由于接口支持,滑动效果更平滑,但是翻页只能调到指定锚点,滑动步长不可控,大部分场景不能满足需求。
方案二可以自行调整翻页的步长,按需滑动至指定高度,不过滑动动画需要自行实现,看起来卡顿感较强。
总体来说,建议使用方案二。
参考链接:
https://blog.csdn.net/weixin_46511008/article/details/126629361
https://www.cnblogs.com/wq805/p/16399600.html
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。