Skip to content

Commit cbd0bf7

Browse files
committed
feat: Implement windowed rendering with horizontal scroll for chart
Implement viewport windowing to render only ~100 candles at a time instead of all data, significantly improving chart performance and readability. Changes: - Add state.go to manage global chart data and visible window - Update app.gx to use GetVisibleChartData() for windowed rendering - Connect ScrollManager.updateChart() to trigger app rerenders - Reduce visible window from 200 to 100 candles - Update subtitle to indicate ~100 candles visible - Set global app and scroll manager instances for state coordination The chart now renders a manageable subset of data with smooth horizontal scrolling, mouse wheel support, keyboard navigation, and automatic data prefetching near viewport edges.
1 parent 850c550 commit cbd0bf7

5 files changed

Lines changed: 61 additions & 13 deletions

File tree

examples/webgpu-chart/app.gx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
package main
22

33
func App() (Component) {
4-
// Fetch initial Bitcoin data from Binance with fallback to static data
5-
chartData := GetChartData()
4+
// Get visible window of chart data (managed by scroll manager)
5+
chartData := GetVisibleChartData()
66

77
Div(
88
ID("app"),
@@ -13,7 +13,7 @@ func App() (Component) {
1313
"Bitcoin Price Chart (Binance Data)"
1414
}
1515
P(Class("subtitle")) {
16-
"WebGPU-powered candlestick chart with viewport scrolling"
16+
"WebGPU-powered candlestick chart with viewport scrolling (~100 candles visible)"
1717
}
1818
Canvas(
1919
ID("chart-canvas"),

examples/webgpu-chart/app_gen.go

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

examples/webgpu-chart/main.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,11 @@ func main() {
3535
appComponent.BindApp(runtimeApp)
3636
log("[Go] App bound")
3737

38+
// Set global app for state management
39+
log("[Go] Setting global app...")
40+
SetGlobalApp(appComponent)
41+
log("[Go] Global app set")
42+
3843
// Mount
3944
log("[Go] Mounting to #app...")
4045
if err := runtimeApp.Mount("#app"); err != nil {
@@ -49,6 +54,11 @@ func main() {
4954
scrollManager := NewScrollManager("chart-canvas", initialData)
5055
log("[Go] Scroll manager initialized")
5156

57+
// Set global scroll manager for state management
58+
log("[Go] Setting global scroll manager...")
59+
SetGlobalScrollManager(scrollManager)
60+
log("[Go] Global scroll manager set")
61+
5262
// Store scroll manager for cleanup (not implemented yet)
5363
_ = scrollManager
5464
})

examples/webgpu-chart/scroll.go

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,9 @@ type ChartDataManager struct {
3939

4040
// NewChartDataManager creates a new chart data manager
4141
func NewChartDataManager(initialData []chart.OHLCV, symbol, interval string) *ChartDataManager {
42-
visibleSize := len(initialData)
43-
if visibleSize > 200 {
44-
visibleSize = 200 // Show 200 candles at a time
42+
visibleSize := 100 // Show 100 candles at a time (as requested)
43+
if len(initialData) < visibleSize {
44+
visibleSize = len(initialData)
4545
}
4646

4747
// Initialize Markov chain generator from last candle
@@ -57,7 +57,7 @@ func NewChartDataManager(initialData []chart.OHLCV, symbol, interval string) *Ch
5757
allData: initialData,
5858
visibleStart: 0,
5959
visibleEnd: visibleSize,
60-
prefetchSize: 100, // Prefetch 100 candles when nearing edge
60+
prefetchSize: 50, // Prefetch 50 candles when nearing edge
6161
totalFetched: len(initialData),
6262
symbol: symbol,
6363
interval: interval,
@@ -269,7 +269,7 @@ func (sm *ScrollManager) attachEventListeners() {
269269
shift = 10 // Scroll right (newer data)
270270
case "Home":
271271
sm.chartData.visibleStart = 0
272-
sm.chartData.visibleEnd = 200
272+
sm.chartData.visibleEnd = 100
273273
sm.updateChart()
274274
return nil
275275
case "End":
@@ -297,11 +297,11 @@ func (sm *ScrollManager) attachEventListeners() {
297297

298298
// updateChart triggers a chart rerender with updated data
299299
func (sm *ScrollManager) updateChart() {
300-
// This is a placeholder - in the actual implementation,
301-
// we need to trigger the chart component to rerender
302-
// For now, we'll just log the viewport change
303300
log("[Scroll] Viewport updated:", sm.chartData.visibleStart, "-", sm.chartData.visibleEnd, "of", len(sm.chartData.allData))
304301

302+
// Trigger the app to rerender with new visible data
303+
TriggerChartUpdate()
304+
305305
// Check if we need to fetch/generate more data
306306
if sm.chartData.visibleEnd > len(sm.chartData.allData)-sm.chartData.prefetchSize {
307307
log("[Scroll] Near end, fetching/generating more data...")

examples/webgpu-chart/state.go

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
//go:build js && wasm
2+
3+
package main
4+
5+
import "github.com/gaarutyunov/guix/pkg/runtime/chart"
6+
7+
// Global state for chart data management
8+
var (
9+
globalScrollManager *ScrollManager
10+
globalApp *App
11+
)
12+
13+
// GetVisibleChartData returns the currently visible chart data
14+
// This is called by the app during rendering to get only the visible window of data
15+
func GetVisibleChartData() []chart.OHLCV {
16+
if globalScrollManager != nil && globalScrollManager.chartData != nil {
17+
return globalScrollManager.chartData.GetVisibleData()
18+
}
19+
// Fallback: return initial data if scroll manager not initialized
20+
return GetChartData()
21+
}
22+
23+
// SetGlobalScrollManager sets the global scroll manager instance
24+
func SetGlobalScrollManager(sm *ScrollManager) {
25+
globalScrollManager = sm
26+
}
27+
28+
// SetGlobalApp sets the global app instance
29+
func SetGlobalApp(app *App) {
30+
globalApp = app
31+
}
32+
33+
// TriggerChartUpdate triggers the app to rerender with updated data
34+
func TriggerChartUpdate() {
35+
if globalApp != nil {
36+
globalApp.Update()
37+
}
38+
}

0 commit comments

Comments
 (0)