@leafer/canvas-web 1.0.0-alpha.10 → 1.0.0-alpha.21

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@leafer/canvas-web",
3
- "version": "1.0.0-alpha.10",
3
+ "version": "1.0.0-alpha.21",
4
4
  "description": "@leafer/canvas-web",
5
5
  "author": "Chao (Leafer) Wan",
6
6
  "license": "MIT",
@@ -19,11 +19,11 @@
19
19
  "leaferjs"
20
20
  ],
21
21
  "dependencies": {
22
- "@leafer/math": "1.0.0-alpha.10",
23
- "@leafer/event": "1.0.0-alpha.10",
24
- "@leafer/debug": "1.0.0-alpha.10"
22
+ "@leafer/math": "1.0.0-alpha.21",
23
+ "@leafer/event": "1.0.0-alpha.21",
24
+ "@leafer/debug": "1.0.0-alpha.21"
25
25
  },
26
26
  "devDependencies": {
27
- "@leafer/interface": "1.0.0-alpha.10"
27
+ "@leafer/interface": "1.0.0-alpha.21"
28
28
  }
29
29
  }
package/src/CanvasBase.ts CHANGED
@@ -122,7 +122,7 @@ export class CanvasBase {
122
122
 
123
123
  // end
124
124
 
125
- public bindContextMethod(): void {
125
+ public __bindContext(): void {
126
126
  let method: IFunction
127
127
  contextMethodNameList.forEach(name => {
128
128
  method = (this.context as IObject)[name]
@@ -1,12 +1,15 @@
1
1
  import { IBounds, ILeaferCanvas, ICanvasStrokeOptions, ICanvasContext2D, ILeaferCanvasConfig, IMatrixData, IBoundsData, IAutoBounds, ISizeData, IScreenSizeData, IResizeEventListener, IMatrixWithBoundsData, IPointData, InnerId, ICanvasManager, IWindingRule } from '@leafer/interface'
2
2
  import { Bounds, BoundsHelper, IncrementId } from '@leafer/math'
3
3
  import { ResizeEvent } from '@leafer/event'
4
+ import { Platform } from '@leafer/platform'
4
5
  import { Debug } from '@leafer/debug'
5
6
 
6
7
  import { CanvasBase } from './CanvasBase'
7
8
 
8
9
 
9
10
  const debug = Debug.get('LeaferCanvas')
11
+
12
+ const temp = new Bounds()
10
13
  const minSize: IScreenSizeData = {
11
14
  width: 1,
12
15
  height: 1,
@@ -15,10 +18,9 @@ const minSize: IScreenSizeData = {
15
18
 
16
19
  export class LeaferCanvas extends CanvasBase implements ILeaferCanvas {
17
20
 
18
- public readonly innerId: InnerId
19
-
20
21
  public manager: ICanvasManager
21
- public view: HTMLCanvasElement
22
+
23
+ public readonly innerId: InnerId
22
24
 
23
25
  public pixelRatio: number
24
26
  public get pixelWidth(): number { return this.width * this.pixelRatio }
@@ -26,6 +28,9 @@ export class LeaferCanvas extends CanvasBase implements ILeaferCanvas {
26
28
 
27
29
  public bounds: IBounds
28
30
 
31
+ public view: HTMLCanvasElement | OffscreenCanvas
32
+ public offscreen: boolean
33
+
29
34
  public recycled?: boolean
30
35
 
31
36
  protected resizeObserver: ResizeObserver
@@ -38,79 +43,105 @@ export class LeaferCanvas extends CanvasBase implements ILeaferCanvas {
38
43
  this.manager = manager
39
44
  this.innerId = IncrementId.create(IncrementId.CNAVAS)
40
45
 
41
- if (config.view) {
46
+ const { view, width, height, pixelRatio, fill, hittable: hitable } = config
47
+ const autoLayout = !width || !height
42
48
 
43
- const { view } = config
49
+ this.pixelRatio = pixelRatio
50
+ this.offscreen = Platform.isWorker || config.offscreen
44
51
 
45
- let realView: unknown = (typeof view === 'string') ? document.getElementById(view) : view as HTMLElement
46
- if (realView) {
47
- if (realView instanceof HTMLCanvasElement) {
52
+ if (this.offscreen) {
53
+ view ? this.view = view as OffscreenCanvas : this.__createView()
54
+ } else {
55
+ view ? this.__createViewFrom(view) : this.__createView()
56
+ const { style } = this.view as HTMLCanvasElement
57
+ if (fill) style.backgroundColor = fill
58
+ if (!hitable) style.pointerEvents = 'none'
59
+ if (autoLayout) style.display || (style.display = 'block')
60
+ }
61
+
62
+ this.__init()
63
+ if (!autoLayout) this.resize(config as IScreenSizeData)
64
+ }
48
65
 
49
- this.view = realView
66
+ protected __init(): void {
67
+ this.context = this.view.getContext('2d') as ICanvasContext2D
68
+ this.smooth = true
69
+ this.__bindContext()
70
+ }
50
71
 
51
- } else {
52
- if (realView === window || realView === document) {
53
- const div = document.createElement('div')
54
- const { style } = div
55
- style.position = 'absolute'
56
- style.top = style.bottom = style.left = style.right = '0px'
57
- style.overflow = 'hidden'
58
- document.body.appendChild(div)
59
- realView = div
60
- }
72
+ protected __createView(): void {
73
+ this.view = this.offscreen ? new OffscreenCanvas(1, 1) : document.createElement('canvas')
74
+ }
75
+
76
+ protected __createViewFrom(inputView: string | object): void {
77
+ let find: unknown = (typeof inputView === 'string') ? document.getElementById(inputView) : inputView as HTMLElement
78
+ if (find) {
79
+ if (find instanceof HTMLCanvasElement) {
80
+
81
+ this.view = find
61
82
 
62
- this.view = document.createElement('canvas');
63
- (realView as HTMLElement).appendChild(this.view)
64
- }
65
83
  } else {
66
- debug.error(`can't find view by id: ${view}`)
67
- }
68
84
 
69
- }
85
+ let parent = find as HTMLDivElement
86
+ if (find === window || find === document) {
87
+ const div = document.createElement('div')
88
+ const { style } = div
89
+ style.position = 'absolute'
90
+ style.top = style.bottom = style.left = style.right = '0px'
91
+ style.overflow = 'hidden'
92
+ document.body.appendChild(div)
93
+ parent = div
94
+ }
70
95
 
71
- if (!this.view) this.view = document.createElement('canvas')
72
- this.pixelRatio = config.pixelRatio
96
+ this.__createView()
97
+ const view = this.view as HTMLCanvasElement
73
98
 
74
- if (!config.webgl) {
75
- this.context = this.view.getContext('2d') as ICanvasContext2D
76
- this.smooth = true
77
- if (config.fill) this.view.style.backgroundColor = config.fill
78
- if (config.width && config.height) this.resize(config as IScreenSizeData)
99
+ if (parent.hasChildNodes()) {
100
+ const { style } = view
101
+ style.position = 'absolute'
102
+ style.top = style.left = '0px'
103
+ parent.style.position || (parent.style.position = 'relative')
104
+ }
79
105
 
80
- this.bindContextMethod()
106
+ parent.appendChild(view)
107
+ }
108
+ } else {
109
+ debug.error(`can't find view by id: ${inputView}`)
110
+ this.__createView()
81
111
  }
82
112
  }
83
113
 
84
- public debug(): void { }
85
-
86
114
  public pixel(num: number): number { return num * this.pixelRatio }
87
115
 
88
- public autoLayout(autoBounds: IAutoBounds, listener: IResizeEventListener): void {
89
- const check = (parentSize: ISizeData) => {
90
- const { x, y, width, height } = autoBounds.getBoundsFrom(parentSize)
91
- const { style } = this.view
92
- style.marginLeft = x + 'px'
93
- style.marginTop = y + 'px'
94
-
95
- if (width !== this.width || height !== this.height) {
96
- const { pixelRatio } = this
97
- const size = { width, height, pixelRatio }
98
- const oldSize = { width: this.width, height: this.height, pixelRatio: this.pixelRatio }
99
- this.resize(size)
100
- if (this.width !== undefined) listener(new ResizeEvent(size, oldSize))
116
+ public startAutoLayout(autoBounds: IAutoBounds, listener: IResizeEventListener): void {
117
+ if (!this.offscreen) {
118
+ const view = this.view as HTMLCanvasElement
119
+ const check = (parentSize: ISizeData) => {
120
+ const { x, y, width, height } = autoBounds.getBoundsFrom(parentSize)
121
+ const { style } = view
122
+ style.marginLeft = x + 'px'
123
+ style.marginTop = y + 'px'
124
+
125
+ if (width !== this.width || height !== this.height) {
126
+ const { pixelRatio } = this
127
+ const size = { width, height, pixelRatio }
128
+ const oldSize = { width: this.width, height: this.height, pixelRatio: this.pixelRatio }
129
+ this.resize(size)
130
+ if (this.width !== undefined) listener(new ResizeEvent(size, oldSize))
131
+ }
101
132
  }
102
- }
103
133
 
104
- this.resizeObserver = new ResizeObserver((entries) => {
105
- for (const entry of entries) {
106
- check(entry.contentRect)
107
- }
108
- })
134
+ this.resizeObserver = new ResizeObserver((entries) => {
135
+ for (const entry of entries) {
136
+ check(entry.contentRect)
137
+ }
138
+ })
109
139
 
110
- const parent = this.view.parentElement
111
- if (parent) {
112
- this.resizeObserver.observe(parent)
113
- check(parent.getBoundingClientRect())
140
+ const parent = view.parentElement
141
+ if (parent) {
142
+ this.resizeObserver.observe(parent)
143
+ check(parent.getBoundingClientRect())
144
+ }
114
145
  }
115
146
  }
116
147
 
@@ -122,26 +153,29 @@ export class LeaferCanvas extends CanvasBase implements ILeaferCanvas {
122
153
  }
123
154
 
124
155
  public resize(size: IScreenSizeData): void {
125
- const { style } = this.view
126
156
  const { width, height, pixelRatio } = size
127
157
  if (this.isSameSize(size)) return
128
158
 
129
159
  let takeCanvas: ILeaferCanvas
130
160
  if (this.context && this.width) {
131
161
  takeCanvas = this.getSameCanvas()
132
- takeCanvas.copy(this)
162
+ takeCanvas.copyWorld(this)
133
163
  }
134
164
 
135
165
  Object.assign(this, { width, height, pixelRatio })
136
166
  this.bounds = new Bounds(0, 0, width, height)
137
167
 
138
- style.width = width + 'px'
139
- style.height = height + 'px'
168
+ if (!this.offscreen) {
169
+ const { style } = this.view as HTMLCanvasElement
170
+ style.width = width + 'px'
171
+ style.height = height + 'px'
172
+ }
173
+
140
174
  this.view.width = width * pixelRatio
141
175
  this.view.height = height * pixelRatio
142
176
 
143
177
  if (this.context && takeCanvas) {
144
- this.copy(takeCanvas)
178
+ this.copyWorld(takeCanvas)
145
179
  takeCanvas.recycle()
146
180
  }
147
181
  }
@@ -201,7 +235,16 @@ export class LeaferCanvas extends CanvasBase implements ILeaferCanvas {
201
235
  }
202
236
  }
203
237
 
204
- public setShadow(x: number, y: number, blur: number, color?: string): void {
238
+ public hitPath(point: IPointData, fillRule?: IWindingRule): boolean {
239
+ return this.context.isPointInPath(point.x, point.y, fillRule)
240
+ }
241
+
242
+ public hitStroke(point: IPointData): boolean {
243
+ return this.context.isPointInStroke(point.x, point.y)
244
+ }
245
+
246
+
247
+ public setWorldShadow(x: number, y: number, blur: number, color?: string): void {
205
248
  const { pixelRatio } = this
206
249
  this.shadowOffsetX = x * pixelRatio
207
250
  this.shadowOffsetY = y * pixelRatio
@@ -209,28 +252,13 @@ export class LeaferCanvas extends CanvasBase implements ILeaferCanvas {
209
252
  this.shadowColor = color || 'black'
210
253
  }
211
254
 
212
- public setBlur(blur: number): void {
255
+ public setWorldBlur(blur: number): void {
213
256
  const { pixelRatio } = this
214
257
  this.filter = `blur(${blur * pixelRatio}px)`
215
258
  }
216
259
 
217
260
 
218
- public hitPath(point: IPointData, fillRule?: IWindingRule): boolean {
219
- return this.context.isPointInPath(point.x, point.y, fillRule)
220
- }
221
-
222
- public hitStroke(point: IPointData): boolean {
223
- return this.context.isPointInStroke(point.x, point.y)
224
- }
225
-
226
- public replaceBy(canvas: ILeaferCanvas, from?: IBoundsData, to?: IBoundsData): void {
227
- canvas.save()
228
- this.blendMode = 'copy'
229
- this.copy(canvas, from, to)
230
- canvas.restore()
231
- }
232
-
233
- public copy(canvas: ILeaferCanvas, from?: IBoundsData, to?: IBoundsData, blendMode?: string): void {
261
+ public copyWorld(canvas: ILeaferCanvas, from?: IBoundsData, to?: IBoundsData, blendMode?: string): void {
234
262
  if (from) {
235
263
  if (!to) to = from
236
264
  const { pixelRatio } = this
@@ -249,7 +277,7 @@ export class LeaferCanvas extends CanvasBase implements ILeaferCanvas {
249
277
  if (fromWorld.b || fromWorld.c) {
250
278
  this.save()
251
279
  this.resetTransform()
252
- this.copy(canvas, fromWorld, BoundsHelper.tempTimesMatrix(toLocalBounds, fromWorld))
280
+ this.copyWorld(canvas, fromWorld, BoundsHelper.tempToWorld(toLocalBounds, fromWorld))
253
281
  this.restore()
254
282
  } else {
255
283
  this.drawImage(canvas.view as HTMLCanvasElement, fromWorld.x * pixelRatio, fromWorld.y * pixelRatio, fromWorld.width * pixelRatio, fromWorld.height * pixelRatio, toLocalBounds.x, toLocalBounds.y, toLocalBounds.width, toLocalBounds.height)
@@ -257,7 +285,7 @@ export class LeaferCanvas extends CanvasBase implements ILeaferCanvas {
257
285
  if (blendMode) this.blendMode = 'normal'
258
286
  }
259
287
 
260
- public fillBounds(bounds: IBoundsData, color: string | object, blendMode?: string): void {
288
+ public fillWorld(bounds: IBoundsData, color: string | object, blendMode?: string): void {
261
289
  const { pixelRatio } = this
262
290
  if (blendMode) this.blendMode = blendMode
263
291
  this.fillStyle = color
@@ -265,7 +293,7 @@ export class LeaferCanvas extends CanvasBase implements ILeaferCanvas {
265
293
  if (blendMode) this.blendMode = 'normal'
266
294
  }
267
295
 
268
- public strokeBounds(bounds: IBoundsData, color: string | object, blendMode?: string): void {
296
+ public strokeWorld(bounds: IBoundsData, color: string | object, blendMode?: string): void {
269
297
  const { pixelRatio } = this
270
298
  if (blendMode) this.blendMode = blendMode
271
299
  this.strokeStyle = color
@@ -273,15 +301,19 @@ export class LeaferCanvas extends CanvasBase implements ILeaferCanvas {
273
301
  if (blendMode) this.blendMode = 'normal'
274
302
  }
275
303
 
276
- public clearBounds(bounds: IBoundsData): void {
304
+ public clearWorld(bounds: IBoundsData, ceilPixel?: boolean): void {
277
305
  const { pixelRatio } = this
278
- this.clearRect(bounds.x * pixelRatio, bounds.y * pixelRatio, bounds.width * pixelRatio, bounds.height * pixelRatio)
306
+ temp.copy(bounds).scale(pixelRatio)
307
+ if (ceilPixel) temp.ceil()
308
+ this.clearRect(temp.x, temp.y, temp.width, temp.height)
279
309
  }
280
310
 
281
- public clipBounds(bounds: IBoundsData): void {
311
+ public clipWorld(bounds: IBoundsData, ceilPixel?: boolean): void {
282
312
  const { pixelRatio } = this
283
313
  this.beginPath()
284
- this.rect(bounds.x * pixelRatio, bounds.y * pixelRatio, bounds.width * pixelRatio, bounds.height * pixelRatio)
314
+ temp.copy(bounds).scale(pixelRatio)
315
+ if (ceilPixel) temp.ceil()
316
+ this.rect(temp.x, temp.y, temp.width, temp.height)
285
317
  this.clip()
286
318
 
287
319
  }
@@ -328,10 +360,13 @@ export class LeaferCanvas extends CanvasBase implements ILeaferCanvas {
328
360
  if (this.view) {
329
361
  super.destroy()
330
362
  this.stopAutoLayout()
363
+ if (!this.offscreen) {
364
+ const view = this.view as HTMLCanvasElement
365
+ if (view.parentElement) view.remove()
366
+ }
331
367
  this.manager = null
332
- if (this.view && this.view.parentElement) this.view.remove()
333
368
  this.view = null
334
- this.bounds = null
369
+ this.context = null
335
370
  }
336
371
  }
337
372