js:使用canvas画一个半圆

背景

需求需要画一个半圆,或者多半圆,其实一下子就能想到 canvas 中的圆弧,核心使用 context.arc

js 复制代码
context.arc(x,y,r,sAngle,eAngle,counterclockwise)


接下来我们看看示例

例一

html 复制代码
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>
<body>

<canvas id="myCanvas" width="400" height="400"></canvas>

<script>
  const canvas = document.getElementById('myCanvas');
  const context = canvas.getContext('2d');
  const width = canvas.width;
  const height = canvas.height;
  const angle = 50; // 你的角度值
  const score = 50; // 你的分数值

  // 外层圆环
  context.beginPath();
  context.arc(width / 2, height - 20, width / 2 - 30, 1 * Math.PI, 2 * Math.PI);
  context.lineWidth = 4;
  context.lineCap = 'round';
  context.strokeStyle = '#DEDEDE';
  context.stroke();

  // 外层进度圆环
  context.beginPath();
  context.arc(width / 2, height - 20, width / 2 - 30, 1 * Math.PI, (1 + angle / 100) * Math.PI);
  context.lineWidth = 4;
  context.lineCap = 'round';
  const gnt1 = context.createLinearGradient(0, 0, 180, 0);
  gnt1.addColorStop(0, '#8ce459');
  gnt1.addColorStop(1, '#62af35');
  context.strokeStyle = gnt1;
  context.stroke();

  // 指示器
  const xAxis = Math.cos(Math.PI * 2 / 360 * (1.8 * (100 + angle))) * (width / 2 - 30);
  const yAxis = Math.sin(Math.PI * 2 / 360 * (1.8 * (100 + angle))) * (width / 2 - 30);
  context.beginPath();
  context.arc(width / 2 + xAxis, height - 20 + yAxis, 5, 0, 2 * Math.PI);
  context.fillStyle = '#5EAD35';
  context.fill();

  // 文本
  const textY = Math.sin(Math.PI * 2 / 360 * (1.8 * (100 + 15))) * (width / 2 - 30);
  context.fillStyle = '#168C66';
  context.font = '40px Arial';
  context.textAlign = 'center';
  context.textBaseline = 'middle';
  context.fillText(score, width / 2 - 5, height + 10 + textY);

  context.fillStyle = '#62AF35';
  context.font = '14px Arial';
  context.fillText('分', width / 2 + 30, height + 18 + textY);

  // 内层圆环
  context.beginPath();
  context.arc(width / 2, height - 20, width / 2 - 40, 1 * Math.PI, 2 * Math.PI);
  context.lineWidth = 2;
  context.setLineDash([1, 4]);
  context.lineCap = 'round';
  context.strokeStyle = '#A2BCC3';
  context.stroke();
</script>

</body>
</html>

例二

html 复制代码
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <style>
    .canvas-main {
      width: 400px;
      height: 400px;
      position: relative;
    }
    .main-text {
      width: 100%;
      height: 100%;
      display: flex;
      align-items: center;
      justify-content: center;
      position: absolute;
      top: 0;
      left: 0;
    }
  </style>
</head>
<body>
<div class="canvas-main">
  <canvas id="main-canvas" width="400" height="400"></canvas>
  <div class="main-text">10分</div>
</div>

<script>
  const canvas = document.getElementById('main-canvas');
  const context = canvas.getContext('2d');
  const width = canvas.width;
  const height = canvas.height;
  const score = 50; // 你的分数值
  const totalScore = 100; // 总分
  const scorePercentage = score / totalScore; // 你的分数值占总分的百分比

  // 外层圆环
  context.beginPath();
  context.arc(width / 2, height / 2, width / 2 - 30, 0.75 * Math.PI, 2.25 * Math.PI, false);
  context.lineWidth = 14;
  context.lineCap = 'round';
  context.strokeStyle = '#f5edfc';
  context.stroke();

  // 外层进度圆环
  context.beginPath();
  // 最小-最大:0.75 * Math.PI 到 2.25 * Math.PI    2.25 - 0.75 = 1.5
  context.arc(width / 2, height / 2, width / 2 - 30, 0.75 * Math.PI, (0.75 + 1.5 * scorePercentage) * Math.PI, false);
  context.lineWidth = 14;
  context.lineCap = 'round';
  const gnt1 = context.createLinearGradient(0, 0, 180, 0);
  gnt1.addColorStop(0, '#f5edfc');
  gnt1.addColorStop(1, '#9c4ce3');
  context.strokeStyle = gnt1;
  context.stroke();

  // 指示器
  const indicatorAngle = 0.75 + 1.5 * scorePercentage;
  const indicatorRadius = width / 2 - 30;
  const indicatorX = width / 2 + Math.cos(indicatorAngle * Math.PI) * indicatorRadius;
  const indicatorY = height / 2 + Math.sin(indicatorAngle * Math.PI) * indicatorRadius;

  context.beginPath();
  context.arc(indicatorX, indicatorY, 10, 0, 2 * Math.PI); // 外圈半径设置为 10
  context.fillStyle = '#fff';
  context.strokeStyle = '#fff'; // 外圈线颜色也为白色
  context.lineWidth = 2; // 设置线宽,增加外圈线的宽度
  context.fill();
  context.stroke();

  // 指示器内部填充红色
  context.beginPath();
  context.arc(indicatorX, indicatorY, 6, 0, 2 * Math.PI);
  context.fillStyle = '#9c4ce3';
  context.fill();
</script>

</body>
</html>

小程序

如果是小程序的话,把 api 换一下

html 复制代码
<canvas id="ring" canvas-id="ring" class="progress-canvas"></canvas>
js 复制代码
Component({
  /**
   * 组件的属性列表
   */
  properties: {
    score: {
      type: Number
    },
    totalScore: {
      type: Number
    }
  },
  observers: {
    score: function(data) {
      if (data || data === 0) {
        this.init()
      }
    }
  },

  /**
   * 组件的方法列表
   */
  methods: {
    init() {
      const query = this.createSelectorQuery()
      query
        .select('#ring')
        .boundingClientRect(res => {
          this.drawRing(
            'ring',
            res.width,
            res.height,
            this.data.score,
            this.data.totalScore
          )
        })
        .exec()
    },
    drawRing: function(canvasId, width, height, score, totalScore) {
      var context = wx.createCanvasContext(canvasId, this)

      // const score = 50 // 你的分数值
      // const totalScore = 100 // 总分
      const scorePercentage = score / totalScore // 你的分数值占总分的百分比

      // 外层圆环
      context.beginPath()
      context.arc(
        width / 2,
        height / 2,
        width / 2 - 30,
        0.75 * Math.PI,
        2.25 * Math.PI,
        false
      )
      context.lineWidth = 14
      context.lineCap = 'round'
      context.strokeStyle = '#f5edfc'
      context.stroke()

      // 外层进度圆环
      context.beginPath()
      context.arc(
        width / 2,
        height / 2,
        width / 2 - 30,
        0.75 * Math.PI,
        (0.75 + 1.5 * scorePercentage) * Math.PI,
        false
      )
      context.lineWidth = 14
      context.lineCap = 'round'
      const gnt1 = context.createLinearGradient(0, 0, 180, 0)
      gnt1.addColorStop(0, '#f5edfc')
      gnt1.addColorStop(1, '#9c4ce3')
      context.strokeStyle = gnt1
      context.stroke()

      // 指示器
      const indicatorAngle = 0.75 + 1.5 * scorePercentage
      const indicatorRadius = width / 2 - 30
      const indicatorX =
        width / 2 + Math.cos(indicatorAngle * Math.PI) * indicatorRadius
      const indicatorY =
        height / 2 + Math.sin(indicatorAngle * Math.PI) * indicatorRadius

      // 指示器外圈
      context.beginPath()
      context.arc(indicatorX, indicatorY, 10, 0, 2 * Math.PI) // 外圈半径设置为 10
      context.setFillStyle('#fff')
      context.setStrokeStyle('#fff') // 外圈线颜色也为白色
      context.setLineWidth(2) // 设置线宽,增加外圈线的宽度
      context.fill()
      context.stroke()

      // 指示器内部填充红色
      context.beginPath()
      context.arc(indicatorX, indicatorY, 6, 0, 2 * Math.PI)
      context.setFillStyle('#9c4ce3')
      context.fill()

      context.draw()
    }
  }
})
相关推荐
m0_74347037几秒前
高性能计算框架实现
开发语言·c++·算法
weixin_307779131 分钟前
2025年中国研究生数学建模竞赛A题:通用神经网络处理器下的核内调度问题——解决方案与实现
开发语言·人工智能·python·数学建模·性能优化
焦糖玛奇朵婷2 分钟前
盲盒小程序开发|解锁开箱新体验[特殊字符]
大数据·开发语言·程序人生·小程序·软件需求
1104.北光c°2 分钟前
基于Canal + Kafka的高可用关注系统:一主多从关系链
java·开发语言·笔记·分布式·程序人生·kafka·一主多从
Mem0rin4 分钟前
[Java]异常及其处理
java·开发语言
HelloReader4 分钟前
Qt Quick vs Qt Widgets如何选择适合你的 UI 技术路线(五)
前端
cmd6 分钟前
吃透 ES6 Generator:yield/next/yield* 核心用法详解
前端·javascript
2401_846341657 分钟前
调试技巧与核心转储分析
开发语言·c++·算法
我叫黑大帅8 分钟前
🎯 DOM 事件:onclick VS addEventListener('click')区别
前端·javascript·面试
踩着两条虫10 分钟前
AI 驱动的 Vue3 应用开发平台 深入探究(二十二):CLI与工具链之开发与生产工作流
前端·vue.js·ai编程