@leafer/canvas-web 1.0.0-beta.15 → 1.0.0-beta.17

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 +4 -3
  2. package/src/LeaferCanvas.ts +194 -0
package/package.json CHANGED
@@ -1,12 +1,13 @@
1
1
  {
2
2
  "name": "@leafer/canvas-web",
3
- "version": "1.0.0-beta.15",
3
+ "version": "1.0.0-beta.17",
4
4
  "description": "@leafer/canvas-web",
5
5
  "author": "Chao (Leafer) Wan",
6
6
  "license": "MIT",
7
7
  "main": "src/index.ts",
8
8
  "types": "types/index.d.ts",
9
9
  "files": [
10
+ "src",
10
11
  "types",
11
12
  "dist"
12
13
  ],
@@ -21,9 +22,9 @@
21
22
  "leaferjs"
22
23
  ],
23
24
  "dependencies": {
24
- "@leafer/core": "1.0.0-beta.15"
25
+ "@leafer/core": "1.0.0-beta.17"
25
26
  },
26
27
  "devDependencies": {
27
- "@leafer/interface": "1.0.0-beta.15"
28
+ "@leafer/interface": "1.0.0-beta.17"
28
29
  }
29
30
  }
@@ -0,0 +1,194 @@
1
+ import { IAutoBounds, ISizeData, IScreenSizeData, IResizeEventListener, ICursorType } from '@leafer/interface'
2
+ import { LeaferCanvasBase, canvasSizeAttrs, ResizeEvent, DataHelper, Platform, Debug, Cursor } from '@leafer/core'
3
+
4
+
5
+ const debug = Debug.get('LeaferCanvas')
6
+
7
+ export class LeaferCanvas extends LeaferCanvasBase {
8
+
9
+ declare public view: HTMLCanvasElement
10
+ declare public parentView: HTMLElement
11
+
12
+ protected resizeObserver: ResizeObserver
13
+ protected autoBounds: IAutoBounds
14
+ protected resizeListener: IResizeEventListener
15
+
16
+ public init(): void {
17
+ const { view } = this.config
18
+
19
+ view ? this.__createViewFrom(view) : this.__createView()
20
+ const { style } = this.view
21
+ style.display || (style.display = 'block')
22
+ this.parentView = this.view.parentElement
23
+
24
+ if (Platform.syncDomFont && !this.parentView) { // fix: firefox default font
25
+ this.view.style.display = 'none'
26
+ document.body.appendChild(this.view)
27
+ }
28
+
29
+ this.__createContext()
30
+
31
+ if (!this.autoLayout) this.resize(this.config as IScreenSizeData)
32
+ }
33
+
34
+ public set backgroundColor(color: string) { this.view.style.backgroundColor = color }
35
+ public get backgroundColor(): string { return this.view.style.backgroundColor }
36
+
37
+ public set hittable(hittable: boolean) { this.view.style.pointerEvents = hittable ? 'auto' : 'none' }
38
+ public get hittable() { return this.view.style.pointerEvents !== 'none' }
39
+
40
+ protected __createView(): void {
41
+ this.view = document.createElement('canvas')
42
+ }
43
+
44
+ public setCursor(cursor: ICursorType | ICursorType[]): void {
45
+ const list: ICursorType[] = []
46
+ this.eachCursor(cursor, list)
47
+ if (typeof list[list.length - 1] === 'object') list.push('default')
48
+ this.view.style.cursor = list.map(item => (typeof item === 'object') ? `url(${item.url}) ${item.x || 0} ${item.y || 0}` : item).join(',')
49
+ }
50
+
51
+ protected eachCursor(cursor: ICursorType | ICursorType[], list: ICursorType[], level = 0): void {
52
+ level++
53
+ if (cursor instanceof Array) {
54
+ cursor.forEach(item => this.eachCursor(item, list, level))
55
+ } else {
56
+ const custom = typeof cursor === 'string' && Cursor.get(cursor)
57
+ if (custom && level < 2) {
58
+ this.eachCursor(custom, list, level)
59
+ } else {
60
+ list.push(cursor)
61
+ }
62
+ }
63
+ }
64
+
65
+ protected __createViewFrom(inputView: string | object): void {
66
+ let find: unknown = (typeof inputView === 'string') ? document.getElementById(inputView) : inputView as HTMLElement
67
+ if (find) {
68
+ if (find instanceof HTMLCanvasElement) {
69
+
70
+ this.view = find
71
+
72
+ } else {
73
+
74
+ let parent = find as HTMLDivElement
75
+ if (find === window || find === document) {
76
+ const div = document.createElement('div')
77
+ const { style } = div
78
+ style.position = 'absolute'
79
+ style.top = style.bottom = style.left = style.right = '0px'
80
+ document.body.appendChild(div)
81
+ parent = div
82
+ }
83
+
84
+ this.__createView()
85
+ const view = this.view
86
+
87
+ if (parent.hasChildNodes()) {
88
+ const { style } = view
89
+ style.position = 'absolute'
90
+ style.top = style.left = '0px'
91
+ parent.style.position || (parent.style.position = 'relative')
92
+ }
93
+
94
+ parent.appendChild(view)
95
+ }
96
+ } else {
97
+ debug.error(`no id: ${inputView}`)
98
+ this.__createView()
99
+ }
100
+ }
101
+
102
+ public updateViewSize(): void {
103
+ const { width, height, pixelRatio } = this
104
+
105
+ const { style } = this.view
106
+ style.width = width + 'px'
107
+ style.height = height + 'px'
108
+
109
+ this.view.width = width * pixelRatio
110
+ this.view.height = height * pixelRatio
111
+
112
+ }
113
+
114
+ public updateClientBounds(): void {
115
+ this.clientBounds = this.view.getBoundingClientRect()
116
+ }
117
+
118
+ public startAutoLayout(autoBounds: IAutoBounds, listener: IResizeEventListener): void {
119
+ this.autoBounds = autoBounds
120
+ this.resizeListener = listener
121
+ try {
122
+
123
+ this.resizeObserver = new ResizeObserver((entries) => {
124
+ this.updateClientBounds()
125
+ for (const entry of entries) this.checkAutoBounds(entry.contentRect)
126
+ })
127
+
128
+ const parent = this.parentView
129
+ if (parent) {
130
+ this.resizeObserver.observe(parent)
131
+ this.checkAutoBounds(parent.getBoundingClientRect())
132
+ }
133
+
134
+ } catch {
135
+
136
+ this.imitateResizeObserver()
137
+
138
+ }
139
+ }
140
+
141
+ protected imitateResizeObserver(): void {
142
+ if (this.autoLayout) {
143
+ if (this.parentView) this.checkAutoBounds(this.parentView.getBoundingClientRect())
144
+ Platform.requestRender(this.imitateResizeObserver.bind(this))
145
+ }
146
+ }
147
+
148
+ protected checkAutoBounds(parentSize: ISizeData): void {
149
+ const view = this.view
150
+ const { x, y, width, height } = this.autoBounds.getBoundsFrom(parentSize)
151
+ if (width !== this.width || height !== this.height) {
152
+ const { style } = view
153
+ const { pixelRatio } = this
154
+ style.marginLeft = x + 'px'
155
+ style.marginTop = y + 'px'
156
+ const size = { width, height, pixelRatio }
157
+ const oldSize = {} as IScreenSizeData
158
+ DataHelper.copyAttrs(oldSize, this, canvasSizeAttrs)
159
+ this.resize(size)
160
+ if (this.width !== undefined) this.resizeListener(new ResizeEvent(size, oldSize))
161
+ }
162
+ }
163
+
164
+ public stopAutoLayout(): void {
165
+ this.autoLayout = false
166
+ this.resizeListener = null
167
+ if (this.resizeObserver) {
168
+ this.resizeObserver.disconnect()
169
+ this.resizeObserver = null
170
+ }
171
+ }
172
+
173
+ public unrealCanvas(): void { // App needs to use
174
+ if (!this.unreal && this.parentView) {
175
+ const view = this.view
176
+ if (view) view.remove()
177
+
178
+ this.view = this.parentView as HTMLCanvasElement
179
+ this.unreal = true
180
+ }
181
+ }
182
+
183
+ public destroy(): void {
184
+ if (this.view) {
185
+ this.stopAutoLayout()
186
+ if (!this.unreal) {
187
+ const view = this.view
188
+ if (view.parentElement) view.remove()
189
+ }
190
+ super.destroy()
191
+ }
192
+ }
193
+
194
+ }