Volume Profile
通过横向双向条形图比较各价格区间的成交量。
适用场景
- 当界面需要以下行为时使用 Volume Profile:通过横向双向条形图比较各价格区间的成交量。
安装
选择安装方式,用法示例将同步更新导入路径。
$ bunx --bun shadcn-vue@2.8.2 add https://orvui.efu.me/r/volume-profile.json https://orvui.efu.me/r/tabs.json
首次使用时,将安装的 src/components/orvui/styles.css 导入项目样式入口。
示例
预览
BTC / USD
Volume by price · Demo data
- Total volume
- 2,271 BTC
- Point of control
- $65,050
- Value area · 70% target
- $64,700 – $65,400
- 64.2% of volume
| Price range | Volume | Share | Point of control | Value area |
|---|---|---|---|---|
| $64,000 to $64,100 | 18 | 0.8% | No | No |
| $64,100 to $64,200 | 27 | 1.2% | No | No |
| $64,200 to $64,300 | 36 | 1.6% | No | No |
| $64,300 to $64,400 | 52 | 2.3% | No | No |
| $64,400 to $64,500 | 70 | 3.1% | No | No |
| $64,500 to $64,600 | 96 | 4.2% | No | No |
| $64,600 to $64,700 | 135 | 5.9% | No | No |
| $64,700 to $64,800 | 172 | 7.6% | No | Yes |
| $64,800 to $64,900 | 205 | 9.0% | No | Yes |
| $64,900 to $65,000 | 241 | 10.6% | No | Yes |
| $65,000 to $65,100 | 278 | 12.2% | Yes | Yes |
| $65,100 to $65,200 | 225 | 9.9% | No | Yes |
| $65,200 to $65,300 | 192 | 8.5% | No | Yes |
| $65,300 to $65,400 | 146 | 6.4% | No | Yes |
| $65,400 to $65,500 | 108 | 4.8% | No | No |
| $65,500 to $65,600 | 79 | 3.5% | No | No |
| $65,600 to $65,700 | 56 | 2.5% | No | No |
| $65,700 to $65,800 | 42 | 1.8% | No | No |
| $65,800 to $65,900 | 31 | 1.4% | No | No |
| $65,900 to $66,000 | 22 | 1.0% | No | No |
| $66,000 to $66,100 | 16 | 0.7% | No | No |
| $66,100 to $66,200 | 11 | 0.5% | No | No |
| $66,200 to $66,300 | 8 | 0.4% | No | No |
| $66,300 to $66,400 | 5 | 0.2% | No | No |
packages/vue/src/charts/volume-profile/demo.vue
<script setup lang="ts">
import { ref, useId } from "vue";
import { Tabs, TabsList, TabsTrigger } from "@/components/orvui/motion/tabs";
import {
VolumeProfile,
VolumeProfilePlot,
VolumeProfileTooltip,
VolumeProfileSummary,
type VolumeProfileBin,
} from "@/components/orvui/charts/volume-profile";
const sessions = [
{
id: "today",
label: "Today",
volumes: [
18, 27, 36, 52, 70, 96, 135, 172, 205, 241, 278, 225, 192, 146, 108, 79,
56, 42, 31, 22, 16, 11, 8, 5,
],
},
{
id: "previous",
label: "Previous",
volumes: [
6, 9, 13, 18, 27, 40, 56, 74, 95, 114, 146, 188, 242, 286, 253, 208, 165,
126, 92, 65, 42, 30, 21, 14,
],
},
{
id: "week",
label: "Week",
volumes: [
75, 104, 143, 214, 305, 382, 494, 626, 795, 902, 1028, 1182, 1097, 973,
825, 695, 589, 481, 372, 289, 195, 154, 119, 84,
],
},
];
const data: Record<string, readonly VolumeProfileBin[]> = {};
for (const session of sessions)
data[session.id] = session.volumes.map((volume, index) => ({
id: `price-${64000 + index * 100}`,
priceLow: 64000 + index * 100,
priceHigh: 64100 + index * 100,
volume,
}));
const currency = new Intl.NumberFormat("en-US", {
style: "currency",
currency: "USD",
maximumFractionDigits: 0,
});
const amount = new Intl.NumberFormat("en-US", { maximumFractionDigits: 2 });
const formatPrice = (price: number) => currency.format(price),
formatVolume = (volume: number) => amount.format(volume);
const session = ref("today"),
previewId = useId(),
panelId = previewId + "-profile";
</script>
<template>
<div class="w-full max-w-xl min-w-0 py-2">
<Tabs v-model="session"
><div class="mb-6 flex flex-wrap items-center justify-between gap-3">
<div class="space-y-1">
<p class="text-sm font-medium">BTC / USD</p>
<p class="text-xs text-muted-foreground">
Volume by price · Demo data
</p>
</div>
<TabsList
aria-label="Profile session"
wrapper-class-name="w-auto"
class="bg-muted"
><TabsTrigger
v-for="item in sessions"
:key="item.id"
:value="item.id"
:id="`${previewId}-${item.id}`"
:aria-controls="panelId"
:tabindex="session === item.id ? 0 : -1"
indicator-class-name="bg-background"
class="min-h-8 px-3 py-1.5 text-xs [&_[data-tabs-label]]:text-foreground"
>{{ item.label }}</TabsTrigger
></TabsList
>
</div>
<div
:id="panelId"
role="tabpanel"
:aria-labelledby="`${previewId}-${session}`"
>
<VolumeProfile
:data="data[session]"
:format-price="formatPrice"
:format-volume="formatVolume"
unit="BTC"
label="BTC volume profile"
><VolumeProfilePlot
class="h-72 sm:h-80" /><VolumeProfileTooltip /><VolumeProfileSummary
/></VolumeProfile></div
></Tabs>
</div>
</template>
packages/vue/src/charts/volume-profile/src/VolumeProfile.vue
状态与事件
需要由表单、路由或父组件管理时,绑定下列状态并接收对应更新。v-model 是属性与 update 事件的简写;值的类型应与 API 一致。
| 组件 | Vue 绑定 | 更新事件 |
|---|---|---|
VolumeProfile | v-model:activeIdnull | string | update:activeId |
可选受控属性未传入时,按组件文档的默认行为工作;不要用固定的 false 或空值代替未传入。异步请求和持久化在应用中处理,再把结果传回组件。
API 参考
VolumeProfile
datareadonly VolumeProfileBin[]—label?string"Volume profile"unit?string"units"formatPrice?(value: number) => stringformatNumberformatVolume?(value: number) => stringformatNumbervalueArea?number | falseundefinedcolor?string"#6366f1"pocColor?string"#f59e0b"activeId?null | string—defaultActiveId?null | stringnullclassName?string—事件
update:activeId—@update:activeIdactiveChange—@activeChange插槽
default() => VNodeChild#defaultVolumeProfilePlot
barClassName?string—showAxes?booleantrue插槽
default() => VNodeChild#defaultVolumeProfileTooltip
side?"top" | "bottom" | "left" | "right""top"插槽
default—作用域参数: row
#default无障碍
为交互控件提供明确的无障碍名称。保留文档中的键盘路径、可见焦点与减少动态效果状态;自定义插槽应继续保留所替代部件的语义。
常见问题
- 如何配置 Volume Profile?
- 通过 data、label、unit、formatPrice、formatVolume、valueArea、color、pocColor、activeId、defaultActiveId、className 配置 Volume Profile;API 表列出其类型与默认值。
- 如何连接到应用状态?
- 监听 update:activeId、activeChange,将组件连接到应用状态。
- 可以在服务端渲染吗?
- 可以在 Vue SSR 应用中正常渲染。服务端与客户端的初始 props 应保持一致;浏览器测量与交互资源在挂载后启动。
更新于
来源与许可 ↗MIT · 0.1.1