@lightningjs/blits 0.10.0 → 1.0.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.
Files changed (38) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/docs/essentials/displaying_text.md +1 -1
  3. package/package.json +3 -3
  4. package/src/component/base/events.js +1 -1
  5. package/src/component/base/utils.js +34 -1
  6. package/src/component/setup/props.js +3 -1
  7. package/src/component/setup/props.test.js +51 -19
  8. package/src/component.d.ts +2 -1
  9. package/src/component.js +20 -13
  10. package/src/components/Circle.js +13 -12
  11. package/src/components/FPScounter.js +388 -536
  12. package/src/components/Image.js +7 -9
  13. package/src/components/Layout.js +49 -0
  14. package/src/components/Layout.orig.js +34 -0
  15. package/src/components/RouterView.js +6 -10
  16. package/src/components/Sprite.js +10 -12
  17. package/src/components/index.js +2 -2
  18. package/src/components/index.orig.js +2 -2
  19. package/src/engine.js +1 -1
  20. package/src/engines/L3/element.js +193 -180
  21. package/src/engines/L3/element.test.js +170 -0
  22. package/src/engines/L3/fontLoader.js +4 -5
  23. package/src/engines/L3/index.js +2 -2
  24. package/src/engines/L3/launch.js +7 -13
  25. package/src/engines/L3/shaderLoader.js +12 -0
  26. package/src/engines/index.js +1 -1
  27. package/src/launch.d.ts +19 -2
  28. package/src/lib/codegenerator/generator.js +212 -84
  29. package/src/lib/codegenerator/generator.test.js +657 -942
  30. package/src/lib/colors/colors.js +59 -46
  31. package/src/lib/precompiler/precompiler.js +2 -1
  32. package/src/lib/reactivity/effect.js +10 -13
  33. package/src/lib/reactivity/reactive.js +24 -14
  34. package/src/lib/symbols.js +1 -0
  35. package/src/router/router.js +17 -3
  36. package/boilerplate/js/default/src/renderer.js +0 -4
  37. package/src/components/Text.js +0 -119
  38. package/src/components/Text.orig.js +0 -81
@@ -20,24 +20,10 @@ import colors from '../../lib/colors/colors.js'
20
20
 
21
21
  import { Log } from '../../lib/log.js'
22
22
  import symbols from '../../lib/symbols.js'
23
-
24
- const deprecationMsg = `
25
- ----------------------------------------------------------------------------------------------------
26
- Deprecation notice:
27
- ----------------------------------------------------------------------------------------------------
28
-
29
- The property for defining a transition easing function has been renamed from "function" to "easing".
30
-
31
- Please update your code like the example below to keep your custom easing function.
32
-
33
- Before: <Element :y.transition=“{v: $y, function: ‘ease-in-out’}” />
34
-
35
- After: <Element :y.transition=“{v: $y, easing: ‘ease-in-out’}” />
36
-
37
- `
23
+ import Settings from '../../settings.js'
38
24
 
39
25
  const isTransition = (value) => {
40
- return typeof value === 'object' && 'transition' in value
26
+ return value !== null && typeof value === 'object' && 'transition' in value
41
27
  }
42
28
 
43
29
  const isObjectString = (str) => {
@@ -49,155 +35,156 @@ const parseToObject = (str) => {
49
35
  }
50
36
 
51
37
  const parsePercentage = function (v, base) {
52
- if (typeof v === 'string' && v.endsWith('%')) {
53
- return this.element.node._parent[base] * (parseFloat(v) / 100)
38
+ if (typeof v !== 'string') {
39
+ return v
40
+ } else if (v.indexOf('%') === v.length - 1) {
41
+ return (
42
+ this.element.parent &&
43
+ this.element.parent[base] &&
44
+ this.element.parent[base] * (parseFloat(v) / 100)
45
+ )
54
46
  }
55
47
  return v
56
48
  }
57
49
 
58
- const unpackTransition = (obj) => {
59
- if (typeof obj === 'object' && obj.constructor === Object) {
60
- if ('value' in obj) {
61
- return obj.value
62
- } else if ('transition' in obj) {
63
- return unpackTransition(obj.transition)
50
+ const unpackTransition = (v) => {
51
+ if (v === null) return v
52
+ if (typeof v === 'string') return v
53
+ else if (typeof v === 'number') return v
54
+ else if (typeof v === 'object' && v.constructor === Object) {
55
+ if ('value' in v) {
56
+ return v.value
57
+ } else if ('transition' in v) {
58
+ return unpackTransition(v.transition)
64
59
  }
65
60
  }
66
- return obj
61
+ return v
67
62
  }
68
63
 
69
- const Props = {
64
+ const colorMap = {
65
+ top: 'colorTop',
66
+ bottom: 'colorBottom',
67
+ left: 'colorLeft',
68
+ right: 'colorRight',
69
+ }
70
+
71
+ let textDefaults
72
+
73
+ const propsTransformer = {
70
74
  set parent(v) {
71
- this._props.parent = v === 'root' ? renderer.root : v.node
72
- this._set.add('parent')
75
+ this.props['parent'] = v === 'root' ? renderer.root : v.node
73
76
  },
74
77
  set rotation(v) {
75
- this._props.rotation = v * (Math.PI / 180)
76
- this._set.add('rotation')
78
+ this.props['rotation'] = v * (Math.PI / 180)
77
79
  },
78
80
  set w(v) {
79
- this._props.width = parsePercentage.call(this, v, 'width')
80
- this._set.add('width')
81
+ this.props['width'] = parsePercentage.call(this, v, 'width')
81
82
  },
82
83
  set width(v) {
83
- this._props.width = parsePercentage.call(this, v, 'width')
84
- this._set.add('width')
84
+ this.props['width'] = parsePercentage.call(this, v, 'width')
85
85
  },
86
86
  set h(v) {
87
- this._props.height = parsePercentage.call(this, v, 'height')
88
- this._set.add('height')
87
+ this.props['height'] = parsePercentage.call(this, v, 'height')
89
88
  },
90
89
  set height(v) {
91
- this._props.height = parsePercentage.call(this, v, 'height')
92
- this._set.add('height')
90
+ this.props['height'] = parsePercentage.call(this, v, 'height')
93
91
  },
94
92
  set x(v) {
95
- this._props.x = parsePercentage.call(this, v, 'width')
96
- this._set.add('x')
93
+ this.props['x'] = parsePercentage.call(this, v, 'width')
97
94
  },
98
95
  set y(v) {
99
- this._props.y = parsePercentage.call(this, v, 'height')
100
- this._set.add('y')
96
+ this.props['y'] = parsePercentage.call(this, v, 'height')
101
97
  },
102
98
  set z(v) {
103
- this._props.zIndex = v
104
- this._set.add('zIndex')
99
+ this.props['zIndex'] = v
105
100
  },
106
101
  set zIndex(v) {
107
- this._props.zIndex = v
108
- this._set.add('zIndex')
102
+ this.props['zIndex'] = v
109
103
  },
110
104
  set color(v) {
111
- if (typeof v === 'object' || (isObjectString(v) && (v = parseToObject(v)))) {
112
- const map = {
113
- top: 'colorTop',
114
- bottom: 'colorBottom',
115
- left: 'colorLeft',
116
- right: 'colorRight',
117
- }
118
- this._props.color = 0
105
+ if (typeof v === 'string' && v.indexOf('{') === -1) {
106
+ this.props['color'] = colors.normalize(v)
107
+ } else if (typeof v === 'object' || (isObjectString(v) && (v = parseToObject(v)))) {
108
+ this.props['color'] = 0
119
109
  Object.entries(v).forEach((color) => {
120
- this._props[map[color[0]]] = colors.normalize(color[1])
121
- this._set.add(map[color[0]])
110
+ this.props[colorMap[color[0]]] = colors.normalize(color[1])
122
111
  })
123
- } else {
124
- this._props.color = colors.normalize(v)
125
112
  }
126
- this._set.add('color')
127
113
  },
128
114
  set src(v) {
129
- this._props.src = v
130
- if (!this._set.has('color')) {
131
- this._props.color = 0xffffffff
115
+ this.props['src'] = v
116
+ if (this.raw.get('color') === undefined) {
117
+ this.props['color'] = 0xffffffff
132
118
  }
133
- this._set.add('src')
134
119
  },
135
120
  set texture(v) {
136
- this._props.texture = v
137
- if (!this._set.has('color')) {
138
- this._props.color = 0xffffffff
121
+ this.props['texture'] = v
122
+
123
+ if (this.raw.get('color') === undefined && (v === null || v === undefined)) {
124
+ this.props['color'] = 0x00000000
125
+ } else if (this.raw.get('color') === undefined) {
126
+ this.props['color'] = 0xffffffff
127
+ }
128
+ },
129
+ set rtt(v) {
130
+ this.props['rtt'] = v
131
+ if (v === true && this.raw.get('color') === undefined) {
132
+ this.props['color'] = 0xffffffff
139
133
  }
140
- this._set.add('texture')
141
134
  },
142
135
  set mount(v) {
143
136
  if (typeof v === 'object' || (isObjectString(v) && (v = parseToObject(v)))) {
144
137
  if ('x' in v) {
145
- this._props.mountX = v.x
146
- this._set.add('mountX')
138
+ this.props['mountX'] = v.x
147
139
  }
148
140
  if ('y' in v) {
149
- this._props.mountY = v.y
150
- this._set.add('mountY')
141
+ this.props['mountY'] = v.y
151
142
  }
152
143
  } else {
153
- this._props.mountX = this._props.mountY = v
154
- this._set.add('mountX')
155
- this._set.add('mountY')
144
+ this.props['mountX'] = v
145
+ this.props['mountY'] = v
156
146
  }
157
147
  },
158
148
  set pivot(v) {
159
149
  if (typeof v === 'object' || (isObjectString(v) && (v = parseToObject(v)))) {
160
150
  if ('x' in v) {
161
- this._set.add('pivotX')
162
- this._props.pivotX = v.x
151
+ this.props['pivotX'] = v.x
163
152
  }
164
153
  if ('y' in v) {
165
- this._set.add('pivotY')
166
- this._props.pivotY = v.y
154
+ this.props['pivotY'] = v.y
167
155
  }
168
156
  } else {
169
- this._props.pivotX = this._props.pivotY = v
170
- this._set.add('pivotX')
171
- this._set.add('pivotY')
157
+ this.props['pivotX'] = v
158
+ this.props['pivotY'] = v
172
159
  }
173
160
  },
174
161
  set scale(v) {
175
162
  if (typeof v === 'object' || (isObjectString(v) && (v = parseToObject(v)))) {
176
163
  if ('x' in v) {
177
- this._set.add('scaleX')
178
- this._props.scaleX = v.x
164
+ this.props['scaleX'] = v.x
179
165
  }
180
166
  if ('y' in v) {
181
- this._set.add('scaleX')
182
- this._props.scaleY = v.y
167
+ this.props['scaleY'] = v.y
183
168
  }
184
169
  } else {
185
- this._props.scale = v
186
- this._set.add('scale')
170
+ this.props['scale'] = v
187
171
  }
188
172
  },
189
173
  set show(v) {
190
- this._props.alpha = v ? 1 : 0
174
+ this.props['alpha'] = v ? 1 : 0
191
175
  },
192
176
  set alpha(v) {
193
- this._props.alpha = v
194
- this._set.add('alpha')
177
+ this.props['alpha'] = v
195
178
  },
196
- set text(v) {
197
- this._props.text = v.toString()
179
+ set shader(v) {
180
+ if (v !== null) {
181
+ this.props['shader'] = renderer.createShader(v.type, v.props)
182
+ } else {
183
+ this.props['shader'] = renderer.createShader('DefaultShader')
184
+ }
198
185
  },
199
186
  set effects(v) {
200
- this._props.shader = renderer.createShader('DynamicShader', {
187
+ this.props['shader'] = renderer.createShader('DynamicShader', {
201
188
  effects: v.map((eff) => {
202
189
  if (eff.props && eff.props.color) {
203
190
  eff.props.color = colors.normalize(eff.props.color)
@@ -205,92 +192,87 @@ const Props = {
205
192
  return eff
206
193
  }),
207
194
  })
208
- this._set.add('effects')
209
195
  },
210
196
  set clipping(v) {
211
- this._props.clipping = v
197
+ this.props['clipping'] = v
212
198
  },
213
199
  set overflow(v) {
214
- this._props.clipping = !!!v
200
+ this.props['clipping'] = !!!v
201
+ },
202
+ set font(v) {
203
+ this.props['fontFamily'] = v
215
204
  },
216
- set fontFamily(v) {
217
- this._props.fontFamily = v
205
+ set size(v) {
206
+ this.props['fontSize'] = v
218
207
  },
219
- set fontSize(v) {
220
- this._props.fontSize = v
208
+ set wordwrap(v) {
209
+ this.props['width'] = v
210
+ this.props['contain'] = 'width'
221
211
  },
222
- set wordWrap(v) {
223
- this._props.width = v
212
+ set maxheight(v) {
213
+ this.props['height'] = v
214
+ this.props['contain'] = 'both'
224
215
  },
225
216
  set contain(v) {
226
- this._props.contain = v
217
+ this.props['contain'] = v
227
218
  },
228
- set maxLines(v) {
229
- this._props.maxLines = v
219
+ set maxlines(v) {
220
+ this.props['maxLines'] = v
230
221
  },
231
- set overflowSuffix(v) {
232
- this._props.overflowSuffix = v === false ? ' ' : v === true ? undefined : v
222
+ set textoverflow(v) {
223
+ this.props['overflowSuffix'] = v === false ? ' ' : v === true ? undefined : v
233
224
  },
234
- set letterSpacing(v) {
235
- this._props.letterSpacing = v || 1
225
+ set letterspacing(v) {
226
+ this.props['letterSpacing'] = v || 1
236
227
  },
237
- set lineHeight(v) {
238
- this._props.lineHeight = v
228
+ set lineheight(v) {
229
+ this.props['lineHeight'] = v
239
230
  },
240
- set textAlign(v) {
241
- this._props.textAlign = v
231
+ set align(v) {
232
+ this.props['textAlign'] = v
242
233
  },
243
- set rtt(v) {
244
- this._props.rtt = v
245
- this._set.add('rtt')
234
+ set content(v) {
235
+ this.props['text'] = '' + v
246
236
  },
247
237
  }
248
238
 
249
239
  const Element = {
250
- defaults: {},
251
240
  populate(data) {
252
241
  const props = {
253
- ...this.defaults,
254
242
  ...this.config,
255
243
  ...data,
256
244
  }
257
245
 
258
- this.initData = data
259
-
260
246
  if (props[symbols.isSlot]) {
261
247
  this[symbols.isSlot] = true
262
248
  }
263
249
 
264
250
  this.props.element = this
265
251
 
266
- for (const [prop, value] of Object.entries(props)) {
267
- const descriptor = Object.getOwnPropertyDescriptor(Props, prop)
268
- if (descriptor && descriptor.set) {
269
- if (value !== undefined) {
270
- this.props[prop] = unpackTransition(value)
271
- }
252
+ const propKeys = Object.keys(props)
253
+ const length = propKeys.length
254
+
255
+ this.props['parent'] = props.parent
256
+ delete props.parent
257
+
258
+ this.props.raw = new Map(Object.entries(props))
259
+
260
+ for (let i = 0; i < length; i++) {
261
+ const key = propKeys[i]
262
+ const value = props[key]
263
+ if (value !== undefined) {
264
+ this.props[key] = unpackTransition(value)
272
265
  }
273
266
  }
274
267
 
275
268
  // correct for default white nodes (but not for text nodes)
276
- if (!props.__textnode && !this.props._set.has('color')) {
277
- this.props._props.color =
278
- this.props._set.has('src') ||
279
- this.props._set.has('texture') ||
280
- (this.props._set.has('rtt') && this.props._props.rtt === true)
281
- ? 0xffffffff
282
- : 0
269
+ if (this.props.props['color'] === undefined && !('__textnode' in props)) {
270
+ this.props.props['color'] = 0
283
271
  }
284
- this.node = props.__textnode
285
- ? renderer.createTextNode(this.props._props)
286
- : renderer.createNode(this.props._props)
287
272
 
288
- if (props.__textnode) {
289
- this.node.on('loaded', (el, { dimensions }) => {
290
- this.node.parent.width = dimensions.width
291
- this.node.parent.height = dimensions.height
292
- })
293
- }
273
+ this.node = props.__textnode
274
+ ? renderer.createTextNode({ ...textDefaults, ...this.props.props })
275
+ : (this.node = renderer.createNode(this.props.props))
294
276
 
295
277
  if (props['@loaded']) {
296
278
  this.node.on('loaded', (el, { type, dimensions }) => {
@@ -303,31 +285,56 @@ const Element = {
303
285
  props['@error'](error, this)
304
286
  })
305
287
  }
288
+
289
+ if (this.component && this.component.___layout) {
290
+ this.node.on('loaded', () => {
291
+ this.component.___layout()
292
+ })
293
+ }
306
294
  },
307
295
  set(prop, value) {
308
296
  if (value === undefined) return
309
- const propsSet = new Set(this.props._set)
297
+ if (this.props.raw.get(prop) === value) return
298
+
299
+ this.props.raw.set(prop, value)
310
300
 
311
- this.props._props = {}
301
+ this.props.props = {}
312
302
  this.props[prop] = unpackTransition(value)
303
+ const props = Object.entries(this.props.props)
313
304
 
314
- for (const [p, v] of Object.entries(this.props._props)) {
315
- if (isTransition(value) && propsSet.has(p)) {
305
+ if (props.length === 1) {
306
+ const [p, v] = props[0]
307
+ if (isTransition(value)) {
316
308
  return this.animate(p, v, value.transition)
317
- } else {
309
+ }
310
+ // set the prop to the value on the node
311
+ this.node[p] = v
312
+ } else {
313
+ for (let i = 0; i < props.length; i++) {
314
+ // todo: fix code duplication
315
+ const [p, v] = props[i]
316
+ if (isTransition(value)) {
317
+ return this.animate(p, v, value.transition)
318
+ }
319
+ // set the prop to the value on the node
318
320
  this.node[p] = v
319
321
  }
320
322
  }
323
+
324
+ // todo: review naming
325
+ if (this.component && this.component.___layout) {
326
+ this.component.___layout()
327
+ }
321
328
  },
322
- async animate(prop, value, transition) {
329
+ animate(prop, value, transition) {
323
330
  // if current value is the same as the value to animate to, instantly resolve
324
- if (this.node[prop] === value) return Promise.resolve()
331
+ if (this.node[prop] === value) return
325
332
  // check if a transition is already scheduled or running on the same prop
326
333
  if (this.scheduledTransitions[prop]) {
327
334
  if (this.scheduledTransitions[prop].f.state === 'running') {
328
- this.scheduledTransitions[prop].f.stop()
329
335
  // fastforward to final value
330
336
  this.node[prop] = this.scheduledTransitions[prop].v
337
+ // this.scheduledTransitions[prop].f.stop()
331
338
  }
332
339
  }
333
340
 
@@ -344,9 +351,7 @@ const Element = {
344
351
  : 300,
345
352
  easing:
346
353
  typeof transition === 'object'
347
- ? 'function' in transition
348
- ? Log.warn(deprecationMsg)
349
- : 'easing' in transition
354
+ ? 'easing' in transition
350
355
  ? transition.easing
351
356
  : 'ease'
352
357
  : 'ease',
@@ -364,48 +369,56 @@ const Element = {
364
369
  f,
365
370
  }
366
371
 
367
- // wait until the animation really starts (depending on specified delay)
368
- const animation = await f.start().waitUntilStarted()
369
-
370
- // fire transition start callback if specified
371
- transition.start &&
372
- typeof transition.start === 'function' &&
373
- transition.start.call(this.component, this, prop, startValue)
374
-
375
- // wait until the animation ends
376
- const finished = await animation.waitUntilStopped()
377
-
378
- // removed the prop from scheduled transitions
379
- delete this.scheduledTransitions[prop]
372
+ if (transition.start && typeof transition.start === 'function') {
373
+ // fire transition start callback when animation really starts (depending on specified delay)
374
+ f.once('animating', () => {
375
+ transition.start.call(this.component, this, prop, startValue)
376
+ })
377
+ }
380
378
 
381
- // fire transition end callback if specified
382
- transition.end &&
383
- typeof transition.end === 'function' &&
384
- transition.end.call(this.component, this, prop, this.node[prop])
379
+ f.once('stopped', () => {
380
+ // remove the prop from scheduled transitions
381
+ this.scheduledTransitions[prop] = null
382
+ // fire transition end callback when animation ends (if specified)
383
+ if (transition.end && typeof transition.end === 'function') {
384
+ transition.end.call(this.component, this, prop, this.node[prop])
385
+ }
386
+ })
385
387
 
386
- return Promise.resolve()
388
+ // start animation
389
+ f.start()
387
390
  },
388
391
  destroy() {
389
392
  Log.debug('Deleting Node', this.nodeId)
390
- Object.values(this.scheduledTransitions).forEach((scheduledTransition) => {
391
- clearTimeout(scheduledTransition.timeout)
392
- })
393
393
  this.node.destroy()
394
394
  },
395
395
  get nodeId() {
396
396
  return this.node && this.node.id
397
397
  },
398
398
  get ref() {
399
- return this.initData.ref || null
399
+ return this.props.ref || null
400
+ },
401
+ get parent() {
402
+ return this.node && this.node.parent
403
+ },
404
+ get children() {
405
+ return this.component[symbols.getChildren]().filter((child) => {
406
+ return child.parent === (this[symbols.isSlot] ? this.node.children[0] : this.node)
407
+ })
400
408
  },
401
409
  }
402
410
 
403
- export default (config, component) =>
404
- Object.assign(Object.create(Element), {
405
- props: Object.assign(Object.create(Props), { _props: {}, _set: new Set() }),
406
- node: null,
411
+ export default (config, component) => {
412
+ if (!textDefaults) {
413
+ textDefaults = {
414
+ fontSize: 32,
415
+ fontFamily: Settings.get('defaultFont', 'sans-serif'),
416
+ }
417
+ }
418
+ return Object.assign(Object.create(Element), {
419
+ props: Object.assign(Object.create(propsTransformer), { props: {} }),
407
420
  scheduledTransitions: {},
408
- initData: {},
409
421
  config,
410
422
  component,
411
423
  })
424
+ }