Candlestick Chart
借助指针或键盘十字线检查开高低收与成交量数据。
适用场景
- 当界面需要以下行为时使用 Candlestick Chart:借助指针或键盘十字线检查开高低收与成交量数据。
安装
选择安装方式,用法示例将同步更新导入路径。
$ bunx --bun shadcn-vue@2.8.2 add https://orvui.efu.me/r/candlestick-chart.json https://orvui.efu.me/r/button.json
首次使用时,将安装的 src/components/orvui/styles.css 导入项目样式入口。
示例
预览
ETH / USD · 5mSimulated
- Candle
- 15:55
- O
- 2,360.34
- H
- 2,369.93
- L
- 2,351.12
- C
- 2,368.44
- Vol
- 151.2
- +0.34%
2,439.722,417.852,395.982,374.112,352.24
2,368.44
| Period | Open | High | Low | Close | Volume |
|---|---|---|---|---|---|
| 12:00 | 2,380.00 | 2,392.92 | 2,380.00 | 2,387.14 | 120 |
| 12:05 | 2,387.14 | 2,403.53 | 2,377.57 | 2,400.26 | 357.1 |
| 12:10 | 2,400.26 | 2,427.22 | 2,387.72 | 2,416.18 | 511.5 |
| 12:15 | 2,416.18 | 2,438.47 | 2,411.26 | 2,430.11 | 529 |
| 12:20 | 2,430.11 | 2,442.00 | 2,426.12 | 2,437.78 | 403.7 |
| 12:25 | 2,437.78 | 2,439.72 | 2,435.41 | 2,437.35 | 179.3 |
| 12:30 | 2,437.35 | 2,437.81 | 2,423.65 | 2,430.24 | 305.9 |
| 12:35 | 2,430.24 | 2,439.16 | 2,420.25 | 2,420.40 | 486.1 |
| 12:40 | 2,420.40 | 2,424.77 | 2,406.18 | 2,412.36 | 538.4 |
| 12:45 | 2,412.36 | 2,416.53 | 2,402.82 | 2,409.06 | 444.6 |
| 12:50 | 2,409.06 | 2,413.17 | 2,407.88 | 2,410.57 | 237.4 |
| 12:55 | 2,410.57 | 2,415.21 | 2,407.68 | 2,414.18 | 250.8 |
| 13:00 | 2,414.18 | 2,420.27 | 2,409.68 | 2,415.71 | 453.3 |
| 13:05 | 2,415.71 | 2,417.64 | 2,407.66 | 2,411.63 | 539.4 |
| 13:10 | 2,411.63 | 2,420.34 | 2,400.52 | 2,400.88 | 478.9 |
| 13:15 | 2,400.88 | 2,409.92 | 2,376.44 | 2,385.57 | 293.1 |
| 13:20 | 2,385.57 | 2,390.52 | 2,357.20 | 2,370.09 | 193.2 |
| 13:25 | 2,370.09 | 2,380.43 | 2,354.99 | 2,359.11 | 413.9 |
| 13:30 | 2,359.11 | 2,359.86 | 2,353.40 | 2,355.60 | 532 |
| 13:35 | 2,355.60 | 2,365.76 | 2,348.85 | 2,359.53 | 506.1 |
| 13:40 | 2,359.53 | 2,372.95 | 2,353.88 | 2,368.09 | 345.4 |
| 13:45 | 2,368.09 | 2,382.12 | 2,367.62 | 2,376.93 | 134.1 |
| 13:50 | 2,376.93 | 2,389.01 | 2,371.12 | 2,382.24 | 368.7 |
| 13:55 | 2,382.24 | 2,382.64 | 2,379.85 | 2,382.51 | 516.4 |
| 14:00 | 2,382.51 | 2,388.83 | 2,376.74 | 2,379.21 | 525.6 |
| 14:05 | 2,379.21 | 2,381.31 | 2,373.89 | 2,376.07 | 393.1 |
| 14:10 | 2,376.07 | 2,380.34 | 2,371.72 | 2,377.13 | 165.3 |
| 14:15 | 2,377.13 | 2,392.12 | 2,370.74 | 2,384.72 | 318.4 |
| 14:20 | 2,384.72 | 2,401.05 | 2,384.03 | 2,398.29 | 492.8 |
| 14:25 | 2,398.29 | 2,428.67 | 2,386.61 | 2,414.48 | 536.9 |
| 14:30 | 2,414.48 | 2,431.46 | 2,403.77 | 2,428.47 | 435.4 |
| 14:35 | 2,428.47 | 2,443.11 | 2,425.47 | 2,436.08 | 223.7 |
| 14:40 | 2,436.08 | 2,439.30 | 2,433.45 | 2,435.67 | 264.2 |
| 14:45 | 2,435.67 | 2,438.48 | 2,422.94 | 2,428.86 | 461.7 |
| 14:50 | 2,428.86 | 2,438.58 | 2,412.41 | 2,419.66 | 539.9 |
| 14:55 | 2,419.66 | 2,420.05 | 2,411.92 | 2,412.53 | 471.4 |
| 15:00 | 2,412.53 | 2,417.15 | 2,406.13 | 2,410.19 | 280.1 |
| 15:05 | 2,410.19 | 2,415.63 | 2,404.81 | 2,412.47 | 207.1 |
| 15:10 | 2,412.47 | 2,419.09 | 2,411.04 | 2,416.46 | 423.9 |
| 15:15 | 2,416.46 | 2,422.73 | 2,413.73 | 2,417.94 | 534.5 |
| 15:20 | 2,417.94 | 2,418.90 | 2,407.67 | 2,413.49 | 500.3 |
| 15:25 | 2,413.49 | 2,423.46 | 2,395.08 | 2,402.27 | 333.3 |
| 15:30 | 2,402.27 | 2,407.34 | 2,385.20 | 2,386.58 | 148.2 |
| 15:35 | 2,386.58 | 2,395.30 | 2,361.74 | 2,370.95 | 379.9 |
| 15:40 | 2,370.95 | 2,379.60 | 2,349.71 | 2,360.01 | 520.8 |
| 15:45 | 2,360.01 | 2,362.03 | 2,354.72 | 2,356.54 | 521.7 |
| 15:50 | 2,356.54 | 2,365.70 | 2,353.68 | 2,360.34 | 382.2 |
| 15:55 | 2,360.34 | 2,369.93 | 2,351.12 | 2,368.44 | 151.2 |
packages/vue/src/charts/candlestick-chart/demo.vue
<script setup lang="ts">
import { onBeforeUnmount, onMounted, ref } from "vue";
import { Button } from "@/components/orvui/motion/button";
import {
CandlestickChart,
CandlestickChartLegend,
CandlestickChartPlot,
type Candle,
} from "@/components/orvui/charts/candlestick-chart";
const label = (seconds: number) =>
`${String(Math.floor(seconds / 3600) % 24).padStart(2, "0")}:${String(Math.floor((seconds % 3600) / 60)).padStart(2, "0")}`;
function initial(): Candle[] {
let previous = 2380;
return Array.from({ length: 48 }, (_, index) => {
const open = previous,
close =
open *
(1 + Math.sin(index * 0.7) * 0.004 + Math.cos(index * 0.23) * 0.003);
const reach =
Math.abs(close - open) * 0.6 +
1.5 +
Math.abs(Math.sin(index * 1.9)) * 3,
start = 43200 + index * 300;
previous = close;
return {
id: String(start),
label: label(start),
open,
close,
high: Math.max(open, close) + reach * Math.abs(Math.cos(index * 1.3)),
low: Math.min(open, close) - reach * Math.abs(Math.sin(index * 0.9)),
volume: 120 + Math.abs(Math.sin(index * 0.6)) * 420,
};
});
}
const playing = ref(true),
state = ref({ tick: 0, candles: initial() });
function advance() {
const tick = state.value.tick + 1,
candles = state.value.candles,
last = candles.at(-1)!;
const price =
last.close *
(1 + Math.sin(tick * 1.7) * 0.0012 + Math.cos(tick * 0.6) * 0.0006);
const next = [
...candles.slice(0, -1),
{
...last,
close: price,
high: Math.max(last.high, price),
low: Math.min(last.low, price),
volume: last.volume + 12 + Math.abs(Math.sin(tick)) * 30,
},
];
if (tick % 8 === 0) {
next.shift();
const start = Number(last.id) + 300;
next.push({
id: String(start),
label: label(start),
open: price,
close: price,
high: price,
low: price,
volume: 0,
});
}
state.value = { tick, candles: next };
}
function step() {
playing.value = false;
advance();
}
let timer: ReturnType<typeof setInterval> | undefined;
onMounted(() => {
timer = setInterval(() => {
if (playing.value && !document.hidden) advance();
}, 650);
});
onBeforeUnmount(() => clearInterval(timer));
const price = new Intl.NumberFormat("en-US", {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
});
const formatPrice = (value: number) => price.format(value);
</script>
<template>
<div class="w-full max-w-3xl space-y-3">
<div class="flex flex-wrap items-center justify-between gap-3 px-1">
<div class="flex items-center gap-2 text-sm font-medium">
ETH / USD · 5m<span
class="rounded border border-border px-1.5 py-0.5 text-[9px] font-normal uppercase tracking-wider text-muted-foreground"
>Simulated</span
>
</div>
<div class="flex items-center gap-2">
<Button variant="ghost" size="sm" @click="playing = !playing">{{
playing ? "Pause" : "Resume"
}}</Button
><Button variant="ghost" size="sm" @click="step">Step</Button>
</div>
</div>
<CandlestickChart
:candles="state.candles"
:last-price="state.candles.at(-1)?.close"
label="Simulated ETH / USD, 5 minute candles"
:format-price="formatPrice"
><CandlestickChartLegend /><CandlestickChartPlot
:height="320"
class="mt-4"
/></CandlestickChart>
</div>
</template>
packages/vue/src/charts/candlestick-chart/src/CandlestickChart.vue
状态与事件
需要由表单、路由或父组件管理时,绑定下列状态并接收对应更新。v-model 是属性与 update 事件的简写;值的类型应与 API 一致。
| 组件 | Vue 绑定 | 更新事件 |
|---|---|---|
CandlestickChart | v-model:activeIdnull | string | update:activeId |
可选受控属性未传入时,按组件文档的默认行为工作;不要用固定的 false 或空值代替未传入。异步请求和持久化在应用中处理,再把结果传回组件。
API 参考
CandlestickChart
candlesreadonly Candle[]—lastPrice?number—activeId?null | string—defaultActiveId?null | stringnullformatPrice?(value: number) => stringformatNumberformatVolume?(value: number) => stringformatCompactlabel?string"Candlestick chart"className?string—事件
update:activeId—@update:activeIdactiveIdChange—@activeIdChange插槽
default() => VNodeChild#defaultCandlestickChartLegend
插槽
default—作用域参数: candle, change
#defaultCandlestickChartPlot
height?number320无障碍
为交互控件提供明确的无障碍名称。保留文档中的键盘路径、可见焦点与减少动态效果状态;自定义插槽应继续保留所替代部件的语义。
常见问题
- 如何配置 Candlestick Chart?
- 通过 candles、lastPrice、activeId、defaultActiveId、formatPrice、formatVolume、label、className 配置 Candlestick Chart;API 表列出其类型与默认值。
- 如何连接到应用状态?
- 监听 update:activeId、activeIdChange,将组件连接到应用状态。
- 可以在服务端渲染吗?
- 可以在 Vue SSR 应用中正常渲染。服务端与客户端的初始 props 应保持一致;浏览器测量与交互资源在挂载后启动。
更新于
来源与许可 ↗MIT · 0.1.1