Skip to content

Instantly share code, notes, and snippets.

@ishowshao
Created March 17, 2017 06:07
Show Gist options
  • Save ishowshao/1c2a09034f99859440a0c47631c260b4 to your computer and use it in GitHub Desktop.
Save ishowshao/1c2a09034f99859440a0c47631c260b4 to your computer and use it in GitHub Desktop.
about scroll
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport"
content="width=device-width,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no">
<title>长列表优化</title>
<script src="vue.js"></script>
<style>
body {
margin: 0;
}
a {
text-decoration: none;
}
#container {
padding: 0 1em;
overflow: auto;
-webkit-overflow-scrolling: touch;
height: 400px;
}
.item {
height: 40px;
padding: 10px 0;
overflow: hidden;
border-bottom: 1px solid #f1f1f1;
}
.item img {
float: left;
margin-right: 10px;
width: 40px;
height: 40px;
}
.item h3 {
margin: 0;
font-size: 0.875rem;
line-height: 1.25rem;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
color: #333;
}
.item .info {
font-size: 0.8125rem;
color: #999;
line-height: 1.25rem;
display: flex;
}
.item .info > div {
flex: 1;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
#loading {
background-color: #cccccc;
-webkit-transition: height 0.3s;
transition: height 0.3s;
height: 0;
overflow: hidden;
}
</style>
</head>
<body>
<div id="component">
<div id="loading" :style="loadingStyle">{{loadingText}}</div>
<div id="container" @scroll="onScroll" @touchstart="onTouchStart" @touchmove="onTouchMove" @touchend="onTouchEnd">
<div class="item" v-for="item in list">
<a href="">
<img :src="'pics/' + (item + 1) + '.jpg'" width="40">
<h3>{{item}}</h3>
<div class="info">
<div>热度:9.4亿</div>
<div>来源:喜马拉雅</div>
<div>主播:未知</div>
</div>
</a>
</div>
</div>
<div v-if="scrollBottom === 0">加载中</div>
</div>
<script>
var LOADING_TEXT_INIT = '下拉推荐';
var LOADING_TEXT_READY = '松开推荐';
var LOADING_TEXT = '玩命加载中';
var LOADING_TEXT_RESULT = '成功加载10条';
var i = 0;
var list = [];
for (; i < 20; i++) {
list.push(i);
}
// 模拟ajax取下拉刷新数据
var fetchData = function (callback) {
var data = [];
for (var j = 0; j < 10; j++) {
data.push(i++);
}
setTimeout(function () {
callback(data);
}, 800);
};
var ITEM_HEIGHT = 61;
var app = new Vue({
el: '#component',
data: {
startY: 0,
distance: 0,
refreshing: false,
loading: false,
scrollTop: 0,
scrollBottom: 1,
loadingStyle: {
height: '0px',
webkitTransition: 'height 0.3s',
transition: 'height 0.3s'
},
loadingText: LOADING_TEXT_INIT,
threshold: 150, // 触发刷新的下拉距离阈值
list: list,
topDetached: [],
bottomDetached: []
},
methods: {
onScroll: function (e) {
var me = this;
var container = e.target;
this.scrollTop = container.scrollTop;
this.scrollBottom = container.scrollHeight - container.offsetHeight - container.scrollTop;
// console.log(this.scrollTop, this.scrollBottom);
// 这里以10行列表为单位进行移除
// 坑:批量移除会一次调整较大高度的scroll位置移动,由于DOM滞后于滚屏,可能导致可滚动区域不足,会导致错位
// 这个坑确实不好描述,但是多留一些留存的DOM就可避免
// 上滑
if (this.scrollTop > 30 * ITEM_HEIGHT) {
this.topDetached.push.apply(this.topDetached, this.list.splice(0, 10));
e.target.scrollTop = this.scrollTop - 10 * ITEM_HEIGHT;
}
if (this.scrollBottom < 10 * ITEM_HEIGHT && this.bottomDetached.length > 0) {
this.list.push(...this.bottomDetached.splice(this.bottomDetached.length - 10, 10));
}
// 向下滚屏
if (this.scrollBottom > 30 * ITEM_HEIGHT) {
this.bottomDetached.push.apply(this.bottomDetached, this.list.splice(this.list.length - 10, 10));
}
if (this.scrollTop < 10 * ITEM_HEIGHT && this.topDetached.length > 0) {
this.list.unshift.apply(this.list, this.topDetached.splice(this.topDetached.length - 10, 10));
e.target.scrollTop += 10 * ITEM_HEIGHT;
}
// 到底部自动加载下一页
if (this.scrollBottom === 0) {
fetchData(function (data) {
me.list.push.apply(me.list, data);
me.scrollBottom = false;
});
}
},
onTouchStart: function (e) {
if (this.loading) {
e.preventDefault();
return;
}
this.startY = e.changedTouches[0].clientY;
if (this.scrollTop === 0) {
this.loadingStyle.webkitTransition = 'none';
this.loadingStyle.transition = 'none';
this.loadingText = LOADING_TEXT_INIT;
}
},
onTouchMove: function (e) {
if (this.loading) {
e.preventDefault();
return;
}
this.distance = e.changedTouches[0].clientY - this.startY;
// 只有在最顶部才有可能开启下拉刷新
if (this.scrollTop === 0) {
// 只有下拉才触发刷新,上划不能做任何事
this.refreshing = this.distance > 0;
if (this.refreshing) {
e.preventDefault();
this.loadingStyle.height = this.distance * (0.4 - 0.4 * this.distance / 2000) + 'px';
if (this.distance > this.threshold) {
this.loadingText = LOADING_TEXT_READY;
}
}
}
},
onTouchEnd: function (e) {
var me = this;
if (this.refreshing) {
this.loadingStyle.webkitTransition = 'height 0.3s';
this.loadingStyle.transition = 'height 0.3s';
if (this.distance > this.threshold) {
e.preventDefault();
this.loadingStyle.height = '30px';
this.loadingText = LOADING_TEXT;
this.loading = true;
fetchData(function (data) {
me.loadingText = LOADING_TEXT_RESULT;
me.list.unshift.apply(me.list, data); // me.list.unshift(...data);
me.loadingStyle.height = '0px';
me.loading = false;
});
}
else {
this.loadingStyle.height = '0px';
}
this.refreshing = false;
}
this.distance = 0;
}
}
});
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment