1. 程式人生 > >炫酷的運動柱狀圖統計動效

炫酷的運動柱狀圖統計動效

要用到柱狀圖,第一時間想到去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值隱藏滾動條

以上屬性設定了是否滾動及滾動條的樣式,我們還需要知道形成滾動的條件。 光有滾動還不行,我們還需要監聽滾動到哪裡了獲取到資訊。用到了target.addEventListener(type, listener[, options])

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)
  }
複製程式碼

小結

使用scroll-x:scroll實現滾動效果,通過this.$refs.scroll.removeEventListener('scroll',this.scrollListenHandle)來監聽滾動的距離,通過計算得出高亮的柱子是哪條。通過點選事件改變this.$refs.scroll.scrollLeft的來實現動畫的效果。