自定义vue组件chart-view

423 阅读1分钟

前言: 我正式接触web前端时间很短,前前后后差不多3个月左右的时间。我是14年从事android开发的,在android里也经常会玩一下自定义控件。针对于之前移动端的折线图,我在利用了空闲时间,用vue也写了一个组件chart-view

一、先看看chartview的效果图

chartview.gif

二、来看看chartview.vue

代码不是很长,在代码里注释的非常详细,如有不懂,欢迎留言。

<template>
  <div >
    <canvas ref="canvas" width="500" height="500" ></canvas>
  </div>
</template>

<script>
export default {
  //部件
  components: {},
  //静态
  //这里相当于自定义了3个属性
  //lineCenterX: X轴在画布上Y方向的坐标点(这样设计的功能是因为公司业务,看明白了可以把他改掉)
  //textFont: 文字大小
  //value_50: 50μv在画布上的长度
  props: ['lineCenterX', 'textFont','value_50'],
  //对象内部的属性监听,也叫深度监听
  watch: {
    
  },
  //属性的结果会被缓存,除非依赖的响应式属性变化才会重新计算。主要当作属性来使用;
  computed: {},
  //方法表示一个具体的操作,主要书写业务逻辑;
  methods: {
    initDataSon(valueList) {

      //将画布设置满屏
      this.$refs.canvas.width = window.innerWidth
      this.$refs.canvas.height = window.innerHeight
      var ctx = this.$refs.canvas.getContext('2d')

      //当前控件的总长宽
      var total_W = this.$refs.canvas.offsetWidth // 返回元素的总宽度
      var total_H = this.$refs.canvas.offsetHeight // 返回元素的总高度

      ctx.font = 'normal ' + this.textFont + 'px Verdana'
      ctx.fillStyle = '#000000'

      //在画布上确定好+-50μv坐标,并渲染到画布上
      ctx.fillText('-50μv', 14, this.lineCenterX + this.value_50)
      ctx.fillText('+50μv', 14, this.lineCenterX - this.value_50 + this.textFont)


      //横坐标 (先用红线画出X轴)
      ctx.moveTo(0, this.lineCenterX)
      ctx.lineTo(total_W, this.lineCenterX)
      ctx.strokeStyle = '#ff0000'
      ctx.lineWidth = 0.3
      ctx.stroke()

      //纵坐标
      // ctx.moveTo(0, 0)
      // ctx.lineTo(0, total_H)

      //画笔改成黑色,准备画折线图
      ctx.beginPath()
      ctx.strokeStyle = '#000000'
      ctx.lineWidth = 0.6

      
      //填充数据是不断需要更新数据且需要渲染界面,所以这里用到了nextTick方法
      this.$nextTick(() => {

        //每段x轴每个刻度值之间的距离(其实这里的ever_x,是根据你当前坐标轴要展示多长时间的数据算的,比如总共波长是200s,你当前
        //只想展示10s,所以真的项目肯定是确定的,最好是把valueList.length也当成一个属性去传递,是个固定值)
        var ever_x = total_W / (valueList.length - 1)
        for (const key in valueList) {
          
          //将数据放大1000倍(这里完全是根据我们项目走的,可以忽略这点)
          let itemValue = valueList[key] * 1000
          //知道了每个x轴坐标点,那么再计算出每个y轴的坐标点
          let trueItemValue = itemValue/50*this.value_50
          if (key === 0) {
            //单独处理下x=0的点
            ctx.moveTo(0, this.lineCenterX - trueItemValue)
          } else {
            //连接之后的所有点
            ctx.lineTo(ever_x * key, this.lineCenterX - trueItemValue)
          }
        }

        ctx.stroke()

      })


    },
  },
  //请求数据
  created() {},
  mounted() {},


  
}

</script>

<style scoped>

</style>


三、页面里的使用

<template>
  <div>
    <button class="buttonStyle" @click="test">{{ buttonText }}</button>
    <div class="fatherCss" ref="fatherCss">
      <canvas-item
        :lineCenterX="50"
        :value_50="50"
        :textFont="14"
        ref="mychild"
      ></canvas-item>
    </div>
  </div>
</template>


<script>
import ChartView from '@/components/ChartView.vue'
import axios from 'axios'

export default {
  data() {
    return {
      //开始时间
      start: 0,
      //满屏展示的波,所占的时间
      showTotalTime: 20,
      //整个数据源的波长时间
      dataTotalTime: 210,
      //波的总数据源list
      list: [],
      //以下用于处理按钮点击
      isScroll: false,
      buttonText: '点击开始',
      timer: null,
    }
  },

  //部件
  components: {
    'canvas-item': ChartView,
  },
  //静态
  props: {},
  //对象内部的属性监听,也叫深度监听
  watch: {},
  //属性的结果会被缓存,除非依赖的响应式属性变化才会重新计算。主要当作属性来使用;
  computed: {},
  //方法表示一个具体的操作,主要书写业务逻辑;
  methods: {
    initData() {
      //如果开始时间不得大于总时间
      if (this.start >= this.dataTotalTime) {
        return
      }

      //如果当前展示波的末尾时间大于总时间,那么末尾时间就等于总时间
      var lastTime = this.start + this.showTotalTime
      if (lastTime >= this.dataTotalTime) {
        lastTime = this.dataTotalTime
      }

      var dataList = []

      //用开始时间和总时间算出数据源起始index,取整
      let start = Math.floor(
        (this.list.length * this.start) / this.dataTotalTime
      )

      //同理算出末尾index
      let end = Math.floor((this.list.length * lastTime) / this.dataTotalTime)

      //从总数据获取当前所展示的数据
      dataList = Object.assign([], dataList, this.list.slice(start, end))
      this.$refs.mychild.initDataSon(dataList)

      //起始时间每次+20ms,因为下面开启了间隔20ms运行一次,模拟波的运动
      this.start += 0.02
    },
    test() {
      if (this.isScroll) {
        //代表是滚动时候
        this.isScroll = false
        this.buttonText = '点击开始'
        clearInterval(this.timer)
      } else {
        //未滚动时候
        this.isScroll = true
        this.buttonText = '点击暂停'
        this.initData()
        this.timer = setInterval(() => {
          this.initData() //自定义函数
        }, 20)
      }
    },
  },
  //请求数据
  created() {
    axios.get('/js/data.json').then(
      (response) => {
        this.list = Object.assign([], this.list, response.data.list)
        this.initData()
      },
      (response) => {
        console.log('error')
      }
    )
  },
  mounted() {},

  beforeDestroy(){
    //生命周期销毁时,清楚定时器timer
    clearInterval(this.timer)
    this.timer = null
  }
}
</script>

<style scoped>
.buttonStyle {
  display: block;
}

.fatherCss {
  position: absolute;
}
</style>




四、github地址

chart-view