@leafer/renderer 1.0.0-alpha.7 → 1.0.0-bate

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 (2) hide show
  1. package/package.json +7 -7
  2. package/src/Renderer.ts +169 -108
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@leafer/renderer",
3
- "version": "1.0.0-alpha.7",
3
+ "version": "1.0.0-bate",
4
4
  "description": "@leafer/renderer",
5
5
  "author": "Chao (Leafer) Wan",
6
6
  "license": "MIT",
@@ -19,13 +19,13 @@
19
19
  "leaferjs"
20
20
  ],
21
21
  "dependencies": {
22
- "@leafer/event": "1.0.0-alpha.7",
23
- "@leafer/math": "1.0.0-alpha.7",
24
- "@leafer/data": "1.0.0-alpha.7",
25
- "@leafer/platform": "1.0.0-alpha.7",
26
- "@leafer/debug": "1.0.0-alpha.7"
22
+ "@leafer/event": "1.0.0-bate",
23
+ "@leafer/math": "1.0.0-bate",
24
+ "@leafer/data": "1.0.0-bate",
25
+ "@leafer/platform": "1.0.0-bate",
26
+ "@leafer/debug": "1.0.0-bate"
27
27
  },
28
28
  "devDependencies": {
29
- "@leafer/interface": "1.0.0-alpha.7"
29
+ "@leafer/interface": "1.0.0-bate"
30
30
  }
31
31
  }
package/src/Renderer.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { ILeaf, ILeaferCanvas, IRenderer, IRendererConfig, IEventListenerId, IBounds, IFunction, ILayoutBlockData } from '@leafer/interface'
1
+ import { ILeaf, ILeaferCanvas, IRenderer, IRendererConfig, IEventListenerId, IBounds, IFunction, IRenderOptions } from '@leafer/interface'
2
2
  import { LayoutEvent, RenderEvent, ResizeEvent } from '@leafer/event'
3
3
  import { Bounds } from '@leafer/math'
4
4
  import { DataHelper } from '@leafer/data'
@@ -6,80 +6,54 @@ import { Platform } from '@leafer/platform'
6
6
  import { Debug, Run } from '@leafer/debug'
7
7
 
8
8
 
9
+ const debug = Debug.get('Renderer')
10
+
9
11
  export class Renderer implements IRenderer {
10
12
 
11
13
  public target: ILeaf
12
14
  public canvas: ILeaferCanvas
13
- public layoutedBlocks: ILayoutBlockData[]
15
+ public updateBlocks: IBounds[]
14
16
 
17
+ public FPS = 60
15
18
  public totalTimes = 0
16
19
  public times: number = 0
17
20
 
18
- public FPS = 60
21
+ public running: boolean
22
+ public rendering: boolean
23
+
24
+ public waitAgain: boolean
25
+ public changed: boolean
19
26
 
20
27
  public config: IRendererConfig = {
28
+ usePartRender: true,
21
29
  maxFPS: 60
22
30
  }
23
31
 
24
- public running: boolean
32
+ protected renderBounds: IBounds
33
+ protected renderOptions: IRenderOptions
34
+ protected totalBounds: IBounds
25
35
 
26
- private _changed = false
27
- set changed(value: boolean) {
28
- if (value) {
29
- if (!this._changed) {
30
- this._changed = true
31
- this.requestRender()
32
- }
33
- } else {
34
- this._changed = false
35
- }
36
- }
37
- get changed(): boolean { return this._changed }
36
+ protected __eventIds: IEventListenerId[]
38
37
 
39
- protected eventIds: IEventListenerId[]
38
+ protected get needFill(): boolean { return !!(!this.canvas.allowBackgroundColor && this.config.fill) }
40
39
 
41
- constructor(target: ILeaf, canvas: ILeaferCanvas, userConfig: IRendererConfig) {
40
+ constructor(target: ILeaf, canvas: ILeaferCanvas, userConfig?: IRendererConfig) {
42
41
  this.target = target
43
42
  this.canvas = canvas
44
43
  if (userConfig) this.config = DataHelper.default(userConfig, this.config)
45
- this.listenEvents()
44
+ this.__listenEvents()
46
45
  }
47
46
 
48
47
  public start(): void {
49
48
  this.running = true
50
- this.changed = true
51
49
  }
52
50
 
53
51
  public stop(): void {
54
52
  this.running = false
55
53
  }
56
54
 
57
- private listenEvents(): void {
58
- const { target } = this
59
- this.eventIds = [
60
- target.on__(RenderEvent.REQUEST, this.onRequest, this),
61
- target.on__(LayoutEvent.END, this.onLayoutEnd, this),
62
- target.on__(RenderEvent.AGAIN, this.renderOnce, this),
63
- target.on__(ResizeEvent.RESIZE, this.onResize, this)
64
- ]
65
- }
66
-
67
- private removeListenEvents(): void {
68
- this.target.off__(this.eventIds)
69
- }
70
-
71
- protected onResize(e: ResizeEvent): void {
72
- if (e.bigger || !e.samePixelRatio) {
73
- const { width, height } = e.old
74
- const bounds = new Bounds(0, 0, width, height)
75
- if (!bounds.includes(this.target.__world)) {
76
- this.target.__updateAttr('fill')
77
- this.changed = true
78
- }
79
- }
80
- }
81
-
82
- protected onRequest(): void {
55
+ public update(): void {
56
+ if (!this.changed) this.__requestRender()
83
57
  this.changed = true
84
58
  }
85
59
 
@@ -87,112 +61,153 @@ export class Renderer implements IRenderer {
87
61
  this.target.emit(LayoutEvent.REQUEST)
88
62
  }
89
63
 
90
- public onLayoutEnd(event: LayoutEvent): void {
91
- this.layoutedBlocks = event.data
92
- }
93
-
94
64
  public render(callback?: IFunction): void {
65
+ if (!(this.running)) return
66
+
95
67
  const { target } = this
96
- const { START, RENDER, END } = RenderEvent
97
68
  this.times = 0
98
- target.emit(START)
99
- this.renderOnce(callback)
100
- target.emit(RENDER)
101
- target.emit(END)
69
+ this.totalBounds = new Bounds()
70
+
71
+ debug.log(target.innerName, '--->')
72
+
73
+ try {
74
+ this.emitRender(RenderEvent.START)
75
+ this.renderOnce(callback)
76
+ this.emitRender(RenderEvent.END, this.totalBounds)
77
+ } catch (e) {
78
+ debug.error(e)
79
+ }
80
+
81
+ debug.log('-------------|')
82
+ }
83
+
84
+ public renderAgain(): void {
85
+ if (this.rendering) {
86
+ this.waitAgain = true
87
+ } else {
88
+ this.renderOnce()
89
+ }
102
90
  }
103
91
 
104
92
  public renderOnce(callback?: IFunction): void {
105
- const { target } = this
106
- const { BEFORE_ONCE, ONCE, AFTER_ONCE } = RenderEvent
93
+ if (this.rendering) return debug.warn('rendering')
94
+ if (this.times > 3) return debug.warn('render max times')
107
95
 
108
96
  this.times++
109
97
  this.totalTimes++
98
+
99
+ this.rendering = true
110
100
  this.changed = false
101
+ this.renderBounds = new Bounds()
102
+ this.renderOptions = {}
111
103
 
112
104
  if (callback) {
113
-
114
- target.emit(BEFORE_ONCE)
115
-
105
+ this.emitRender(RenderEvent.BEFORE)
116
106
  callback()
117
-
118
107
  } else {
119
-
120
108
  this.requestLayout()
121
109
 
122
- target.emit(BEFORE_ONCE)
110
+ this.emitRender(RenderEvent.BEFORE)
123
111
 
124
- if (this.totalTimes > 1) {
125
- if (this.layoutedBlocks) this.partRender()
112
+ if (this.config.usePartRender && this.totalTimes > 1) {
113
+ this.partRender()
126
114
  } else {
127
115
  this.fullRender()
128
116
  }
129
-
130
117
  }
131
118
 
132
- target.emit(ONCE)
133
- target.emit(AFTER_ONCE)
119
+ this.emitRender(RenderEvent.RENDER, this.renderBounds, this.renderOptions)
120
+ this.emitRender(RenderEvent.AFTER, this.renderBounds, this.renderOptions)
134
121
 
122
+ this.updateBlocks = null
123
+ this.rendering = false
135
124
 
136
- if (this.layoutedBlocks) {
137
- this.layoutedBlocks.forEach(item => { item.destroy() })
138
- this.layoutedBlocks = undefined
125
+ if (this.waitAgain) {
126
+ this.waitAgain = false
127
+ this.renderOnce()
139
128
  }
140
-
141
- this.__checkAgain()
142
129
  }
143
130
 
144
- protected __checkAgain(): void {
145
- if (this.changed && this.times < 3) this.target.emit(RenderEvent.AGAIN)
131
+ public partRender(): void {
132
+ const { canvas, updateBlocks: list } = this
133
+ if (!list) return debug.warn('PartRender: need update attr')
134
+
135
+ if (list.some(block => block.includes(this.target.__world))) this.mergeBlocks()
136
+ list.forEach(block => { if (canvas.bounds.hit(block) && !block.isEmpty()) this.clipRender(block) })
146
137
  }
147
138
 
139
+ public clipRender(block: IBounds): void {
140
+ const t = Run.start('PartRender')
141
+ const { canvas } = this
148
142
 
149
- protected partRender(): void {
150
- const { canvas, layoutedBlocks } = this
143
+ const bounds = block.getIntersect(canvas.bounds)
144
+ const includes = block.includes(this.target.__world)
145
+ const realBounds = new Bounds().copy(bounds)
151
146
 
152
- if (layoutedBlocks.some(block => block.updatedBounds.includes(this.target.__world))) {
153
- this.fullRender(canvas.bounds)
147
+ canvas.save()
148
+
149
+ if (includes && !Debug.showRepaint) {
150
+ canvas.clear()
154
151
  } else {
155
- let bounds: IBounds
156
- layoutedBlocks.forEach(block => {
157
- bounds = block.updatedBounds
158
- if (canvas.bounds.hit(bounds) && !bounds.isEmpty()) this.clipRender(bounds.getIntersect(canvas.bounds))
159
- })
152
+ bounds.spread(1 + 1 / this.canvas.pixelRatio).ceil()
153
+ canvas.clearWorld(bounds, true)
154
+ canvas.clipWorld(bounds, true)
160
155
  }
156
+
157
+ this.__render(bounds, realBounds)
158
+ canvas.restore()
159
+
160
+ Run.end(t)
161
161
  }
162
162
 
163
- public clipRender(bounds: IBounds): void {
164
- const t = Run.start('part render')
165
- const { canvas, target } = this
163
+ public fullRender(): void {
164
+ const t = Run.start('FullRender')
165
+ const { canvas } = this
166
166
 
167
167
  canvas.save()
168
- canvas.clearBounds(bounds)
169
- if (Debug.showRepaint) canvas.strokeBounds(bounds, 'red')
170
- canvas.clipBounds(bounds)
171
- target.__render(canvas, { bounds })
168
+ canvas.clear()
169
+ this.__render(canvas.bounds)
172
170
  canvas.restore()
173
171
 
174
172
  Run.end(t)
175
173
  }
176
174
 
177
- public fullRender(bounds?: IBounds): void {
178
- const { canvas, target } = this
179
- Renderer.fullRender(target, canvas, bounds)
180
- if (Debug.showRepaint) canvas.strokeBounds(bounds || canvas.bounds, 'red')
175
+ protected __render(bounds: IBounds, realBounds?: IBounds): void {
176
+ const options: IRenderOptions = bounds?.includes(this.target.__world) ? {} : { bounds }
177
+
178
+ if (this.needFill) this.canvas.fillWorld(bounds, this.config.fill)
179
+ if (Debug.showRepaint) this.canvas.strokeWorld(bounds, 'red')
180
+
181
+ this.target.__render(this.canvas, options)
182
+
183
+ this.renderBounds = realBounds || bounds
184
+ this.renderOptions = options
185
+ this.totalBounds.isEmpty() ? this.totalBounds = this.renderBounds : this.totalBounds.add(this.renderBounds)
186
+
187
+ if (Debug.showHitView) this.renderHitView(options)
188
+ if (Debug.showBoundsView) this.renderBoundsView(options)
181
189
  }
182
190
 
183
- static fullRender(target: ILeaf, canvas: ILeaferCanvas, bounds?: IBounds): void {
184
- const t = Run.start('full render')
185
- if (!bounds) bounds = canvas.bounds
191
+ public renderHitView(_options: IRenderOptions): void { }
186
192
 
187
- canvas.save()
188
- canvas.clear()
189
- target.__render(canvas, canvas.bounds.includes(target.__world) ? {} : { bounds })
190
- canvas.restore()
193
+ public renderBoundsView(_options: IRenderOptions): void { }
191
194
 
192
- Run.end(t)
195
+ public addBlock(block: IBounds): void {
196
+ if (!this.updateBlocks) this.updateBlocks = []
197
+ this.updateBlocks.push(block)
193
198
  }
194
199
 
195
- private requestRender(): void {
200
+ public mergeBlocks(): void {
201
+ const { updateBlocks: list } = this
202
+ if (list) {
203
+ const bounds = new Bounds()
204
+ bounds.setByList(list)
205
+ list.length = 0
206
+ list.push(bounds)
207
+ }
208
+ }
209
+
210
+ protected __requestRender(): void {
196
211
  const startTime = Date.now()
197
212
  Platform.requestRender(() => {
198
213
  if (this.changed) {
@@ -202,12 +217,58 @@ export class Renderer implements IRenderer {
202
217
  })
203
218
  }
204
219
 
220
+ protected __onResize(e: ResizeEvent): void {
221
+ if (this.canvas.unreal) return
222
+ if (e.bigger || !e.samePixelRatio) {
223
+ const { width, height } = e.old
224
+ const bounds = new Bounds(0, 0, width, height)
225
+ if (!bounds.includes(this.target.__world) || this.needFill || !e.samePixelRatio) {
226
+ this.addBlock(this.canvas.bounds)
227
+ this.target.forceUpdate('blendMode')
228
+ }
229
+ }
230
+ }
231
+
232
+ protected __onLayoutEnd(event: LayoutEvent): void {
233
+ if (event.data) event.data.map(item => {
234
+ let empty: boolean
235
+ if (item.updatedList) item.updatedList.list.some(leaf => {
236
+ empty = (!leaf.__world.width || !leaf.__world.height)
237
+ if (empty) {
238
+ debug.warn(leaf.innerName, ': none bounds')
239
+ empty = (!leaf.isBranch || leaf.isBranchLeaf) // render object
240
+ }
241
+ return empty
242
+ })
243
+ this.addBlock(empty ? this.canvas.bounds : item.updatedBounds)
244
+ })
245
+ }
246
+
247
+ protected emitRender(type: string, bounds?: IBounds, options?: IRenderOptions): void {
248
+ this.target.emitEvent(new RenderEvent(type, this.times, bounds, options))
249
+ }
250
+
251
+ protected __listenEvents(): void {
252
+ const { target } = this
253
+ this.__eventIds = [
254
+ target.on_(RenderEvent.REQUEST, this.update, this),
255
+ target.on_(LayoutEvent.END, this.__onLayoutEnd, this),
256
+ target.on_(RenderEvent.AGAIN, this.renderAgain, this),
257
+ target.on_(ResizeEvent.RESIZE, this.__onResize, this)
258
+ ]
259
+ }
260
+
261
+ protected __removeListenEvents(): void {
262
+ this.target.off_(this.__eventIds)
263
+ }
264
+
205
265
  public destroy(): void {
206
266
  if (this.target) {
207
- this.removeListenEvents()
208
- this.target = undefined
209
- this.canvas = undefined
210
- this.config = undefined
267
+ this.stop()
268
+ this.__removeListenEvents()
269
+ this.target = null
270
+ this.canvas = null
271
+ this.config = null
211
272
  }
212
273
  }
213
274
  }