@lightningjs/blits 2.4.0 → 2.5.0
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 +15 -0
- package/index.d.ts +20 -8
- package/package.json +6 -2
- package/src/component.js +7 -0
- package/src/components/Circle.js +3 -0
- package/src/components/FPScounter.js +206 -1330
- package/src/components/RouterView.js +3 -0
- package/src/components/index.js +0 -2
- package/src/engines/L3/element.js +134 -9
- package/src/engines/L3/spriteTexture.js +71 -0
- package/src/focus/focus.js +3 -1
- package/src/lib/codegenerator/generator.js +12 -2
- package/src/lib/symbols.js +4 -0
- package/src/platforms.d.ts +1 -0
- package/src/platforms.js +23 -0
- package/src/router/router.js +9 -10
- package/src/components/Sprite.js +0 -256
package/src/components/index.js
CHANGED
|
@@ -29,9 +29,14 @@ import { Log } from '../../lib/log.js'
|
|
|
29
29
|
import symbols from '../../lib/symbols.js'
|
|
30
30
|
import Settings from '../../settings.js'
|
|
31
31
|
import shaders from '../../lib/shaders/shaders.js'
|
|
32
|
+
import { resolveSpriteTexture } from './spriteTexture.js'
|
|
32
33
|
|
|
33
34
|
const holderComponentMap = new WeakMap()
|
|
34
35
|
|
|
36
|
+
/** @param {any} frame */
|
|
37
|
+
const spriteFrameKey = (frame) =>
|
|
38
|
+
typeof frame === 'object' && frame !== null ? JSON.stringify(frame) : frame
|
|
39
|
+
|
|
35
40
|
/**
|
|
36
41
|
* Creates a padding object from a value and direction.
|
|
37
42
|
* @param {number|object|string|undefined} padding - The padding value.
|
|
@@ -291,6 +296,24 @@ const propsTransformer = {
|
|
|
291
296
|
this.props['color'] = 0xffffffff
|
|
292
297
|
}
|
|
293
298
|
},
|
|
299
|
+
set image(v) {
|
|
300
|
+
this.raw['image'] = v
|
|
301
|
+
if (this.element[symbols.isSprite] === true && this.element.node) {
|
|
302
|
+
this.element._scheduleNativeSpriteSync()
|
|
303
|
+
}
|
|
304
|
+
},
|
|
305
|
+
set map(v) {
|
|
306
|
+
this.raw['map'] = v
|
|
307
|
+
if (this.element[symbols.isSprite] === true && this.element.node) {
|
|
308
|
+
this.element._scheduleNativeSpriteSync()
|
|
309
|
+
}
|
|
310
|
+
},
|
|
311
|
+
set frame(v) {
|
|
312
|
+
this.raw['frame'] = v
|
|
313
|
+
if (this.element[symbols.isSprite] === true && this.element.node) {
|
|
314
|
+
this.element._scheduleNativeSpriteSync()
|
|
315
|
+
}
|
|
316
|
+
},
|
|
294
317
|
set fit(v) {
|
|
295
318
|
const resizeMode = {}
|
|
296
319
|
|
|
@@ -548,6 +571,7 @@ export const elementAttributes = Object.keys(propsTransformer)
|
|
|
548
571
|
const Element = {
|
|
549
572
|
/**
|
|
550
573
|
* Populates the element with data
|
|
574
|
+
* @this {import('../../component.js').BlitsElement}
|
|
551
575
|
* @param {import('../../component.js').BlitsElementProps} props
|
|
552
576
|
*/
|
|
553
577
|
populate(props) {
|
|
@@ -557,6 +581,21 @@ const Element = {
|
|
|
557
581
|
this[symbols.isSlot] = true
|
|
558
582
|
}
|
|
559
583
|
|
|
584
|
+
if (props[symbols.isSprite] === true) {
|
|
585
|
+
this[symbols.isSprite] = true
|
|
586
|
+
this._spriteState = {
|
|
587
|
+
spriteTexture: null,
|
|
588
|
+
currentSrc: null,
|
|
589
|
+
_loadedCb: null,
|
|
590
|
+
_failedCb: null,
|
|
591
|
+
_syncScheduled: false,
|
|
592
|
+
lastTexture: null,
|
|
593
|
+
lastImage: null,
|
|
594
|
+
lastMap: null,
|
|
595
|
+
lastFrameKey: undefined,
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
|
|
560
599
|
this.props.element = this
|
|
561
600
|
|
|
562
601
|
this.props['parent'] = props['parent'] || this.config.parent
|
|
@@ -600,16 +639,20 @@ const Element = {
|
|
|
600
639
|
holderComponentMap.set(this.node, this.component)
|
|
601
640
|
}
|
|
602
641
|
|
|
603
|
-
if (
|
|
604
|
-
this.
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
642
|
+
if (this[symbols.isSprite] === true) {
|
|
643
|
+
this._syncNativeSprite()
|
|
644
|
+
} else {
|
|
645
|
+
if (props['@loaded'] !== undefined && typeof props['@loaded'] === 'function') {
|
|
646
|
+
this.node.on('loaded', (el, { type, dimensions }) => {
|
|
647
|
+
props['@loaded']({ w: dimensions.w, h: dimensions.h, type }, this)
|
|
648
|
+
})
|
|
649
|
+
}
|
|
608
650
|
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
651
|
+
if (props['@error'] !== undefined && typeof props['@error'] === 'function') {
|
|
652
|
+
this.node.on('failed', (el, error) => {
|
|
653
|
+
props['@error'](error, this)
|
|
654
|
+
})
|
|
655
|
+
}
|
|
613
656
|
}
|
|
614
657
|
|
|
615
658
|
if (props.__layout === true) {
|
|
@@ -657,6 +700,78 @@ const Element = {
|
|
|
657
700
|
this.node.data = { ...this.props.props['data'] }
|
|
658
701
|
}
|
|
659
702
|
},
|
|
703
|
+
/**
|
|
704
|
+
* @this {import('../../component.js').BlitsElement}
|
|
705
|
+
*/
|
|
706
|
+
_scheduleNativeSpriteSync() {
|
|
707
|
+
if (this[symbols.isSprite] !== true || !this.node || this.eol === true) return
|
|
708
|
+
const st = this._spriteState
|
|
709
|
+
if (st === undefined) return
|
|
710
|
+
if (st._syncScheduled === true) return
|
|
711
|
+
st._syncScheduled = true
|
|
712
|
+
queueMicrotask(() => {
|
|
713
|
+
st._syncScheduled = false
|
|
714
|
+
if (this.eol === true || !this.node) return
|
|
715
|
+
this._syncNativeSprite()
|
|
716
|
+
})
|
|
717
|
+
},
|
|
718
|
+
/**
|
|
719
|
+
* @this {import('../../component.js').BlitsElement}
|
|
720
|
+
*/
|
|
721
|
+
_syncNativeSprite() {
|
|
722
|
+
if (this[symbols.isSprite] !== true || !this.node) return
|
|
723
|
+
const st = this._spriteState
|
|
724
|
+
if (st === undefined) return
|
|
725
|
+
|
|
726
|
+
const raw = this.props.raw
|
|
727
|
+
const image = raw['image']
|
|
728
|
+
const map = raw['map']
|
|
729
|
+
const frameKey = spriteFrameKey(raw['frame'])
|
|
730
|
+
|
|
731
|
+
if (
|
|
732
|
+
st.lastTexture != null &&
|
|
733
|
+
image === st.lastImage &&
|
|
734
|
+
map === st.lastMap &&
|
|
735
|
+
frameKey === st.lastFrameKey
|
|
736
|
+
) {
|
|
737
|
+
return
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
const imageChanged = image !== st.lastImage
|
|
741
|
+
st.lastImage = image
|
|
742
|
+
st.lastMap = map
|
|
743
|
+
st.lastFrameKey = frameKey
|
|
744
|
+
|
|
745
|
+
const texture = resolveSpriteTexture(this, renderer, st)
|
|
746
|
+
if (texture === st.lastTexture) return
|
|
747
|
+
|
|
748
|
+
st.lastTexture = texture
|
|
749
|
+
this.set('texture', texture)
|
|
750
|
+
|
|
751
|
+
if (imageChanged !== true) return
|
|
752
|
+
|
|
753
|
+
const prevL = st._loadedCb
|
|
754
|
+
const prevF = st._failedCb
|
|
755
|
+
st._loadedCb =
|
|
756
|
+
raw['@loaded'] && typeof raw['@loaded'] === 'function'
|
|
757
|
+
? (payload) => {
|
|
758
|
+
const d = payload?.dimensions
|
|
759
|
+
const w = payload?.w ?? payload?.width ?? d?.width ?? d?.w
|
|
760
|
+
const h = payload?.h ?? payload?.height ?? d?.height ?? d?.h
|
|
761
|
+
raw['@loaded']({ w, h }, this)
|
|
762
|
+
}
|
|
763
|
+
: null
|
|
764
|
+
st._failedCb =
|
|
765
|
+
raw['@error'] && typeof raw['@error'] === 'function'
|
|
766
|
+
? (payload) => raw['@error'](payload, this)
|
|
767
|
+
: null
|
|
768
|
+
if (st.spriteTexture) {
|
|
769
|
+
if (prevL) st.spriteTexture.off('loaded', prevL)
|
|
770
|
+
if (prevF) st.spriteTexture.off('failed', prevF)
|
|
771
|
+
if (st._loadedCb) st.spriteTexture.on('loaded', st._loadedCb)
|
|
772
|
+
if (st._failedCb) st.spriteTexture.on('failed', st._failedCb)
|
|
773
|
+
}
|
|
774
|
+
},
|
|
660
775
|
/**
|
|
661
776
|
* Set an individual property on the node
|
|
662
777
|
*
|
|
@@ -823,6 +938,16 @@ const Element = {
|
|
|
823
938
|
|
|
824
939
|
Log.debug('Deleting Node', this.nodeId)
|
|
825
940
|
|
|
941
|
+
if (this[symbols.isSprite] === true && this._spriteState) {
|
|
942
|
+
const st = this._spriteState
|
|
943
|
+
if (st.spriteTexture) {
|
|
944
|
+
if (st._loadedCb) st.spriteTexture.off('loaded', st._loadedCb)
|
|
945
|
+
if (st._failedCb) st.spriteTexture.off('failed', st._failedCb)
|
|
946
|
+
}
|
|
947
|
+
if (st.subTextures) st.subTextures.clear()
|
|
948
|
+
this._spriteState = null
|
|
949
|
+
}
|
|
950
|
+
|
|
826
951
|
// Clear all pending debounce timeouts
|
|
827
952
|
const debounceProps = Object.keys(this.debounceTimeouts)
|
|
828
953
|
for (let i = 0; i < debounceProps.length; i++) {
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolves a sprite sheet frame from map + frame (template Sprite element path on L3).
|
|
3
|
+
* @param {import('../../component.js').BlitsElement} element
|
|
4
|
+
* @param {object} r - renderer
|
|
5
|
+
* @param {any} state - mutated sprite cache (see L3 element _spriteState)
|
|
6
|
+
* @returns {any|null} texture for the node
|
|
7
|
+
*/
|
|
8
|
+
export function resolveSpriteTexture(element, r, state) {
|
|
9
|
+
const image = element.props.raw['image']
|
|
10
|
+
if (image === undefined || image === null) {
|
|
11
|
+
return null
|
|
12
|
+
}
|
|
13
|
+
if (r === null || r === undefined || r.createTexture === undefined) {
|
|
14
|
+
return null
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
if (
|
|
18
|
+
state.spriteTexture === undefined ||
|
|
19
|
+
state.spriteTexture === null ||
|
|
20
|
+
state.currentSrc !== image
|
|
21
|
+
) {
|
|
22
|
+
state.spriteTexture = r.createTexture('ImageTexture', {
|
|
23
|
+
src: image,
|
|
24
|
+
})
|
|
25
|
+
state.currentSrc = image
|
|
26
|
+
state.subTextures = null
|
|
27
|
+
state.currentMap = null
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const map = element.props.raw['map']
|
|
31
|
+
|
|
32
|
+
if (state.currentMap !== map) {
|
|
33
|
+
state.currentMap = map
|
|
34
|
+
state.subTextures = null
|
|
35
|
+
}
|
|
36
|
+
const frame = element.props.raw['frame']
|
|
37
|
+
|
|
38
|
+
let options = null
|
|
39
|
+
if (map != null && frame != null) {
|
|
40
|
+
if (map.frames != null && map.frames !== undefined && frame in map.frames) {
|
|
41
|
+
options = Object.assign({}, map.defaults || {}, map.frames[frame])
|
|
42
|
+
} else if (frame in map) {
|
|
43
|
+
options = map[frame]
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if ((options === null || options === undefined) && typeof frame === 'object' && frame !== null) {
|
|
48
|
+
options = frame
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (options != null) {
|
|
52
|
+
const key = typeof frame === 'object' ? JSON.stringify(frame) : String(frame)
|
|
53
|
+
if (!state.subTextures) {
|
|
54
|
+
state.subTextures = new Map()
|
|
55
|
+
}
|
|
56
|
+
const cached = state.subTextures.get(key)
|
|
57
|
+
if (cached !== undefined) {
|
|
58
|
+
return cached
|
|
59
|
+
}
|
|
60
|
+
const subTexture = r.createTexture('SubTexture', {
|
|
61
|
+
texture: state.spriteTexture,
|
|
62
|
+
x: options.x,
|
|
63
|
+
y: options.y,
|
|
64
|
+
w: options.w,
|
|
65
|
+
h: options.h,
|
|
66
|
+
})
|
|
67
|
+
state.subTextures.set(key, subTexture)
|
|
68
|
+
return subTexture
|
|
69
|
+
}
|
|
70
|
+
return state.spriteTexture
|
|
71
|
+
}
|
package/src/focus/focus.js
CHANGED
|
@@ -41,6 +41,8 @@ export default {
|
|
|
41
41
|
return focusedComponent
|
|
42
42
|
},
|
|
43
43
|
set(component, event) {
|
|
44
|
+
if (component === undefined || component.eol === true) return
|
|
45
|
+
|
|
44
46
|
clearTimeout(setFocusTimeout)
|
|
45
47
|
|
|
46
48
|
// early return if already focused
|
|
@@ -75,7 +77,7 @@ export default {
|
|
|
75
77
|
|
|
76
78
|
// and finally set focus to the leaf component
|
|
77
79
|
setFocusTimeout = setTimeout(
|
|
78
|
-
() => setFocus(component, event),
|
|
80
|
+
() => component.eol !== true && setFocus(component, event),
|
|
79
81
|
this.hold === true ? Settings.get('holdTimeout', DEFAULT_HOLD_TIMEOUT_MS) : 0
|
|
80
82
|
)
|
|
81
83
|
},
|
|
@@ -206,6 +206,10 @@ const generateElementCode = function (
|
|
|
206
206
|
renderCode.push(`elementConfigs[${counter}][Symbol.for('isSlot')] = true`)
|
|
207
207
|
}
|
|
208
208
|
|
|
209
|
+
if (templateObject[Symbol.for('componentType')] === 'Sprite') {
|
|
210
|
+
renderCode.push(`elementConfigs[${counter}][Symbol.for('isSprite')] = true`)
|
|
211
|
+
}
|
|
212
|
+
|
|
209
213
|
if (options.holder) {
|
|
210
214
|
renderCode.push(`elementConfigs[${counter}]['holder'] = true`)
|
|
211
215
|
}
|
|
@@ -611,11 +615,16 @@ const generateForLoopCode = function (templateObject, parent) {
|
|
|
611
615
|
if (
|
|
612
616
|
templateObject[Symbol.for('componentType')] === 'Element' ||
|
|
613
617
|
templateObject[Symbol.for('componentType')] === 'Slot' ||
|
|
614
|
-
templateObject[Symbol.for('componentType')] === 'Text'
|
|
618
|
+
templateObject[Symbol.for('componentType')] === 'Text' ||
|
|
619
|
+
templateObject[Symbol.for('componentType')] === 'Sprite' ||
|
|
620
|
+
templateObject[Symbol.for('componentType')] === 'Layout'
|
|
615
621
|
) {
|
|
616
622
|
if (templateObject[Symbol.for('componentType')] === 'Text') {
|
|
617
623
|
templateObject.__textnode = 'true'
|
|
618
624
|
}
|
|
625
|
+
if (templateObject[Symbol.for('componentType')] === 'Layout') {
|
|
626
|
+
templateObject.__layout = 'true'
|
|
627
|
+
}
|
|
619
628
|
generateElementCode.call(ctx, templateObject, parent, {
|
|
620
629
|
key: 'scope.key',
|
|
621
630
|
component: 'scope.',
|
|
@@ -788,7 +797,8 @@ const generateCode = function (templateObject, parent = false, options = {}) {
|
|
|
788
797
|
childTemplateObject[Symbol.for('componentType')] === 'Element' ||
|
|
789
798
|
childTemplateObject[Symbol.for('componentType')] === 'Slot' ||
|
|
790
799
|
childTemplateObject[Symbol.for('componentType')] === 'Text' ||
|
|
791
|
-
childTemplateObject[Symbol.for('componentType')] === 'Layout'
|
|
800
|
+
childTemplateObject[Symbol.for('componentType')] === 'Layout' ||
|
|
801
|
+
childTemplateObject[Symbol.for('componentType')] === 'Sprite'
|
|
792
802
|
) {
|
|
793
803
|
if (childTemplateObject[Symbol.for('componentType')] === 'Text') {
|
|
794
804
|
childTemplateObject.__textnode = 'true'
|
package/src/lib/symbols.js
CHANGED
|
@@ -61,6 +61,7 @@
|
|
|
61
61
|
* @property {symbol} components
|
|
62
62
|
* @property {symbol} config
|
|
63
63
|
* @property {symbol} isSlot
|
|
64
|
+
* @property {symbol} isSprite
|
|
64
65
|
* @property {symbol} props
|
|
65
66
|
* @property {symbol} parent
|
|
66
67
|
* @property {symbol} slots
|
|
@@ -68,6 +69,7 @@
|
|
|
68
69
|
* @property {symbol} isComponent
|
|
69
70
|
* @property {symbol} effects
|
|
70
71
|
* @property {symbol} removeGlobalEffects
|
|
72
|
+
* @property {symbol} tagContent
|
|
71
73
|
*/
|
|
72
74
|
|
|
73
75
|
/**
|
|
@@ -126,6 +128,8 @@ export default {
|
|
|
126
128
|
config: Symbol.for('config'),
|
|
127
129
|
// Symbol 'isSlot' utilized within generated code
|
|
128
130
|
isSlot: Symbol.for('isSlot'),
|
|
131
|
+
// Symbol 'isSprite' utilized within generated code (native Sprite on L3 element)
|
|
132
|
+
isSprite: Symbol.for('isSprite'),
|
|
129
133
|
// Symbol 'props' utilized within generated code
|
|
130
134
|
props: Symbol.for('props'),
|
|
131
135
|
// Symbol 'slots' utilized within generated code
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { WebPlatform, WebPlatformNext, WebPlatformChrome50, WebPlatformLegacy } from '@lightningjs/renderer/platforms'
|
package/src/platforms.js
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2023 Comcast Cable Communications Management, LLC
|
|
3
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
* you may not use this file except in compliance with the License.
|
|
5
|
+
* You may obtain a copy of the License at
|
|
6
|
+
*
|
|
7
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
*
|
|
9
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
* See the License for the specific language governing permissions and
|
|
13
|
+
* limitations under the License.
|
|
14
|
+
*
|
|
15
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
export {
|
|
19
|
+
WebPlatform,
|
|
20
|
+
WebPlatformNext,
|
|
21
|
+
WebPlatformChrome50,
|
|
22
|
+
WebPlatformLegacy,
|
|
23
|
+
} from '@lightningjs/renderer/platforms'
|
package/src/router/router.js
CHANGED
|
@@ -408,33 +408,32 @@ const executeBeforeHook = async function (
|
|
|
408
408
|
try {
|
|
409
409
|
result = await hooks[hookName].call(parent, route, previousRoute)
|
|
410
410
|
if (isString(result)) {
|
|
411
|
-
currentRoute = previousRoute
|
|
412
|
-
to(result)
|
|
411
|
+
this.currentRoute = previousRoute
|
|
412
|
+
to(result, {}, {}, this.name)
|
|
413
413
|
return false
|
|
414
414
|
}
|
|
415
415
|
} catch (error) {
|
|
416
416
|
Log.error(`Error or Rejected Promise in "${hookName}" Hook`, error)
|
|
417
|
+
this.currentRoute = previousRoute
|
|
417
418
|
if (this.history.length > 0) {
|
|
418
419
|
preventHashChangeNavigation = true
|
|
419
|
-
currentRoute = previousRoute
|
|
420
|
-
/// hmmmm
|
|
421
420
|
window.history.back()
|
|
422
421
|
navigatingBack = false
|
|
423
422
|
state.navigating = false
|
|
424
|
-
return false
|
|
425
423
|
}
|
|
424
|
+
return false
|
|
426
425
|
}
|
|
427
426
|
// If the resolved result is an object, redirect if the path in the object was changed
|
|
428
427
|
if (isObject(result) === true && result.path !== currentPath) {
|
|
429
|
-
currentRoute = previousRoute
|
|
430
|
-
to(result.path, result.data, result.options)
|
|
428
|
+
this.currentRoute = previousRoute
|
|
429
|
+
to(result.path, result.data, result.options, this.name)
|
|
431
430
|
return false
|
|
432
431
|
}
|
|
433
432
|
// If the resolved result is false, cancel navigation
|
|
434
433
|
if (result === false) {
|
|
434
|
+
this.currentRoute = previousRoute
|
|
435
435
|
if (this.history.length > 0) {
|
|
436
436
|
preventHashChangeNavigation = true
|
|
437
|
-
currentRoute = previousRoute
|
|
438
437
|
window.history.back()
|
|
439
438
|
navigatingBack = false
|
|
440
439
|
state.navigating = false
|
|
@@ -491,11 +490,11 @@ const executeTransition = async (transition, element, animate) => {
|
|
|
491
490
|
}
|
|
492
491
|
}
|
|
493
492
|
|
|
494
|
-
export const to = (path, data = {}, options = {}) => {
|
|
493
|
+
export const to = (path, data = {}, options = {}, routerViewName = '') => {
|
|
495
494
|
navigationData = data
|
|
496
495
|
overrideOptions = options
|
|
497
496
|
|
|
498
|
-
setHash(path)
|
|
497
|
+
setHash(path, routerViewName)
|
|
499
498
|
}
|
|
500
499
|
|
|
501
500
|
export const toRouterView = (routerView, path, data = {}, options = {}) => {
|