• Canvas 折线图组件封装 Better


    背景

    前段时间开发快应用,因为快应用的生态不活跃,没有像 ECharts 这么强大的图表库,所以为了实现需求自己用原生的Canvas封装了一个。
    直接上代码!!!


    Canvas折线图组件代码

    <template>
      <div class="wrapper">
    		<canvas id="canvas"  show="{{ isHaveData }}" style="{{canvasWH.width + 'px'}}; height: {{canvasWH.height + 'px'}};"></canvas>
        <div show="{{ !isHaveData }}" class="no-data">
          <text>暂无数据</text>
        </div>
    	</div>
    </template>
    <script>
    import { getCurrentTime, hourToString, formatYAxis, checkYAxisHaveData } from '../../utils/common.js'
    
    export default {
      props: {
        padding: {
          default: { top:10, right: 10, bottom:10, left:40 }
        },
        dataZone: {
          default: {  640, height:400 }
        },
        showHourNum: {
          default: 8
        }
      },
    	data() {
    		return {
          // padding: { top:10, right: 10, bottom:10, left:40 },
          // dataZone: {  640, height:400 },
          xAxis: [7, 8, 9, 10, 11, 12, 13, 14, 15],
          yData: [36.5, 36.6, 36.4, 36.8, 36.1, 37.2, 36.8, "", "", "", "", "", "", 36.8, 36.8],
          isHaveData: false,
          canvasWH: {
             this.dataZone.width + this.padding.left + this.padding.right + 50,
            height: this.dataZone.height + this.padding.top + this.padding.bottom + 30
          }
    		}
      },
      draw(endTime) {
        const canvas = this.$element('canvas')
        const context = canvas.getContext("2d")
    
        this.clearCanvas(context) // 清空画布
        this.drawBackground(context) // 画背景
        this.drawYAxis(context) // 画 y轴
        this.drawXAxis(context) // 画 X 轴
        this.drawLine(context, endTime) // 画折线
      },
      clearCanvas(ctx) {
        ctx.clearRect(0, 0, this.canvasWH.width, this.canvasWH.height)
      },
      drawBackground(ctx) {
        // 画背景
        const MAX = 45
        const MIN = 25
        const oneDegreeHigh = this.dataZone.height / (MAX-MIN)
        ctx.beginPath();
        let total = 0
        // 45 ~ 43
        const height0 = (45 - 43) * oneDegreeHigh
        ctx.fillStyle = "#F2F5F4"
        ctx.fillRect(this.padding.left, total+this.padding.top, this.dataZone.width, height0);
        total += height0
        // 43 ~ 37
        const height1 = (43 - 37) * oneDegreeHigh
        ctx.fillStyle = "#FFAEAE"
        ctx.fillRect(this.padding.left, total + this.padding.top, this.dataZone.width, height1);
        total += height1
        // 37 ~36
        const height2 = (37 - 36) * oneDegreeHigh
        ctx.fillStyle = "#9CF5CA"
        ctx.fillRect(this.padding.left, total + this.padding.top, this.dataZone.width, height2);
        total += height2
        // 36 ~ 32
        const height3 = (36 - 32) * oneDegreeHigh
        ctx.fillStyle = "#B1E9FF"
        ctx.fillRect(this.padding.left, total + this.padding.top, this.dataZone.width, height3);
        total += height3
        // 32 ~25
        const height4 = (32 - 25) * oneDegreeHigh
        ctx.fillStyle = "#F2F5F4"
        ctx.fillRect(this.padding.left, total + this.padding.top, this.dataZone.width, height4);
      },
      drawYAxis(ctx) {
        // 画 y轴
        const MAX = 45
        const MIN = 25
        const oneDegreeHigh = this.dataZone.height / (MAX-MIN)
        const height0 = (45 - 43) * oneDegreeHigh
        const height1 = (45 - 37) * oneDegreeHigh 
        const height2 = (45 - 36) * oneDegreeHigh
        const height3 = (45 - 32) * oneDegreeHigh
        const height4 = (45 - 25) * oneDegreeHigh
        ctx.beginPath();
        ctx.font = "bold 12px Arial"
        ctx.fillStyle = "#646464"
        ctx.textAlign = "center"
        ctx.textBaseline  = "middle"
        ctx.fillText( '45', 20, this.padding.top) // 实心文字
        ctx.fillText( '43', 20, this.padding.top + height0) // 实心文字
        ctx.fillText( '37', 20, this.padding.top + height1) // 实心文字
        ctx.fillText( '36', 20, this.padding.top + height2) // 实心文字
        ctx.fillText( '32', 20, this.padding.top + height3) // 实心文字
        ctx.fillText( '25', 20, this.padding.top + height4) // 实心文字
      },
      drawXAxis(ctx) {
        const _this = this
        axisLine(ctx) // 设置线
        axisTick(ctx, 8) // 设置刻度
        axisLabel(ctx, 18) // 设置文字
        // X 轴线
        function axisLine(ctx) {
          const start = {x: _this.padding.left - 3, y: _this.padding.top + _this.dataZone.height}
          const end = {x: _this.padding.left + _this.dataZone.width, y: _this.padding.top + _this.dataZone.height}
          ctx.beginPath();
          ctx.moveTo(start.x, start.y)
          ctx.lineWidth="1";
          ctx.strokeStyle="#808080"; // 路径Style
          ctx.lineTo(end.x, end.y)
          ctx.stroke(); // 进行绘制
        }
        // X 轴标记
        function axisTick(ctx, size) {
          const dataLen = _this.xAxis.length
          const interval = _this.dataZone.width / (dataLen - 1)
          const y0 = _this.padding.top + _this.dataZone.height - size / 2
          const y1 = _this.padding.top + _this.dataZone.height + size / 2
          ctx.beginPath();
          for (let i = 0; i < dataLen; i++) {
            ctx.lineWidth="2";
            ctx.strokeStyle="#808080"; // 路径Style
            ctx.moveTo(i*interval + _this.padding.left, y0)
            ctx.lineTo(i*interval + _this.padding.left, y1)
          }
          ctx.stroke(); // 进行绘制
        }
        // X 轴文字
        function axisLabel(ctx, top) {
          const dataLen = _this.xAxis.length
          const interval = _this.dataZone.width / (dataLen - 1)
          const y = _this.padding.top + _this.dataZone.height + top
          ctx.beginPath();
          ctx.font = "bold 12px Arial"
          ctx.fillStyle = "#646464"
          ctx.textAlign = "center"
          for (let i = 0; i < dataLen; i++) {
            const x = i * interval + _this.padding.left
            ctx.fillText( _this.xAxis[i], x, y) // 标记文字
          }
        }
      },
      drawLine(ctx, endTime) {
        const _this = this
        ctx.moveTo(0 + this.padding.left,200);
        // ctx.font = "bold 10px Arial"
        // ctx.fillStyle = "#4B4B4B"
        ctx.lineWidth="2";
        ctx.strokeStyle="#4B4B4B";
        const endMinute = endTime.slice(-4, -2)
        const xArray = calcuX(endMinute)
        let isStroke = false
        this.yData.forEach((val, index)=> {
          if (val === '') {
            isStroke = false
          } else {
            const x = xArray[index]
            const y = calcuY(val)
            if (isStroke) {
              ctx.lineTo(x, y)
              ctx.stroke(); // 进行绘制
            } else {
              ctx.beginPath();
              ctx.moveTo(x, y);
              ctx.arc(x, y, 0.5, 0, 2*Math.PI) // 画点 (x, y, 半径, 起始角度,结束角度)
              ctx.fillStyle="back"
              // ctx.fill()
            }
            isStroke = true
          }
        })
    
        // 计算 1 个 y轴坐标
        function calcuY(tempre) {
          const yPointInterval = _this.dataZone.height / (45 -25)
          const y = (45 - tempre) * yPointInterval + _this.padding.top
          return parseFloat(y.toFixed(1))
        }
        // 计算 全部 x轴坐标
        function calcuX(endMinute) {
          let xArray = []
          const POINT_TIME = 3 // 3分钟一个点
          const dataLen = _this.xAxis.length
          const hourInterval = _this.dataZone.width / (dataLen-1)
          const xPointInterval = hourInterval / (60 / POINT_TIME)
          const xAxisSize = _this.padding.left + _this.dataZone.width
          const lastPointX = xAxisSize - parseInt((60-endMinute)/3) * xPointInterval
          for(let i = 0; i < _this.yData.length; i++) {
            xArray.unshift(lastPointX - i * xPointInterval)
          }
          return xArray
        }
      },
    
      createXbyEndTime(endTime) {
        let endHour = endTime.slice(-6, -4)
        let arr = []
        let hour = parseInt(endHour) + 1 // 在当前小时的基础上,多增加 1 小时
        for (let i = 0; i < 24; i++) {
          if (hour < 0) {
            arr.unshift(hourToString(hour + 24))
          } else {
            arr.unshift(hourToString(hour))
          }
          hour--
        }
        return arr
      },
      initChart(endTime, originalYArray) {
        const showDataLength = this.showHourNum * 20 // 一小时20个点
        const endMinute = endTime.slice(-4, -2)
        const offsetPoint = parseInt((60-endMinute)/3 - 1) // 差几个点补全最后一小时
        this.yData = formatYAxis(originalYArray).slice(0 - (showDataLength-offsetPoint))
        this.xAxis = this.createXbyEndTime(endTime).slice(0 - (this.showHourNum+1))
        this.isHaveData = checkYAxisHaveData(this.yData)
        this.draw(endTime)
        console.log(`加载canvas endTime=${endTime} originalYArray=${originalYArray} xAxis=${this.xAxis}`)
      },
    }
    </script>
    <style lang="scss" scoped>
    @import './../../assets/styles/style.scss';
    .wrapper {
      padding-top: 10px;
    }
    .box {
    	 740px;
    	height: 450px;
    }
    .no-data {
       100%;
      height: 500px;
      justify-content: center;
      text {
        text-align: center;
      }
    }
    </style>
    
    
  • 相关阅读:
    linux 查看 服务 命令
    Java Swing中键盘事件的处理(转)
    VI常用命令及快捷键(转)
    Linux source用法(转)
    无线桥接 WDS 中继(转)
    在远程桌面连接中使用任务管理器(转)
    linux 运行 级别(转)
    linux 当前用户 命令 w who(转)
    vecket适合和不适合的10种人(转)
    在查找预编译头使用时跳过解决(转)
  • 原文地址:https://www.cnblogs.com/huangtq/p/16077545.html
Copyright © 2020-2023  润新知