言
最近的一個項目做的是vue組件中的一個應用,「處理滾動列表」,這個應該是很常見的需求了,在項目中遇到的痛點,難點,如何一步步解決的,以及小細節一些優化。
借鑒某課的思路,仿QQ音樂效果,記錄一下,自己字母解決這個難題,分享給你們,「希望對你們做移動端滾動列表問題有所幫助」
GitHub倉庫
處理滾動列表最終效果
從最終效果來看,實現了三個我的難點
接下來就是一步步去實現,優化上面的效果
better-scroll 移動端滾動的解決方案
vue-lazyload 圖片懶加載
基本上實現上面的效果就是基于這兩個第三方庫
「better-scroll基本使用」
經常會遇到的問題就是初始化了,「還是不能滾動」。那么對于這個而言,我最近用到一些經驗是什么呢?
我們先看常見的html結果
<div class="wrapper">
<ul class="content">
<li>...</li>
<li>...</li>
...
</ul>
<!-- you can put some other DOMs here, it won't affect the scrolling
</div>
復制代碼
滾動的原理是什么
better-scroll原理說明
wrapper是父容器,它一定要有「固定高度」,content是內容區域,它是父元素的第一個元素,它content會隨著內容的大小撐開而撐高,只有這個高度大于wrapper父容器高度時,才會出現滾動,也就是它的原理。
那么我們怎么去初始化呢
import BScroll from '@better-scroll/core'
let wrapper = document.querySelector('.wrapper')
let scroll = new BScroll(wrapper,{})
//{}配置一些信息
復制代碼
點這里有文檔
接下來就開始把
這個scroll組件是子組件,也可以算是個base組件,完成日常滾動的效果
<template>
<div ref="wrapper">
<slot></slot>
</div>
</template>
<script type="text/ecmascript-6">
import BScroll from 'better-scroll'
export default {
props: {
probeType: {
type: Number,
default: 1
},
click: {
type: Boolean,
default: true
},
listenScroll: {
type: Boolean,
default: false
},
data: {
type: Array,
default: null
},
pullup: {
type: Boolean,
default: false
},
beforeScroll: {
type: Boolean,
default: false
},
refreshDelay: {
type: Number,
default: 20
}
},
mounted() {
setTimeout(() => {
this._initScroll()
}, 20)
},
methods: {
// 初始化Scroll
_initScroll() {
// 判斷是否初始化
if (!this.$refs.wrapper) {
return
}
// 調用Scroll實例,表現可以滑動
this.scroll = new BScroll(this.$refs.wrapper, {
probeType: this.probeType,
click: this.click
})
if (this.listenScroll) {
let me = this
this.scroll.on('scroll', (pos) => {
me.$emit('scroll', pos)
})
}
if (this.pullup) {
this.scroll.on('scrollEnd', () => {
if (this.scroll.y <= (this.scroll.maxScrollY + 50)) {
this.$emit('scrollToEnd')
}
})
}
if (this.beforeScroll) {
this.scroll.on('beforeScrollStart', () => {
this.$emit('beforeScroll')
})
}
},
disable() {
this.scroll && this.scroll.disable()
},
enable() {
this.scroll && this.scroll.enable()
},
refresh() { // 刷新scroll,重新計算高度
this.scroll && this.scroll.refresh()
},
scrollTo() {
this.scroll && this.scroll.scrollTo.apply(this.scroll, arguments)
},
scrollToElement() {
this.scroll && this.scroll.scrollToElement.apply(this.scroll, arguments)
}
},
watch: {
// 監聽到數據的變化,就會重新去refresh數據,重新去計算響應的數據
data() {
setTimeout(() => {
this.refresh()
}, this.refreshDelay)
}
}
}
</script>
<style scoped lang="stylus" rel="stylesheet/stylus">
</style>
復制代碼
在父組件中導入即可
listview組件導入
<template>
<scroll
:listen-scroll="listenScroll"
:probe-type="probeType"
:data="data"
class="listview"
ref="listview"
>
<ul>
<li v-for="(group,index) in data" class="list-group" ref="listGroup" :key="index">
<h2 class="list-group-title">{{group.title}}</h2>
<uL>
<li
@click="selectItem(item)"
v-for="(item, index) in group.items"
class="list-group-item"
:key="index"
>
<img class="avatar" v-lazy="item.avatar" />
<span class="name">{{item.name}}</span>
</li>
</uL>
</li>
</ul>
</scroll>
</template>
復制代碼
然后導入scroll組件即可,看看效果
處理滾動列表-實現列表滾動
上面在listview組件中導入scroll組件,完成基本的列表滾動效果,接下來,完善一步一步效果吧。
<div
class="list-shortcut"
@touchstart="onShortcutTouchStart"
@touchmove.stop.prevent="onShortcutTouchMove"
>
<!-- data-index方便獲取一個列表中的index -->
<ul>
<li
v-for="(item, index) in shortcutList"
:data-index="index"
class="item"
:class="{'current':currentIndex===index}"
:key="index"
>{{item}}</li>
</ul>
</div>
復制代碼
點擊右側快速路口的話,會跳轉到相應的title去,使用的方法就是
scrollElement
scrollToElement(el, time, offsetX, offsetY, easing)
「這個方法很方便的解決了我們第一個難點」,現在就差獲取右側快速路口的索引值了
給每一個li增加一個data-index屬性名稱,值為index下
:data-index="index"
復制代碼
這樣子每次就可以獲取當前的索引值
有了索引值,我們就可以直接調用srcollToElement()方法,完成左側的跳轉效果。
this.$refs.listview.scrollToElement(this.$refs.listGroup[index], 0);
// 這個index就是獲取到下標索引值,然后通過這個
// 這個第二個參數是滾動的動畫的時間,我們默認為0就行,文檔上面也有專門的說明,可以去看看。
復制代碼
我們看看效果吧下
處理滾動列表-實現點擊右側跳轉相應位置
接下來完成「touchMove事件」,我們綁定到div上
@touchmove.stop.prevent="onShortcutTouchMove"
// 兩個修飾符阻止冒泡以及默認的事件
復制代碼
思路
看代碼
onShortcutTouchStart(e) {
// 獲取到右側的列表索引值
let anchorIndex = getData(e.target, "index");
let firstTouch = e.touches[0];
this.touch.y1 = firstTouch.pageY; // 計入一開始y軸上的位置
this.touch.anchorIndex = anchorIndex; // 保存了每次點擊的錨點
this._scrollTo(anchorIndex);
},
// 監聽的是TouchMove事件
onShortcutTouchMove(e) {
let firstTouch = e.touches[0];
this.touch.y2 = firstTouch.pageY;
// 滾動的兩個差值 也就是y軸上的偏移
// 除以每個高度,這樣子的話,就知道偏移了幾個錨點
let delta = ((this.touch.y2 - this.touch.y1) / ANCHOR_HEIGHT) | 0;
let anchorIndex = parseInt(this.touch.anchorIndex) + delta;
this._scrollTo(anchorIndex);
},
復制代碼
效果怎么樣呢,基本上點擊和手勢移動都較為完美的實現了。
處理滾動列表-實現手勢移動右側跳轉相應位置
左右聯動的效果指的是左側點擊到某個區域,緊接著右側快速路口也跳轉到相應位置,這里其實指的就是高亮效果。
效果就是滑動列表,右側的字母會相應的高亮,達到同步的作用,難點是什么呢?
ListGroup計算高度
從圖片上面看,我們發現每個listGroup分組里面的成員是不固定的,所以我們怎么去獲取到相應的currentIndex呢?
「我們可以獲取到每次滾動的距離,那怎么樣去獲取相應的currentIndex呢,比如滑到K分組時,currentIndex是對應的下標?」
有個不錯的思路:
那么我們按照上面的思路來完善吧
_calculateHeight() {
// 這個方法就是計算每個listGroup高度
this.listHeight = [];
const list = this.$refs.listGroup;
let height = 0;
this.listHeight.push(height);
for (let i = 0; i < list.length; i++) {
let item = list[i];
height += item.clientHeight;
this.listHeight.push(height);
}
},
復制代碼
這個listHeigth數據就是我們維護的第i個分組的clientHeight距離
第二步,我們監控這個scrollY,這個變量表示的就是滾動的距離
watch: {
// 每次去watch這個滾動的距離,
scrollY(newY) {
const listHeight = this.listHeight;
// 當滾動到頂部,newY>0
if (newY > 0) {
this.currentIndex = 0;
return;
}
// 在中間部分滾動
for (let i = 0; i < listHeight.length - 1; i++) {
let height1 = listHeight[i];
let height2 = listHeight[i + 1];
if (-newY >= height1 && -newY < height2) {
this.currentIndex = i;
this.diff = height2 + newY;
return;
}
}
// 當滾動到底部,且-newY大于最后一個元素的上限
this.currentIndex = listHeight.length - 2;
},
復制代碼
這里需要提醒的就是,我們怎么去拿到這個scrollY滾動距離呢?
說到這個,我們得看到scroll組件中,閱讀它的API,會發現它提供了on方法,該方法可以去監聽該「實例的鉤子函數」,所以我們去「監聽鉤子函數scroll」
「scroll鉤子函數」
所以我們可以通過這個鉤子來獲取滾動的實時坐標
if (this.listenScroll) {
let me = this
this.scroll.on('scroll', (pos) => {
// me指的就是實例
// 通過監聽scroll事件,有一個回調,pos是一個對象,有x,y軸的具體距離
// 去派發一個scroll事件,這樣子外部也就是父組件可以拿到我們的pos
me.$emit('scroll', pos)
})
}
復制代碼
這樣子我們在子組件scroll中向外派發一個scroll事件,并且把「pos = {Object} {x, y} 滾動的實時坐標」向外傳遞,這樣子的話,父組件通過@scroll="scroll" 就可以拿到這個坐標pos
這樣子我們這個難點就解決了。
我們來看看效果
這樣子基本上問題就解決了,但是呢還會遇到一個問題?
這個是文檔上面的內容,我們可以看到這個配置項還是很重要的,我們listview組件需要通過props向子組件傳遞probeType值,值為3,這樣子就可以「在滾動中實時派發 scroll 事件」。
<scroll :probe-type='3'></scroll>
// 當然了,這個probeTyep會在data中拿到
復制代碼
咱給小編:
1. 點贊+評論
2. 點頭像關注,轉發給有需要的朋友。
您的支持是小編不斷輸出的動力,謝謝!!
頁中添加滾動字幕效果
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>滾動字體的設置</title>
</head>
<body>
<canvas id="canvas1" width="600" height="600" style="border:1px solid #000000"></canvas>
<script type="text/javascript">
var canvas1 = document.querySelector("#canvas1") // 1.找到畫布對象
var ctx = canvas1.getContext("2d") // 2.上下文對象(畫筆)
ctx.shadowBlur = 10; // 陰影距離
ctx.shadowColor = "red" // 陰影顏色
ctx.shadowOffsetX = 30 // 陰影偏移
ctx.shadowOffsetY = 30 // 陰影偏移
ctx.font = "150px 楷體"
ctx.fillText("你好!", 20,150)
ctx.fillText("你好!", 20,350)
ctx.strokeText('你好!',23, 153)
ctx.strokeText('你好',23, 553)
canvas繪制文字
var x = 600
setInterval(function(){
if(x > -350){
//清空畫布
ctx.clearRect(0,0,600,600)
ctx.strokeText('你好!',x, 153)
ctx.fillText("你好!", x,350)
ctx.font = "50px 宋體"
ctx.strokeText('每天學習一點點',x, 553)
x -= 3
}else{x=590}
}, 16)
</script>
</body>
</html>
頁中實現像表格文檔那樣固定table的表頭和第一列內容,類似于excel表格那樣!下面說說實現方法
效果如下:
在數據眾多的列表下,規定的區域內上下左右都可以滾動查看,然而表頭和側邊表頭都還在,方便用戶查看數據,增強用戶體驗!
實現代碼
html結構:
css代碼:
javascript代碼:
*請認真填寫需求信息,我們會在24小時內與您取得聯系。