@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.
- package/package.json +7 -7
- 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-
|
|
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-
|
|
23
|
-
"@leafer/math": "1.0.0-
|
|
24
|
-
"@leafer/data": "1.0.0-
|
|
25
|
-
"@leafer/platform": "1.0.0-
|
|
26
|
-
"@leafer/debug": "1.0.0-
|
|
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-
|
|
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,
|
|
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
|
|
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
|
|
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
|
-
|
|
32
|
+
protected renderBounds: IBounds
|
|
33
|
+
protected renderOptions: IRenderOptions
|
|
34
|
+
protected totalBounds: IBounds
|
|
25
35
|
|
|
26
|
-
|
|
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
|
|
38
|
+
protected get needFill(): boolean { return !!(!this.canvas.allowBackgroundColor && this.config.fill) }
|
|
40
39
|
|
|
41
|
-
constructor(target: ILeaf, canvas: ILeaferCanvas, userConfig
|
|
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.
|
|
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
|
-
|
|
58
|
-
|
|
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
|
-
|
|
99
|
-
|
|
100
|
-
target.
|
|
101
|
-
|
|
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
|
-
|
|
106
|
-
|
|
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
|
-
|
|
110
|
+
this.emitRender(RenderEvent.BEFORE)
|
|
123
111
|
|
|
124
|
-
if (this.totalTimes > 1) {
|
|
125
|
-
|
|
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
|
-
|
|
133
|
-
|
|
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.
|
|
137
|
-
this.
|
|
138
|
-
this.
|
|
125
|
+
if (this.waitAgain) {
|
|
126
|
+
this.waitAgain = false
|
|
127
|
+
this.renderOnce()
|
|
139
128
|
}
|
|
140
|
-
|
|
141
|
-
this.__checkAgain()
|
|
142
129
|
}
|
|
143
130
|
|
|
144
|
-
|
|
145
|
-
|
|
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
|
-
|
|
150
|
-
const
|
|
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
|
-
|
|
153
|
-
|
|
147
|
+
canvas.save()
|
|
148
|
+
|
|
149
|
+
if (includes && !Debug.showRepaint) {
|
|
150
|
+
canvas.clear()
|
|
154
151
|
} else {
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
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
|
|
164
|
-
const t = Run.start('
|
|
165
|
-
const { canvas
|
|
163
|
+
public fullRender(): void {
|
|
164
|
+
const t = Run.start('FullRender')
|
|
165
|
+
const { canvas } = this
|
|
166
166
|
|
|
167
167
|
canvas.save()
|
|
168
|
-
canvas.
|
|
169
|
-
|
|
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
|
-
|
|
178
|
-
const
|
|
179
|
-
|
|
180
|
-
if (
|
|
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
|
-
|
|
184
|
-
const t = Run.start('full render')
|
|
185
|
-
if (!bounds) bounds = canvas.bounds
|
|
191
|
+
public renderHitView(_options: IRenderOptions): void { }
|
|
186
192
|
|
|
187
|
-
|
|
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
|
-
|
|
195
|
+
public addBlock(block: IBounds): void {
|
|
196
|
+
if (!this.updateBlocks) this.updateBlocks = []
|
|
197
|
+
this.updateBlocks.push(block)
|
|
193
198
|
}
|
|
194
199
|
|
|
195
|
-
|
|
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.
|
|
208
|
-
this.
|
|
209
|
-
this.
|
|
210
|
-
this.
|
|
267
|
+
this.stop()
|
|
268
|
+
this.__removeListenEvents()
|
|
269
|
+
this.target = null
|
|
270
|
+
this.canvas = null
|
|
271
|
+
this.config = null
|
|
211
272
|
}
|
|
212
273
|
}
|
|
213
274
|
}
|