当前位置:首页 > JavaScript

js实现漏斗

2026-02-02 08:14:56JavaScript

使用 Canvas 绘制漏斗图

Canvas 提供了灵活的绘图能力,适合实现自定义漏斗效果。以下是一个基础实现示例:

const canvas = document.getElementById('funnelCanvas');
const ctx = canvas.getContext('2d');

function drawFunnel(data) {
  const width = canvas.width;
  const height = canvas.height;
  const segmentCount = data.length;
  const segmentHeight = height / segmentCount;

  data.forEach((item, index) => {
    const ratio = 1 - (index * 0.15); // 每层宽度递减比例
    const segmentWidth = width * ratio;
    const x = (width - segmentWidth) / 2;
    const y = index * segmentHeight;

    ctx.fillStyle = `hsl(${index * 30}, 70%, 60%)`;
    ctx.beginPath();
    ctx.moveTo(x, y);
    ctx.lineTo(x + segmentWidth, y);
    ctx.lineTo(x + segmentWidth * 0.9, y + segmentHeight);
    ctx.lineTo(x + segmentWidth * 0.1, y + segmentHeight);
    ctx.closePath();
    ctx.fill();

    ctx.fillStyle = '#fff';
    ctx.textAlign = 'center';
    ctx.fillText(item.label, width/2, y + segmentHeight/2 + 5);
  });
}

drawFunnel([
  { label: 'Visits', value: 1000 },
  { label: 'Signups', value: 800 },
  { label: 'Purchases', value: 400 }
]);

使用 SVG 创建漏斗图

SVG 方案更适合需要动态交互的场景:

function createSVGFunnel(data, containerId) {
  const container = document.getElementById(containerId);
  const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
  svg.setAttribute('viewBox', '0 0 200 300');

  const total = Math.max(...data.map(d => d.value));
  const segmentHeight = 300 / data.length;

  data.forEach((item, i) => {
    const width = 200 * (item.value / total);
    const y = i * segmentHeight;
    const path = document.createElementNS("http://www.w3.org/2000/svg", "path");

    const d = `
      M ${100 - width/2} ${y}
      L ${100 + width/2} ${y}
      L ${100 + width/2 * 0.9} ${y + segmentHeight}
      L ${100 - width/2 * 0.9} ${y + segmentHeight}
      Z
    `;

    path.setAttribute('d', d);
    path.setAttribute('fill', `hsl(${i * 40}, 80%, 65%)`);
    svg.appendChild(path);

    const text = document.createElementNS("http://www.w3.org/2000/svg", "text");
    text.setAttribute('x', '100');
    text.setAttribute('y', y + segmentHeight/2);
    text.setAttribute('fill', 'white');
    text.setAttribute('text-anchor', 'middle');
    text.textContent = `${item.label}: ${item.value}`;
    svg.appendChild(text);
  });

  container.appendChild(svg);
}

使用 D3.js 高级方案

对于复杂数据可视化,D3.js 提供了更专业的解决方案:

import * as d3 from 'd3';

function renderD3Funnel(data, container) {
  const width = 600;
  const height = 400;
  const margin = { top: 20, right: 20, bottom: 60, left: 20 };

  const svg = d3.select(container)
    .append('svg')
    .attr('width', width)
    .attr('height', height);

  const maxValue = d3.max(data, d => d.value);
  const yScale = d3.scaleLinear()
    .domain([0, data.length])
    .range([margin.top, height - margin.bottom]);

  const xScale = d3.scaleLinear()
    .domain([0, maxValue])
    .range([0, width - margin.left - margin.right]);

  const color = d3.scaleOrdinal()
    .domain(data.map((d,i) => i))
    .range(d3.schemeTableau10);

  const funnel = svg.append('g')
    .attr('transform', `translate(${margin.left},0)`);

  data.forEach((d, i) => {
    const currentWidth = xScale(d.value);
    const nextWidth = i < data.length - 1 ? xScale(data[i+1].value) : 0;

    funnel.append('path')
      .attr('d', `
        M ${(width - currentWidth)/2} ${yScale(i)}
        L ${(width + currentWidth)/2} ${yScale(i)}
        L ${(width + nextWidth)/2} ${yScale(i+1)}
        L ${(width - nextWidth)/2} ${yScale(i+1)}
        Z
      `)
      .attr('fill', color(i))
      .attr('stroke', '#fff');

    funnel.append('text')
      .attr('x', width/2)
      .attr('y', yScale(i) + (yScale(1) - yScale(0))/2)
      .attr('text-anchor', 'middle')
      .attr('fill', 'white')
      .text(`${d.label} (${d.value})`);
  });
}

响应式设计注意事项

实现响应式漏斗图需要考虑以下要点:

js实现漏斗

  • 使用百分比宽度而非固定像素值
  • 添加 resize 事件监听器重新计算尺寸
  • 设置适当的 viewBox 保持 SVG 比例
  • 使用 CSS media queries 调整文字大小
window.addEventListener('resize', function() {
  const containerWidth = document.getElementById('chartContainer').clientWidth;
  const aspectRatio = 2/3; // 宽高比
  const newHeight = containerWidth * aspectRatio;

  d3.select('#funnelSVG')
    .attr('width', containerWidth)
    .attr('height', newHeight);
});

以上方案可根据具体需求选择,Canvas 适合性能敏感场景,SVG 适合需要交互的场景,D3.js 则提供最完整的数据可视化功能。实际项目中可结合 CSS 动画和过渡效果增强用户体验。

标签: 漏斗js
分享给朋友:

相关文章

js防抖和节流实现

js防抖和节流实现

防抖(Debounce)的实现 防抖的核心思想是在事件被触发后,延迟执行回调函数。如果在延迟时间内再次触发事件,则重新计时。适用于输入框搜索、窗口大小调整等场景。 function debounce…

js轮播图实现原理

js轮播图实现原理

轮播图的基本原理 轮播图通过动态切换显示的内容(图片、文本等)实现视觉上的滑动效果。核心原理是利用CSS和JavaScript控制元素的显示与隐藏,或通过变换位置实现滑动动画。 实现方法一:…

js实现拷贝

js实现拷贝

实现文本拷贝 使用 document.execCommand 方法(已废弃但兼容性较好): function copyText(text) { const textarea = document…

js实现自举

js实现自举

实现 JavaScript 自举的方法 JavaScript 自举(Bootstrap)通常指通过 JavaScript 动态加载自身或依赖的脚本,实现运行时环境的初始化或扩展。以下是几种常见实现方式…

js实现授权

js实现授权

授权流程设计 授权流程通常涉及前端与后端的交互,常见方案包括OAuth2.0、JWT等。以JWT为例的典型流程: 用户提交凭证(如用户名密码)到认证服务 服务端验证通过后生成包含用户信息的JWT令牌…

js实现密码

js实现密码

密码强度验证 使用正则表达式验证密码强度是一种常见方法。以下代码检查密码是否包含大小写字母、数字和特殊字符,且长度至少为8位: function checkPasswordStrength(pass…