@lightningjs/blits 1.29.5 → 1.30.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +16 -0
- package/docs/_sidebar.md +2 -0
- package/docs/performance/lazy-loading.md +161 -0
- package/docs/sidebar.json +18 -0
- package/package.json +2 -2
- package/src/announcer/announcer.js +1 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,21 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
_25 jun 2025_
|
|
4
|
+
|
|
5
|
+
## v1.30.1
|
|
6
|
+
|
|
7
|
+
- Bumped renderer to v2.14.1
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
_24 jun 2025_
|
|
11
|
+
|
|
12
|
+
## v1.30.0
|
|
13
|
+
|
|
14
|
+
- Added documentation on lazy loading performance
|
|
15
|
+
- Bumped renderer to v2.14.0 (simplified texture throttling)
|
|
16
|
+
- Fixed bug in announcer when utterance is cancelled
|
|
17
|
+
|
|
18
|
+
|
|
3
19
|
_12 jun 2025_
|
|
4
20
|
|
|
5
21
|
## v1.29.5
|
package/docs/_sidebar.md
CHANGED
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
# Lazy Loading with the :range Parameter
|
|
2
|
+
|
|
3
|
+
The for-loop in Blits comes with a [`:range` parameter](../built-in/for-loop.html#using-the-range-attribute) that allows you to render only a portion of an array rather than all the items.
|
|
4
|
+
|
|
5
|
+
This `:range` parameter can also be used to easily implement an efficient form of _lazy loading_. Lazy loading is a crucial concept for performance in TV applications where rails can contain hundreds of items, but only a small subset is visible at any time.
|
|
6
|
+
|
|
7
|
+
## Basic range implementation
|
|
8
|
+
|
|
9
|
+
For the basic syntax of the `:range` attribute, we typically set dynamic `from` and `to` values, which define the subset of items to display.
|
|
10
|
+
|
|
11
|
+
```js
|
|
12
|
+
<Tile :for="(item, index) in $items" :range="{from: $from, to: $to}" />
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
A full example of a Rail component with a range would look something like this:
|
|
16
|
+
|
|
17
|
+
```js
|
|
18
|
+
export default Blits.Component('RangedRail', {
|
|
19
|
+
template: `
|
|
20
|
+
<Element :x.transition="0 - $focused * 300">
|
|
21
|
+
<Tile
|
|
22
|
+
:for="(item, index) in $items"
|
|
23
|
+
:range="{from: $range, to: $range + 7}"
|
|
24
|
+
:key="$item.id"
|
|
25
|
+
:focused="$focused"
|
|
26
|
+
w="300"
|
|
27
|
+
h="200"
|
|
28
|
+
:x="$index * 320"
|
|
29
|
+
/>
|
|
30
|
+
</Element>
|
|
31
|
+
`,
|
|
32
|
+
state() {
|
|
33
|
+
return {
|
|
34
|
+
range: 0,
|
|
35
|
+
focused: 0,
|
|
36
|
+
items: [] // Array of 100+ items
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
input: {
|
|
40
|
+
right() {
|
|
41
|
+
this.focus = Math.min(this.focus + 1, items.length)
|
|
42
|
+
this.range++
|
|
43
|
+
},
|
|
44
|
+
left() {
|
|
45
|
+
this.focus = Math.max(this.focus - 1, 0)
|
|
46
|
+
this.range--
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
})
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Benefits of this approach are:
|
|
53
|
+
|
|
54
|
+
- **Selective Rendering**: Only items within the specified range are actually created as elements
|
|
55
|
+
- **Performance Optimization**: Reduces memory usage and improves rendering performance
|
|
56
|
+
- **Dynamic Range Updates**: The range can be computed based on user interaction and scroll position
|
|
57
|
+
|
|
58
|
+
A downside of this approach is that, as we scroll we are also **destroying** components that are out of range. And we may need those
|
|
59
|
+
same items when we scroll back (and thus recreate them). On top of that, destroying Components and Elements also comes at a cost.
|
|
60
|
+
|
|
61
|
+
## Lazy loading with the range attribute
|
|
62
|
+
|
|
63
|
+
We can solve the problem of destroying Components that we may want to use again by slightly changing the logic of the Rail component.
|
|
64
|
+
|
|
65
|
+
1) We set `from: 0`, to ensure we always start from the first item (and _keep_ the first items)
|
|
66
|
+
2) We use `to: $range + 7` to render up to the current position plus a lookahead buffer
|
|
67
|
+
3) The `$range` value tracks how far the user has navigated through the rail, but it doesn't decrement when navigating back
|
|
68
|
+
|
|
69
|
+
With these changes a full example of a LazyRail component would look something like this:
|
|
70
|
+
|
|
71
|
+
```js
|
|
72
|
+
export default Blits.Component('LazyRail', {
|
|
73
|
+
template: `
|
|
74
|
+
<Element :x.transition="0 - $focused * 300">
|
|
75
|
+
<Tile
|
|
76
|
+
:for="(item, index) in $items"
|
|
77
|
+
:range="{from: 0, to: $range + 7}"
|
|
78
|
+
:key="$item.id"
|
|
79
|
+
w="300"
|
|
80
|
+
h="200"
|
|
81
|
+
:x="$index * 320"
|
|
82
|
+
/>
|
|
83
|
+
</Element>
|
|
84
|
+
`,
|
|
85
|
+
state() {
|
|
86
|
+
return {
|
|
87
|
+
range: 0,
|
|
88
|
+
items: [] // Array of 100+ items
|
|
89
|
+
}
|
|
90
|
+
},
|
|
91
|
+
input: {
|
|
92
|
+
right() {
|
|
93
|
+
this.focus = Math.min(this.focus + 1, items.length)
|
|
94
|
+
// push the range update to the next tick to spread out the work
|
|
95
|
+
// and ease the CPU a bit
|
|
96
|
+
this.$setTimeout(() => this.range = this.focus)
|
|
97
|
+
|
|
98
|
+
},
|
|
99
|
+
left() {
|
|
100
|
+
this.focus = Math.max(this.focus - 1, 0)
|
|
101
|
+
// we don't decrement the range when scrolling back to keep components alive
|
|
102
|
+
// this.range--
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
})
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
## Performance Benefits
|
|
109
|
+
|
|
110
|
+
Properly applying the range attribute provides **Memory Efficiency**:
|
|
111
|
+
|
|
112
|
+
- Instead of creating 100+ elements, only ~8-15 are active
|
|
113
|
+
- Reduces GPU memory usage on constrained TV hardware
|
|
114
|
+
- Lower overall memory footprint
|
|
115
|
+
|
|
116
|
+
It also is beneficial for **Faster Rendering**
|
|
117
|
+
|
|
118
|
+
- Initial page load only renders visible items
|
|
119
|
+
- Scroll performance improves as fewer elements need updates
|
|
120
|
+
- Reduced layout calculations
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
It **optimizes bandwidth**
|
|
124
|
+
|
|
125
|
+
- Images and assets only load for rendered items
|
|
126
|
+
- Reduces network requests on page load
|
|
127
|
+
- Progressive content loading
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
### Alpha Transitions
|
|
131
|
+
|
|
132
|
+
For a smoother experience you consider applying an alpha transition, that fades out items at the end of the rang.e
|
|
133
|
+
|
|
134
|
+
```js
|
|
135
|
+
:alpha.transition="$index <= $range ? 1 : ($index <= $range + 3 ? 0.8 : 0.6)"
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
This creates a dimming effect for previously viewed items while maintaining full opacity for the current and upcoming items.
|
|
139
|
+
|
|
140
|
+
## Best Practices
|
|
141
|
+
|
|
142
|
+
### Buffer Size
|
|
143
|
+
|
|
144
|
+
Use a buffer of 5-10 items ahead of current position:
|
|
145
|
+
|
|
146
|
+
```js
|
|
147
|
+
// Conservative buffer for low-end devices
|
|
148
|
+
:range="{from: $currentIndex - 1, to: $currentIndex + 6}"
|
|
149
|
+
|
|
150
|
+
// Larger buffer for smoother experience
|
|
151
|
+
:range="{from: $currentIndex - 2, to: $currentIndex + 10}"
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
## Performance Impact
|
|
155
|
+
|
|
156
|
+
On typical TV hardware:
|
|
157
|
+
|
|
158
|
+
- **Memory Usage**: 70-80% reduction compared to full rendering
|
|
159
|
+
- **Initial Load Time**: 3-5x faster page initialization
|
|
160
|
+
- **Scroll Performance**: Consistent fps maintained vs potential frame drops
|
|
161
|
+
- **Network Requests**: Reduced by 80-90% on initial load
|
package/docs/sidebar.json
CHANGED
|
@@ -112,6 +112,15 @@
|
|
|
112
112
|
}
|
|
113
113
|
]
|
|
114
114
|
},
|
|
115
|
+
{
|
|
116
|
+
"text": "Performance",
|
|
117
|
+
"items": [
|
|
118
|
+
{
|
|
119
|
+
"text": "Lazy Loading",
|
|
120
|
+
"link": "/performance/lazy-loading"
|
|
121
|
+
}
|
|
122
|
+
]
|
|
123
|
+
},
|
|
115
124
|
{
|
|
116
125
|
"text": "Router",
|
|
117
126
|
"items": [
|
|
@@ -149,5 +158,14 @@
|
|
|
149
158
|
"link": "/plugins/global_app_state"
|
|
150
159
|
}
|
|
151
160
|
]
|
|
161
|
+
},
|
|
162
|
+
{
|
|
163
|
+
"text": "Performance",
|
|
164
|
+
"items": [
|
|
165
|
+
{
|
|
166
|
+
"text": "Lazy loading",
|
|
167
|
+
"link": "/performance/lazy-loading"
|
|
168
|
+
}
|
|
169
|
+
]
|
|
152
170
|
}
|
|
153
171
|
]
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lightningjs/blits",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.30.1",
|
|
4
4
|
"description": "Blits: The Lightning 3 App Development Framework",
|
|
5
5
|
"bin": "bin/index.js",
|
|
6
6
|
"exports": {
|
|
@@ -47,7 +47,7 @@
|
|
|
47
47
|
},
|
|
48
48
|
"dependencies": {
|
|
49
49
|
"@lightningjs/msdf-generator": "^1.1.1",
|
|
50
|
-
"@lightningjs/renderer": "^2.
|
|
50
|
+
"@lightningjs/renderer": "^2.14.1"
|
|
51
51
|
},
|
|
52
52
|
"repository": {
|
|
53
53
|
"type": "git",
|