开发预览 更新于 2026-05-10

RidgePlot 密度脊图

多组密度曲线垂直堆叠。

基础用法

数据通过 props 传入,纯 SVG 渲染,无第三方图表库依赖。 配色取自 --viz-1..8 token,色盲友好。

背景 视口
2024 Q12024 Q22024 Q32024 Q4
src/App.vue
<script setup lang="ts">
import { CfRidgePlot } from '@chufix-design/vue';
function bell(center: number, n = 30) {
  return Array.from({ length: n }, (_, i) => {
    const x = (i - center) / 4;
    return Math.exp(-x * x);
  });
}
const rows = [
  { label: '2024 Q1', density: bell(8) },
  { label: '2024 Q2', density: bell(12) },
  { label: '2024 Q3', density: bell(15) },
  { label: '2024 Q4', density: bell(20) },
];
</script>
<template>
  <CfRidgePlot :rows="rows" />
</template>
<script setup>
import { CfRidgePlot } from '@chufix-design/vue';
function bell(center, n = 30) {
  return Array.from({ length: n }, (_, i) => {
    const x = (i - center) / 4;
    return Math.exp(-x * x);
  });
}
const rows = [
  { label: '2024 Q1', density: bell(8) },
  { label: '2024 Q2', density: bell(12) },
  { label: '2024 Q3', density: bell(15) },
  { label: '2024 Q4', density: bell(20) },
];
</script>
<template>
  <CfRidgePlot :rows="rows" />
</template>
import { CfRidgePlot } from '@chufix-design/react';

export default function Demo() {
  function bell(center: number, n = 30) {
    return Array.from({ length: n }, (_, i) => {
      const x = (i - center) / 4;
      return Math.exp(-x * x);
    });
  }
  const rows = [
    { label: '2024 Q1', density: bell(8) },
    { label: '2024 Q2', density: bell(12) },
    { label: '2024 Q3', density: bell(15) },
    { label: '2024 Q4', density: bell(20) },
  ];
  return (
    <>
      <CfRidgePlot rows={rows} />
    </>
  );
}
import { CfRidgePlot } from '@chufix-design/react';

export default function Demo() {
  function bell(center, n = 30) {
    return Array.from({ length: n }, (_, i) => {
      const x = (i - center) / 4;
      return Math.exp(-x * x);
    });
  }
  const rows = [
    { label: '2024 Q1', density: bell(8) },
    { label: '2024 Q2', density: bell(12) },
    { label: '2024 Q3', density: bell(15) },
    { label: '2024 Q4', density: bell(20) },
  ];
  return (
    <>
      <CfRidgePlot rows={rows} />
    </>
  );
}

多峰分布

把多个 bell 叠加可得到双峰分布,常见于跨季节、跨用户群的二次集中点。

背景 视口
src/App.vue
<script setup lang="ts">
import { CfRidgePlot } from '@chufix-design/vue';
function bell(center: number, n = 30, height = 1) {
  return Array.from({ length: n }, (_, i) => {
    const x = (i - center) / 4;
    return Math.exp(-x * x) * height;
  });
}
function combine(...waves: number[][]) {
  const out = new Array(waves[0].length).fill(0);
  for (const w of waves) for (let i = 0; i < w.length; i++) out[i] += w[i];
  return out;
}
const rows = [
  { label: '春', density: combine(bell(8, 30, 1), bell(20, 30, 0.4)) },
  { label: '夏', density: combine(bell(15, 30, 1.2)) },
  { label: '秋', density: combine(bell(10, 30, 0.6), bell(22, 30, 1.0)) },
  { label: '冬', density: combine(bell(5, 30, 0.8), bell(25, 30, 0.6)) },
];
</script>
<template>
  <CfRidgePlot :rows="rows" :height="220" :overlap="0.5" />
</template>
<script setup>
import { CfRidgePlot } from '@chufix-design/vue';
function bell(center, n = 30, height = 1) {
  return Array.from({ length: n }, (_, i) => {
    const x = (i - center) / 4;
    return Math.exp(-x * x) * height;
  });
}
function combine(...waves: number[][]) {
  const out = new Array(waves[0].length).fill(0);
  for (const w of waves) for (let i = 0; i < w.length; i++) out[i] += w[i];
  return out;
}
const rows = [
  { label: '春', density: combine(bell(8, 30, 1), bell(20, 30, 0.4)) },
  { label: '夏', density: combine(bell(15, 30, 1.2)) },
  { label: '秋', density: combine(bell(10, 30, 0.6), bell(22, 30, 1.0)) },
  { label: '冬', density: combine(bell(5, 30, 0.8), bell(25, 30, 0.6)) },
];
</script>
<template>
  <CfRidgePlot :rows="rows" :height="220" :overlap="0.5" />
</template>
import { CfRidgePlot } from '@chufix-design/react';

export default function Demo() {
  const rows = [
    { label: '春', density: combine(bell(8, 30, 1), bell(20, 30, 0.4)) },
    { label: '夏', density: combine(bell(15, 30, 1.2)) },
    { label: '秋', density: combine(bell(10, 30, 0.6), bell(22, 30, 1.0)) },
    { label: '冬', density: combine(bell(5, 30, 0.8), bell(25, 30, 0.6)) },
  ];
  return (
    <>
      <CfRidgePlot rows={rows} overlap={0.5} />
    </>
  );
}
import { CfRidgePlot } from '@chufix-design/react';

export default function Demo() {
  const rows = [
    { label: '春', density: combine(bell(8, 30, 1), bell(20, 30, 0.4)) },
    { label: '夏', density: combine(bell(15, 30, 1.2)) },
    { label: '秋', density: combine(bell(10, 30, 0.6), bell(22, 30, 1.0)) },
    { label: '冬', density: combine(bell(5, 30, 0.8), bell(25, 30, 0.6)) },
  ];
  return (
    <>
      <CfRidgePlot rows={rows} overlap={0.5} />
    </>
  );
}

API

属性类型默认值说明
rowsRidgeRow[]{ label, density: number[], colorIndex? }[]
widthnumber480SVG 宽度
heightnumber240SVG 高度
overlap0..10.6行间重叠比例
ariaLabelstring透传给根 <svg>aria-label

Events

Vue 事件React 回调载荷类型说明
item-enteronItemEnterRidgePlotInteractionPayload鼠标进入某行密度曲线时触发
item-leaveonItemLeaveRidgePlotInteractionPayload鼠标离开某行时触发

类型

interface RidgePlotInteractionPayload {
  row: RidgeRow;
  rowIndex: number;
  nativeEvent?: PointerEvent;
}

反馈与讨论

RidgePlot 密度脊图 的讨论

0
0 / 600
正在加载评论...