Created
March 17, 2017 06:07
-
-
Save ishowshao/1c2a09034f99859440a0c47631c260b4 to your computer and use it in GitHub Desktop.
about scroll
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!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