实现非完整渲染的两种思路
1 .懒渲染:常见的无限滚动,每次只渲染一部分,剩余部分滚动到可见区域,就可以在渲染另一部分
2 .可视区域渲染饭:只渲染可见部分,不可见部分不渲染
一般的实践
1 .初始的数据是完整的,不会动态增加,不会出现从一条没有到不断增多的情况
2 .升级的版本就是每一项高度不确定的情况
实际的项目
1 .数据会从0开始增加,而且还会不端的增加,每增加一次数据,都需要滚动到最新的内容
2 .那其实就是会出现两个控制数据展示的情况,一个是鼠标滚动,切换显示的数据
3 .一个就是新增数据的时候,需要滚动到最新。滚动其实分为两部分
1 .数据的滚动:transform实现
2 .滚动条的滚动:
界面滚动到某部分的实现
1 .锚点快速定位
1 .本来的思路就是主页面滚动的部分也让滚动条使用scrollBy滚动响应的位置即可。
2 .但是算的太麻烦了,还不如在元素的最下面加一个看不见的锚点,每次都直接让他滚到最下方锚点的位置
3 .$('html, body').animate({scrollTop: $('#box').offset().top}, 1000)
4 .实测锚点不行,还是需要scrollBy这个方法来操作
2 .js获取并修改scrollTop:
1 .返回顶部
2 .document.documentElement.scrollTop=0
3 .scrollBy():每次以固定的步长进行移动
4 .scrollTo():直接移动到固定的位置
总结
1 .scrollLeft/scrollTop这两个属性只能作为元素上,在window对象上没有效果。而pageXOffset/pageYOffset只能作用于window对象上,在元素上没有效果。而scrollTo和scrollBy不仅可以作用于window对象上,还可以作用于元素上。实现的调用的统一。
2 .scrollLeft/scrollTop和pageXOffset/pageYOffset控制滚动定位,想要定位平滑,只能借助于CSS scroll-behavior属性,JS这块设置无力。但是scrollTo和scrollBy在比较方便,直接有API参数支持。
3 .兼容性
if (!window.scrollTo) {
window.scrollTo = function (x, y) {
window.pageXOffset = x;
window.pageYOffset = y;
};
}
if (!window.scrollBy) {
window.scrollBy = function (x, y) {
window.pageXOffset += x;
window.pageYOffset += y;
};
}
if (!document.body.scrollTo) {
Element.prototype.scrollTo = function (x, y) {
this.scrollLeft = x;
this.scrollTop = y;
};
}
if (!document.body.scrollBy) {
Element.prototype.scrollBy = function (x, y) {
this.scrollLeft += x;
this.scrollTop += y;
};
}
实现原理
1 .div结构
<div ref="scroll_wp" @scroll="handleScroll" class="List_wp">
//整个父级div:用来显示里面的所有列表
//监听scroll事件,通过scroll事件来让下面的页面滚动
//他的height,width就是展示界面的大小
<div class="List_scroll" :style="computedScroll" ref="list_scroll"></div>
//注意computedScroll,所以这个用来撑起来滚动条,这里决定是否可以滚到对应的部分,所以这里的值一定要是所有元素的height的和,每有新的元素,高度都要加到这个里面
<div class="List_view" ref="list_view">
<LineLi v-for="(c,index) in visiableData"
:msg="c.msg"
v-bind:from="c.from"
:index="index"
:key="c.index"
/>
</div>
//这是具体的每一条的数据
</div>
mounted事件
1 .当dom结构发生复用的时候,并不能检测到传入props的变化,需要使用别的生命周期函数
2 .问题
1 .想要的是每新加一个数据,都要新算他的高度,凑出总的高度,但是mounted事件会导致后面的不会触发这个事件,应为他是复用的,旧的数据是不触发事件的。
2 .update()事件:这个是会导致其实每一个都是走的复用逻辑,前一个数据使用的后一个数据的dom结构,会导致每增加一个元素,计算一遍之前的高度。
3 .加了key之后发现虽然不会总算了,但是发现每次滑动也还会触发muunted事件
4 .我发现自己实现组件的方法还是有问题
5 .
最大不同
1 .别人的例子都是,初始给出的数据是固定的,不会增加的
2 .在这个场景里面,由于是websocket应用,会不停的增加数据。所以初始的数据是无时无刻都在变的。而且还要求最新的数据要一
直在视野里面,就是新增数据要滚动
3 .夭折版本:实现了固定高度的虚拟列表
<template>
<div ref="scroll_wp" @scroll="handleScroll" class="List_wp">
<div class="List_scroll" :style="computedScroll" ref="list_scroll"></div>
<div class="List_view" ref="list_view">
<LineLi v-for="(c,index) in visiableData"
:msg="c.msg"
v-bind:from="c.from"
:key="c.msg"
/>
</div>
</div>
</template>
<script>
import LineLi from './line'
export default {
props:{
data:{
type:Array,
default:function(){
return []
}
},
// 需要展示的数据
lineHeight:{
type:Number,
default:35,
},
// 单行文本的高度
},
data:function(){
return {
visiableData:[],
count:null,
start:0,
scrollOffset:0,
end:null,
wrapperHeight:0,
totalHeight:0,
heights:[],
}
},
components:{
LineLi,
},
computed:{
computedScroll(){
let height=(this.data.length+2)*this.lineHeight+"px"
return {
"height":height,
}
},
},
methods:{
handleScroll(){
this.scrollOffset=this.$refs.scroll_wp.scrollTop
},
scrollTop(offset){
this.$refs.list_view.style.webkitTransform=`
translate3d(0,${offset}px,0)
`
},
},
mounted(){
// 一开始这里没值。。
this.wrapperHeight=this.$refs.scroll_wp.getBoundingClientRect().height
this.count=Math.floor(this.wrapperHeight/this.lineHeight)
// this.end=this.start+this.count
// this.visiableData=this.data.slice(this.start,this.end)
// 计算出最小高度的时候需要展示的数据的数目。这样会出现渲染数据多的情况,而不会出现展示不足的情况
},
created(){
function throttle(func,delay){
let lastTime=null
return function(){
let nowTime=new Date()
if(nowTime-lastTime>delay||!lastTime){
func();
lastTime=nowTime
}else{
return
console.log('我正在控制这个函数的触发次数')
}
}
}
// this.newScroll=throttle(this.handleScroll,300)
},
watch:{
data(n,o){
let nlen=n.length
if(nlen<=this.count){
// 这里如果是等于就会少一个
this.visiableData=n
}else{
// console.log(nlen,"change")
// this.scrollOffset+=35
// 这俩会互相影响
// 还需要一个让里面的页面滚动的部分,现在只是走了让他偏移的大小,但是实际上是没有滚动的
this.$refs.scroll_wp.scrollBy(0,35)
// console.log(this.$refs.newLine.getBoundingClientRect().top)
}
},
scrollOffset(n,o){
this.scrollTop(n)
this.start=Math.floor(n/35)
this.end=this.start+this.count
// console.log(this.start)
// console.log(this.end-this.start)
this.visiableData=this.data.slice(this.start,this.end)
}
},
}
</script>
<style lang="less" src="./list.less">
</style>
<template>
<div class="hp_line" :title="time" ref="hp_line">
<div class="hp_line_cell">
<div class="hp_line_svg">
<svg class="icon" aria-hidden="true" v-if="from==1">
<use xlink:href="#icon-yunfuwuqi-copy-copy"></use>
</svg>
<svg class="icon" aria-hidden="true" v-else-if="from==2">
<use xlink:href="#icon-youxi1-copy"></use>
</svg>
<svg class="icon" aria-hidden="true" v-else-if="from==3">
<use xlink:href="#icon-gongjuxiang-copy"></use>
</svg>
<svg class="icon" aria-hidden="true" v-else>
<use xlink:href="#icon-gongjuxiang-copy"></use>
</svg>
</div>
<div class="hp_line_msg">
{{msg}}
</div>
</div>
</div>
</template>
<script>
export default {
props:{
from:{
type:Number,
default:0
},
msg:{
type:String,
default:'空'
},
time:{
type:String,
default:'123123'
},
isError:{
type:Boolean,
default:false
},
index:{
type:Number,
default:30
}
},
data:function(){
return {
height:35
}
},
mounted(){
// this.$parent.totalHeight+=this.$refs.hp_line.getBoundingClientRect().height
// this.$parent.heights.push(this.$parent.totalHeight)
// console.log(this.$parent.totalHeight)
// console.log(this.$parent.heights)
},
beforeUpdate(){
// 计算一下自己的高度
// this.$parent.totalHeight+=this.$refs.hp_line.getBoundingClientRect().height
// this.$parent.heights.push(this.$parent.totalHeight)
// console.log(this.$parent.totalHeight)
// console.log(this.$parent.heights)
//
}
}
</script>
<style lang="less" src="./line.less">
</style>
6216618401001324664