微信小程序——绘制折线图

本文详细介绍了一种在微信小程序中使用canvas元素绘制折线图的方法。通过具体代码实例,展示了如何设置wxml、wxss、json和js文件,实现自定义折线图的绘制,包括坐标轴、刻度线、虚线以及数据点的连接线。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、wxml

<view class="canvas-view">
    <canvas class="canvas" canvas-id="canvasId"></canvas>
</view>

二、wxss

.canvas-view{
  height: 100%;
  display: flex;
  align-items: center;
  margin-top: 48rpx;
}
.canvas{
  width: 100%;
  height: 640rpx;
}

三、json

{
  "component": true,
  "usingComponents": {}
}

四、js

const app = getApp()

Page({
  data: {
    list: [50, 150, 170, 200, 100, 267, 20, 50, 130, 124],
    h32: 32,
    h64: 64,
    h360: 360,
    h420: 420,
    s28: 28,
    s18: 18,
    //Y轴分成的大分段
    heightLineNum: 7,
    //X轴分成的大分段
    widthLineNum: 10,
    //Y轴一个分段的值
    yOneDuan: 50
  },

  onLoad: function (options) {
    //折线图
    this.initChart()
  },

  // 初始化折线图
  initChart: function () {
    const ctx = wx.createCanvasContext('canvasId')

    ctx.beginPath()
    ctx.setStrokeStyle('#000000')
    ctx.setFillStyle('#FFFFFF')
    ctx.setLineWidth(1)

    //坐标原点,Y轴坐标值从上往下是增加
    const leftBottomX = this.getEleWidth(this.data.h64)
    const leftBottomY = this.getEleWidth(this.data.h360)
    //Y坐标
    const leftTopX = this.getEleWidth(this.data.h64)
    const leftTopY = this.getEleWidth(this.data.h32)
    //X坐标
    const rightBottomX = this.getEleWidth(this.data.h420)
    const rightBottomY = this.getEleWidth(this.data.h360)

    const yHeight = this.getEleWidth(this.data.h360 - this.data.h32)
    const xWidth = this.getEleWidth(this.data.h420 - this.data.h64)

    //从Y轴坐标开始画坐标系
    //Y轴坐标到原点坐标画出Y轴线
    //画完Y轴线,再从原点坐标到X轴坐标画出X轴线
    ctx.moveTo(leftTopX, leftTopY)
    ctx.lineTo(leftBottomX, leftBottomY)
    ctx.lineTo(rightBottomX, rightBottomY)
    //设置字体大小
    ctx.setFontSize(this.getEleWidth(this.data.s28))
    //设置字的位置
    ctx.fillText("光伏总电量及总用电量曲线", this.getEleWidth(63), this.getEleWidth(32))

    //划分Y轴
    this.drawYScale(ctx);
    //划分X轴
    this.drawXScale(ctx);

    //画折线
    this.drawCharts(ctx);
    ctx.stroke()
    ctx.draw(true)
  },

  //划分Y轴
  drawYScale: function (ctx) {
    var that = this;

    //Y轴坐标刻度横坐标起点
    var scaleStartX = this.getEleWidth(this.data.h64)
    //长的刻度
    var scaleEndX = this.getEleWidth(this.data.h64 + 18)
    //短的刻度
    var littleScaleEndX = this.getEleWidth(this.data.h64 + 9)

    //Y轴刻度总高度
    const yHeight = this.getEleWidth(this.data.h360)
    //一个大分段的长度,一共分为6段
    var oneScaleX = yHeight / this.data.heightLineNum
    //大分段数字字体大小
    ctx.setFontSize(this.getEleWidth(this.data.s18))
    //大分段数字位置横坐标
    var textX = this.getEleWidth(this.data.h64 - 42)
    //大分段,长刻度:50-300
    for (var i = 1; i < this.data.heightLineNum; i++) {
      var scaleEndY = yHeight - oneScaleX * i
      //画长刻度线条
      ctx.moveTo(scaleStartX, scaleEndY)
      ctx.lineTo(scaleEndX, scaleEndY)
      ctx.fillText(this.data.yOneDuan * i, textX, scaleEndY + this.getEleWidth(10))
      var littleScaleStartY = yHeight - oneScaleX * (i - 1)
      //小分段,短刻度
      for (var j = 1; j < 5; j++) {
        var littleScaleEndY = littleScaleStartY - (oneScaleX / 5) * j
        //画短刻度线条
        ctx.moveTo(scaleStartX, littleScaleEndY)
        ctx.lineTo(littleScaleEndX, littleScaleEndY)
        ctx.stroke();
      }
    }
    //高和低虚线Y轴坐标
    const lowlimitLineY = yHeight - oneScaleX * 2
    const middlelimitLineY = yHeight - oneScaleX * 4
    const highlimitLineY = yHeight - oneScaleX * 6

    //虚线总长度
    const rightBottomX = this.getEleWidth(this.data.h420)
    const space = this.getEleWidth(10)
    //限制虚线
    that.drawDashLine(ctx, scaleStartX, lowlimitLineY, rightBottomX, lowlimitLineY, space)
    that.drawDashLine(ctx, scaleStartX, middlelimitLineY, rightBottomX, middlelimitLineY, space)
    that.drawDashLine(ctx, scaleStartX, highlimitLineY, rightBottomX, highlimitLineY, space)
  },

  //划分X轴
  drawXScale: function (ctx) {
    var that = this;
    //虚线总高度
    var scaleStartY = this.getEleWidth(that.data.h360)
    //虚线顶点Y轴高度
    var scaleEndY = this.getEleWidth(that.data.h32)
    //X轴总长度=X轴横坐标-向右偏移长度
    const xWidth = this.getEleWidth(that.data.h420 - that.data.h64)
    //X轴起始点
    const xMaginLeft = this.getEleWidth(that.data.h64)
    //一个分段的宽度
    const oneScaleX = xWidth / (that.data.widthLineNum + 1)
    const space = this.getEleWidth(10)
    for (var i = 0; i < that.data.widthLineNum + 1; i++) {
      var toEndX = xMaginLeft + oneScaleX * i;
      if (i > 0) {
        that.drawDashLine(ctx, toEndX, scaleStartY, toEndX, scaleEndY, space)
      }
      ctx.fillText(i, toEndX - this.getEleWidth(5), scaleStartY + this.getEleWidth(30))
    }
  },

  //画虚线
  drawDashLine: function (ctx, x1, y1, x2, y2, dashLength) {
    //传context对象,始点x和y坐标,终点x和y坐标,虚线长度
    ctx.beginPath()
    ctx.setLineWidth(0.5)
    var dashLen = dashLength === undefined ? 3 : dashLength,
      //得到横向的宽度;
      xpos = x2 - x1,
      //得到纵向的高度;
      ypos = y2 - y1,
      numDashes = Math.floor(Math.sqrt(xpos * xpos + ypos * ypos) / dashLen);
    //利用正切获取斜边的长度除以虚线长度,得到要分为多少段;
    for (var i = 0; i < numDashes; i++) {
      if (i % 2 === 0) {
        ctx.moveTo(x1 + (xpos / numDashes) * i, y1 + (ypos / numDashes) * i);
        //有了横向宽度和多少段,得出每一段是多长,起点 + 每段长度 * i = 要绘制的起点;
      } else {
        ctx.lineTo(x1 + (xpos / numDashes) * i, y1 + (ypos / numDashes) * i);
      }
    }
    ctx.stroke();
  },

  //画折线
  drawCharts: function (ctx) {
    ctx.beginPath()
    ctx.setStrokeStyle("#FFFFFF")
    var that = this;
    var list = that.data.list;

    const yHeight = this.getEleWidth(that.data.h360)
    const xWidth = this.getEleWidth(that.data.h420 - this.data.h64)
    //X坐标,一个空格的值
    const oneScaleX = xWidth / (that.data.widthLineNum + 1)
    //Y坐标,一个空格的值
    var oneScaleY = yHeight / this.data.heightLineNum;

    for (var i = 0; i < list.length; i++) {
      var height = list[i];
      //计算X坐标
      var x = oneScaleX * (i + 1) + this.getEleWidth(that.data.h64);
      //计算Y坐标
      var y = yHeight - oneScaleY / this.data.yOneDuan * height
      if (i == 0) {
        ctx.moveTo(x, y)
      } else {
        ctx.lineTo(x, y)
      }
    }

    ctx.stroke()
    ctx.draw(true)
  },

  //获取屏幕自适应宽度
  getEleWidth: function (w) {
    var real = 0;
    try {
      var res = wx.getSystemInfoSync().windowWidth;
      //以宽度480px设计做宽度的自适应
      var scale = (480 / 2) / (w / 2);
      real = Math.floor(res / scale);
      return real;
    } catch (e) {
      return false;
    }
  }
})
### 如何在 UniApp 微信小程序中用 ECharts 实现折线图 要在 UniApp 的微信小程序环境中实现基于 ECharts 的折线图功能,可以按照以下方法操作: #### 1. 下载并引入必要的依赖库 首先需要下载 `echarts` 和其适配版本的组件。可以通过 npm 安装 `echarts` 及其针对小程序优化的插件 `mpvue-echarts` 或者直接使用官方推荐的小程序专用版本。 安装命令如下: ```bash npm install echarts mpvue-echarts ``` 如果项目中有 `package-lock.json` 文件,则可以在其中定位到 `mpvue-echarts` 并将其本地化存储至项目的指定目录下[^4]。 对于更轻量化的场景,可以直接从 GitHub 获取专门用于微信小程序的 ECharts 版本[^1]: https://github.com/ecomfe/echarts-for-weixin 将该仓库中的必要文件复制到项目内的合适路径(如 `/components/echarts/`),并通过模块导入方式加载它[^2]。 #### 2. 配置 JSON 文件支持自定义组件 为了使微信小程序能够识别新增加的图表渲染组件,在页面对应的 `.json` 文件里声明所使用的外部组件名称及其相对位置[^3]: ```json { "usingComponents": { "ec-canvas": "/components/ec-canvas/ec-canvas" } } ``` 这里假设已把上述提到的 `ec-canvas` 组件放置于根级 components 路径之下;实际部署时需依据个人项目结构调整具体地址。 #### 3. 设置 CSS 样式确保正常展示 由于默认情况下容器大小可能不足以容纳完整的图形结构,因此必须通过样式表设定固定的宽高比例来满足绘制需求: ```css /* app.wxss */ .ec-canvas { width: 100%; height: 30%; /* 这里的高度可以根据实际情况调整 */ } .chart_one { width: 750rpx; height: 600rpx; } ``` 以上代码片段设置了两个不同层次上的尺寸参数——一个是作用在整个画布区域上的一般性规则,另一个则是特定实例专属的选择器类名`.chart_one`. #### 4. HTML 结构编写 最后一步是在 WXML 中构建起承载最终效果的实际 DOM 元素树形结构。注意要嵌套一层额外包裹层以便更好地控制布局行为: ```html <view class="chart_one"> <ec-canvas id="myChartDomLine" canvas-id="myCanvasIdForLine" ec="{{ ec }}"></ec-canvas> </view> ``` 此处的关键点在于绑定了一个名为 `ec` 的数据属性对象给 `<ec-canvas>` 成分标签作为初始化选项传递过去。 #### 5. 初始化逻辑处理 接着就是在 JavaScript 层面完成具体的业务流程编码工作了。下面给出一段简单的例子演示如何创建一条基本样式的直线型趋势曲线图: ```javascript export default { data() { return { ec: {} }; }, onLoad() { this.initEc(); }, methods: { initEc() { const chartData = [ ['Mon', 18], ['Tue', 29], ['Wed', 33], ['Thu', 31], ['Fri', 44], ['Sat', 13], ['Sun', 38] ]; let option = { xAxis: { type: 'category', boundaryGap: false, data: chartData.map(item => item[0]) }, yAxis: { type: 'value' }, series: [{ name:'销量', type:'line', smooth:true, symbolSize:8, showSymbol:false, lineStyle:{ color:'#FFA500' }, areaStyle:{}, data:chartData.map(item=>item[1]), }] }; setTimeout(() => { this.ec = { lazyLoad: true, // 延迟加载 onInit: (canvas, width, height,dpr) => { const ctx = canvas.getContext('2d'); const myChart = echarts.init(canvas, null, {width,height,dpr}); myChart.setOption(option); return myChart; } }; }, 10); } } }; ``` 此脚本实现了动态生成一组模拟销售统计数据,并利用这些数值描绘出相应的视觉表现形式。 --- ###
评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值