当前位置:首页 > JavaScript

原生js实现饼图

2026-01-31 04:32:10JavaScript

使用Canvas绘制饼图

在HTML中创建<canvas>元素作为绘图区域,通过JavaScript计算数据比例并绘制扇形。

<canvas id="pieChart" width="400" height="400"></canvas>
function drawPieChart(data) {
  const canvas = document.getElementById('pieChart');
  const ctx = canvas.getContext('2d');
  const total = data.reduce((sum, item) => sum + item.value, 0);
  let startAngle = 0;

  data.forEach((item, index) => {
    const sliceAngle = (item.value / total) * 2 * Math.PI;
    ctx.beginPath();
    ctx.fillStyle = item.color || getRandomColor();
    ctx.moveTo(200, 200);
    ctx.arc(200, 200, 150, startAngle, startAngle + sliceAngle);
    ctx.closePath();
    ctx.fill();
    startAngle += sliceAngle;
  });
}

function getRandomColor() {
  return `#${Math.floor(Math.random()*16777215).toString(16)}`;
}

// 示例数据
const chartData = [
  { value: 30, color: '#FF6384' },
  { value: 50, color: '#36A2EB' },
  { value: 20, color: '#FFCE56' }
];

drawPieChart(chartData);

添加交互效果

为饼图添加鼠标悬停高亮效果,通过检测鼠标位置是否在扇形区域内实现交互。

canvas.addEventListener('mousemove', (e) => {
  const rect = canvas.getBoundingClientRect();
  const x = e.clientX - rect.left - 200;
  const y = e.clientY - rect.top - 200;
  const distance = Math.sqrt(x*x + y*y);

  if (distance <= 150) {
    const angle = Math.atan2(y, x);
    let cumulativeAngle = 0;

    data.forEach((item, index) => {
      const sliceAngle = (item.value / total) * 2 * Math.PI;
      if (angle >= cumulativeAngle && angle < cumulativeAngle + sliceAngle) {
        // 高亮处理逻辑
      }
      cumulativeAngle += sliceAngle;
    });
  }
});

添加图例和标签

在饼图旁添加说明性文字,显示各数据项的名称和占比。

function drawLegend() {
  const legend = document.createElement('div');
  legend.style.display = 'flex';
  legend.style.flexDirection = 'column';

  data.forEach(item => {
    const legendItem = document.createElement('div');
    legendItem.style.display = 'flex';
    legendItem.style.alignItems = 'center';

    const colorBox = document.createElement('div');
    colorBox.style.width = '20px';
    colorBox.style.height = '20px';
    colorBox.style.backgroundColor = item.color;
    colorBox.style.marginRight = '10px';

    const label = document.createElement('span');
    label.textContent = `${item.name}: ${((item.value/total)*100).toFixed(1)}%`;

    legendItem.appendChild(colorBox);
    legendItem.appendChild(label);
    legend.appendChild(legendItem);
  });

  document.body.appendChild(legend);
}

响应式调整

监听窗口大小变化事件,动态调整饼图尺寸。

function resizeCanvas() {
  const container = canvas.parentElement;
  const size = Math.min(container.clientWidth, container.clientHeight) * 0.8;
  canvas.width = size;
  canvas.height = size;
  drawPieChart(data);
}

window.addEventListener('resize', resizeCanvas);
resizeCanvas();

动画效果实现

为饼图添加加载动画效果,使用requestAnimationFrame逐步绘制。

原生js实现饼图

function animatePieChart() {
  let progress = 0;
  const duration = 1000; // 动画持续时间(ms)
  const startTime = performance.now();

  function frame(timestamp) {
    progress = Math.min((timestamp - startTime) / duration, 1);
    ctx.clearRect(0, 0, canvas.width, canvas.height);

    let currentAngle = 0;
    data.forEach(item => {
      const endAngle = currentAngle + (item.value / total) * 2 * Math.PI * progress;
      ctx.beginPath();
      ctx.fillStyle = item.color;
      ctx.moveTo(200, 200);
      ctx.arc(200, 200, 150, currentAngle, endAngle);
      ctx.closePath();
      ctx.fill();
      currentAngle = endAngle;
    });

    if (progress < 1) requestAnimationFrame(frame);
  }

  requestAnimationFrame(frame);
}

注意事项

  • 确保数据总和不为零,否则会导致计算比例时出现除零错误
  • 对于大量数据项,建议合并小比例项目为"其他"类别
  • 考虑添加过渡动画时注意性能影响
  • 移动端需要额外处理触摸事件

标签: js
分享给朋友:

相关文章

vue实现js休眠

vue实现js休眠

Vue 中实现 JavaScript 休眠 在 Vue 中实现 JavaScript 休眠通常需要使用异步方式,以避免阻塞主线程。以下是几种常见方法: 使用 setTimeout 和 Pro…

js实现选项卡

js实现选项卡

实现选项卡的基本思路 选项卡通常由一组标签和对应的内容面板组成。点击标签时,显示对应的内容面板,隐藏其他面板。实现这一效果需要结合HTML结构、CSS样式和JavaScript交互逻辑。 HTML结…

js实现复制功能

js实现复制功能

使用 document.execCommand 方法 这种方法适用于较旧的浏览器,但在现代浏览器中可能被逐步淘汰。通过创建一个临时的 textarea 元素,将文本内容放入其中,然后执行复制命令。…

js实现复制到剪贴板

js实现复制到剪贴板

使用document.execCommand方法(传统方式,已废弃但部分浏览器仍支持) 该方法适用于旧版浏览器,但已被标记为废弃。执行前需确保文本已被选中: function copyToClipb…

js实现换肤

js实现换肤

使用CSS变量实现换肤 通过CSS变量可以轻松实现主题切换功能。CSS变量在根元素中定义,通过JavaScript动态修改这些变量值。 :root { --primary-color: #349…

js实现变形

js实现变形

使用 CSS 和 JavaScript 实现元素变形 在 JavaScript 中实现变形效果通常结合 CSS 的 transform 属性,通过动态修改该属性实现元素的旋转、缩放、倾斜或平移。 基…