炫酷的運動柱狀圖統計動效
阿新 • • 發佈:2018-12-23
要用到柱狀圖,第一時間想到去echart看看示例扒一套程式碼下來。看了官方教程後感覺不太適合本專案,考慮後覺得自己動手寫個圖示比較省事而且不用引包。那就開始吧!
柱狀圖
滾動圖表首先得有一些柱子呀,那我們就來早柱子,這些都是個寬度相等,高度按比例*設定好的最高高度
的柱子。通過flex佈局就可以實現這些。
<div class="move__wrap">
<div class="move__item"
v-for="member in list"
:key="member.id"></div>
</div>
// style
<style lang='scss' scoped>
.move {
&__wrap {
display: flex;
align-items: flex-end;
height: 100%;
}
}
</style>
複製程式碼
給父級設定flex,設定交叉軸的對其方式為flex-end就可以把柱子都立起來了。但是可以看到日期和柱子是同時滾動的,它們應該同級的標籤。
<div class="move__wrap">
<div class="move__item"
v-for="member in list"
:key="member.id">
// 柱子
<div class="child__chart"></div>
// 日期
<div></div>
</div>
</div>
// style
<style lang='scss' scoped>
.move {
&__wrap {
display: flex;
align-items: flex-end;
height: 100%;
}
&__item{
display: flex;
flex-wrap: wrap;
width: 38px;
}
}
</style>
複製程式碼
設定兩個彈性盒子之後柱狀圖和日期都已經出來了。需要注意的一點是每個柱狀圖之間是有間隙的,但是日期是沒有間隙的。
我們不能直接給move__item設定左外邊距,將 child__chart設定寬度為37並且設定margin-left:1就有了如下圖表
這裡還需要定一個圖示的最高的高度。比如想定最高的高度為180,我這裡的做法是找出list中最大的一個值,其他項以它為標準做百分百比運算。
// @param {Array} list 陣列項為數字即步數
<div class="move__item"
v-for="member in list"
:key="member.id">
<div class="child__chart"
:style="{height:member/maxStep*180+'px'}"></div>
</div>
// js
computed:{
// 找出最大值參考
maxStep() {
let max = 0
this.seriesDataList.forEach(number => {
max = number > max ? number : max
})
return max
},
}
複製程式碼
動起來
到此柱狀圖我們已經完成了,現在的目標就是讓這個圖表滑動起來。 這裡我們需要了解一些css屬性。
overflow-x : 當一個塊級元素的內容在水平方向發生溢位時,應該截斷溢位內容,或者顯示滾動條,或者直接顯示溢位內容。 visible | hidden | clip | scroll | auto 。
overflow-y : 同上。
以上屬性設定了是否滾動及滾動條的樣式,我們還需要知道形成滾動的條件。-webkit-scrollbar : CSS偽類選擇器影響了一個元素的滾動條的樣式 可以設定css屬性,這裡使用none值隱藏滾動條
- 有父子兩個元素
- 子元素的高/寬 > 父元素的高/寬
你也可以點選這裡檢視更多
EventTarget.addEventListener() 方法 : 將指定的監聽器註冊到 EventTarget 上,當該物件觸發指定的事件時,指定的回撥函式就會被執行。
- type 表示監聽事件型別的字串這裡我們用到的是scroll
- listener 需要執行的函式,考慮到之後要根據條件去removeEventListener移除事件監聽,我們可以在data函式中定義一個有名的listener
- options 表示是在冒泡階段或者捕獲階段會觸發listener,預設為false即在冒泡階段觸發。
- eventTarget 事件目標可以是一個文件上的元素我們在
mounted鉤子中
通過ref獲取到我們目標值上,需要注意的滾動監聽事件要繫結在滾動元素父級的DOM
data(){
return {
scrollListenHandle: () => {
console.log(this.$refs.scroll.scrollLeft)
this.scrollLength = this.$refs.scroll.scrollLeft
}
}
},
mounted() {
this.$refs.scroll.addEventListener('scroll', this.scrollListenHandle)
}
複製程式碼
現在我們已經設定好x軸方向的滾動效果和了滾動事件來看看效果吧
高亮
我們已經將圖表滾起來了,我們需要將當前選中的日期高亮起來。這裡涉及到一個參考,預設讓圖示可視區域的中心點的柱子高亮。 怎麼拿到這個中心點呢?假設我們一頁可以顯示九根柱子,每根柱子的寬度是38px,當我們向右移動38px時,則第六根柱子會亮起來。計算公式可以是`(移動的距離+半個螢幕寬度)/38`就是當前移動時需要高亮的柱子的序號data(){
// 滾動時不斷更新scrollLength滾動距離
scrollListenHandle: () => {
this.scrollLength = this.$refs.scroll.scrollLeft
},
},
mounted() {
// 獲取當前螢幕的寬度
this.screenWidth = screen.width
},
computed:{
// 獲取到當前高亮的目標值的序號
selectIndex() {
return parseInt(
(this.scrollLength + this.screenWidth / 2) / singleWidth
)
},
}
複製程式碼
通過滾動事件將滾動的距離更新,通過計算屬性得到當前選中柱子的序號,有了這個序號我們可以通過vue的動態繫結class設定高亮的樣式。html程式碼如下:
<div ref="scroll"
class="chart__move"
@touchmove="onTouchMove">
<div ref="child"
class="move__child"
:style="{width:AllLength + 'px'}">
<div v-for="(item,index) in chartList"
:key="index"
class="child__wrap">
<div class="child__chart"
:class="{'child__chart--white':selectIndex === index}"
:style="{height:item/maxStep*180+'px'}"></div>
<div class="child__date"
:class="{'child__date--blue':selectIndex === index}">{{dateList[index]}}</div>
</div>
</div>
</div>
複製程式碼
按照我們的做法高亮的柱子
範圍限定在 this.screenWidth / 2 ~ 總長度 - this.screenWidth / 2
在除了這個返回之外的柱子我們只能通過點選讓它高亮,
畢竟已經到頭了或者到尾了沒辦法移動了
。這裡又涉及到一個問題,當我們點選柱子時,如果還在
範圍限定在 this.screenWidth / 2 ~ 總長度 - this.screenWidth / 2
中的柱子,當我們點選它時,應該將它移動到中間的位置上來。
這裡我們需要通過this.$refs.scroll.scrollLeft賦值來達到效果
。
頁面一共九根柱子時,點選第6根柱子時,將序號傳給 onSelectDateClick事件,我們要做的兩件事,讓第6根柱子亮起來。這很簡單,使用:class動態繫結即可。還需要做的是將滾動元素的scrollLeft值增加38px(一個柱子的寬度)。這樣做效果是達到了,但是我們需要點選的時候需要有一個過渡的效果,也就是慢慢移過去。可以設定一個過渡時間。好的下面就開始幹吧!
// 定義數字的含義避免魔法數 而且方便統一修改
const singleWidth = 38 // 單個柱子的寬度
const duration = 100 // 過渡時間
export default {
methods:{
onSelectDateClick(index) {
// 獲取到高亮的index
this.clickIndex = index
// 目標的scroolLeft
const targetLeft = index * singleWidth - this.screenWidth / 2
// 當前的scrollLeft
this.currentLeft = this.$refs.scroll.scrollLeft
// 每秒的速度
this.speed = (targetLeft - this.currentLeft) / duration
// 記下開始的時間
this.startTime = new Date()
// 開始過渡函式
this.update()
},
update() {
// 定時更新 this.$refs.scroll.scrollLeft
this.rAF = setInterval(() => {
const time = new Date() - this.startTime
this.$refs.scroll.scrollLeft = time * this.speed + this.currentLeft
// 如果時間超過我們預定的過渡時間就停止更新
if (time > duration) {
clearInterval(this.rAF)
}
})
},
}
}
複製程式碼
為了避免干擾html 動態繫結class設定為
:class="{'child__chart--white':clickIndex===index}"
複製程式碼
來看下效果
看起來不錯,我們繼續把selectIndex加上,現在類名高亮受兩個index決定 :class="{'child__chart--white':selectIndex === index||clickIndex===index}"
複製程式碼
當我通過點選改變 scrollLeft的值時會也會觸發滾動事件,當selectIndex和clickIndex不同時會出現兩個高亮的柱子。我們需要一個值來區分現在是現在是滾動還是點選事件。
// 給滾動區域的父級加一個touchMove事件
onTouchMove() {
this.isCenter = true
this.clickIndex = -1
}
複製程式碼
通過touchMove事件通過isCenter來判斷是否在滾動,如果在滾動將clickIndex置為-1,這樣滾動的時候clickIndex為-1
// 判斷isCenter是否在滾動否則返回-1
selectIndex() {
if (this.isCenter) {
return parseInt(
(this.scrollLength + this.screenWidth / 2) / singleWidth
)
} else {
return -1
}
},
複製程式碼
經過上面的判斷處理,clickIndex和scrollIndex就不會重複,不會出現兩條高亮線的問題。
移除滾動事件
當資料列表寬度小於一個螢幕時或解除安裝頁面的時候,我們需要移除監聽事件,優化效能。
watch:{
'chartList.length'(newLength) {
if(newLength*singleWidth < this.screenWidth){
this.$refs.scroll.removeEventListener('scroll', this.scrollListenHandle)
}
}
},
destroyed() {
this.$refs.scroll.removeEventListener('scroll', this.scrollListenHandle)
}
複製程式碼