@leafer/display-module 1.0.0-beta.15 → 1.0.0-beta.16

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,12 +1,13 @@
1
1
  {
2
2
  "name": "@leafer/display-module",
3
- "version": "1.0.0-beta.15",
3
+ "version": "1.0.0-beta.16",
4
4
  "description": "@leafer/display-module",
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,10 +22,10 @@
21
22
  "leaferjs"
22
23
  ],
23
24
  "dependencies": {
24
- "@leafer/event": "1.0.0-beta.15",
25
- "@leafer/math": "1.0.0-beta.15"
25
+ "@leafer/event": "1.0.0-beta.16",
26
+ "@leafer/math": "1.0.0-beta.16"
26
27
  },
27
28
  "devDependencies": {
28
- "@leafer/interface": "1.0.0-beta.15"
29
+ "@leafer/interface": "1.0.0-beta.16"
29
30
  }
30
31
  }
@@ -0,0 +1,90 @@
1
+ import { ILeaf, ILeaferCanvas, IRenderOptions, IBranchRenderModule } from '@leafer/interface'
2
+
3
+
4
+ export const BranchRender: IBranchRenderModule = {
5
+
6
+ __updateChange(): void {
7
+ const { __layout: layout } = this
8
+ if (layout.childrenSortChanged) {
9
+ this.__updateSortChildren()
10
+ layout.childrenSortChanged = false
11
+ }
12
+
13
+ this.__.__checkSingle()
14
+ },
15
+
16
+
17
+ __render(canvas: ILeaferCanvas, options: IRenderOptions): void {
18
+ if (this.__worldOpacity) {
19
+
20
+ if (this.__.__single) {
21
+ canvas.resetTransform()
22
+ const tempCanvas = canvas.getSameCanvas()
23
+
24
+ this.__renderBranch(tempCanvas, options)
25
+
26
+ canvas.opacity = this.__worldOpacity
27
+
28
+ const blendMode = this.__.isEraser ? 'destination-out' : this.__.blendMode
29
+ options.matrix ? canvas.copyWorld(tempCanvas, null, null, blendMode) : canvas.copyWorld(tempCanvas, this.__world, this.__world, blendMode)
30
+
31
+ tempCanvas.recycle()
32
+ } else {
33
+ this.__renderBranch(canvas, options)
34
+ }
35
+
36
+ }
37
+ },
38
+
39
+ __renderBranch(canvas: ILeaferCanvas, options: IRenderOptions): void {
40
+
41
+ let child: ILeaf
42
+ const { children } = this
43
+
44
+ if (this.__hasMask && children.length > 1) {
45
+
46
+ let mask: boolean
47
+ let maskCanvas = canvas.getSameCanvas()
48
+ let contentCanvas = canvas.getSameCanvas()
49
+
50
+ for (let i = 0, len = children.length; i < len; i++) {
51
+ child = children[i]
52
+
53
+ if (child.isMask) {
54
+ if (mask) {
55
+ this.__renderMask(canvas, contentCanvas, maskCanvas)
56
+ maskCanvas.clear()
57
+ contentCanvas.clear()
58
+ } else {
59
+ mask = true
60
+ }
61
+
62
+ child.__render(maskCanvas, options)
63
+ continue
64
+ }
65
+
66
+ child.__render(contentCanvas, options)
67
+ }
68
+
69
+ this.__renderMask(canvas, contentCanvas, maskCanvas)
70
+ maskCanvas.recycle()
71
+ contentCanvas.recycle()
72
+
73
+ } else {
74
+
75
+ const { bounds, hideBounds } = options
76
+
77
+ for (let i = 0, len = children.length; i < len; i++) {
78
+ child = children[i]
79
+
80
+ if (bounds && !bounds.hit(child.__world, options.matrix)) continue
81
+ if (hideBounds && hideBounds.includes(child.__world, options.matrix)) continue
82
+
83
+ child.__render(canvas, options)
84
+ }
85
+
86
+ }
87
+
88
+ }
89
+
90
+ }
@@ -0,0 +1,140 @@
1
+ import { ILeafBoundsModule } from '@leafer/interface'
2
+ import { BoundsHelper } from '@leafer/math'
3
+
4
+
5
+ const { toOuterOf, copyAndSpread } = BoundsHelper
6
+
7
+ export const LeafBounds: ILeafBoundsModule = {
8
+
9
+ __updateWorldBounds(): void {
10
+
11
+ if (this.__layout.boundsChanged) {
12
+
13
+ let resize: boolean
14
+ const layout = this.__layout
15
+
16
+
17
+ if (layout.boxChanged) {
18
+
19
+ this.__updatePath()
20
+ this.__updateRenderPath()
21
+
22
+ this.__updateBoxBounds()
23
+ layout.boxChanged = false
24
+ resize = true
25
+ }
26
+
27
+
28
+ if (layout.localBoxChanged) { // position change
29
+
30
+ this.__updateLocalBoxBounds()
31
+ layout.localBoxChanged = false
32
+
33
+ if (layout.strokeSpread) layout.strokeChanged = true
34
+ if (layout.renderSpread) layout.renderChanged = true
35
+ this.parent?.__layout.boxChange()
36
+ }
37
+
38
+
39
+ if (layout.strokeChanged) {
40
+
41
+ layout.strokeSpread = this.__updateStrokeSpread()
42
+
43
+ if (layout.strokeSpread) {
44
+
45
+ if (layout.strokeBounds === layout.boxBounds) {
46
+ layout.spreadStroke()
47
+ }
48
+
49
+ this.__updateStrokeBounds()
50
+ this.__updateLocalStrokeBounds()
51
+
52
+ } else {
53
+ layout.spreadStrokeCancel()
54
+ }
55
+
56
+ layout.strokeChanged = false
57
+ if (layout.renderSpread) layout.renderChanged = true
58
+
59
+ if (this.parent) this.parent.__layout.strokeChange()
60
+ resize || (resize = true)
61
+ }
62
+
63
+
64
+ if (layout.renderChanged) {
65
+
66
+ layout.renderSpread = this.__updateRenderSpread()
67
+
68
+ if (layout.renderSpread) {
69
+
70
+ if (layout.renderBounds === layout.boxBounds || layout.renderBounds === layout.strokeBounds) {
71
+ layout.spreadRender()
72
+ }
73
+
74
+ this.__updateRenderBounds()
75
+ this.__updateLocalRenderBounds()
76
+
77
+ } else {
78
+ layout.spreadRenderCancel()
79
+ }
80
+
81
+ layout.renderChanged = false
82
+
83
+ if (this.parent) this.parent.__layout.renderChange()
84
+ }
85
+
86
+
87
+ layout.boundsChanged = false
88
+
89
+ toOuterOf(this.__layout.renderBounds, this.__world, this.__world)
90
+
91
+ if (resize) this.__onUpdateSize()
92
+
93
+ } else {
94
+ toOuterOf(this.__layout.renderBounds, this.__world, this.__world)
95
+ }
96
+
97
+ },
98
+
99
+ __updateLocalBoxBounds(): void {
100
+ toOuterOf(this.__layout.boxBounds, this.__local, this.__local)
101
+ },
102
+
103
+ __updateLocalStrokeBounds(): void {
104
+ toOuterOf(this.__layout.strokeBounds, this.__local, this.__layout.localStrokeBounds)
105
+ },
106
+
107
+ __updateLocalRenderBounds(): void {
108
+ toOuterOf(this.__layout.renderBounds, this.__local, this.__layout.localRenderBounds)
109
+ },
110
+
111
+
112
+ __updateBoxBounds(): void {
113
+ const b = this.__layout.boxBounds
114
+ const { width, height } = this.__
115
+ b.x = 0
116
+ b.y = 0
117
+ b.width = width
118
+ b.height = height
119
+ },
120
+
121
+ __updateNaturalSize(): void {
122
+ const { __: data, __layout: layout } = this
123
+ data.__naturalWidth = layout.boxBounds.width
124
+ data.__naturalHeight = layout.boxBounds.height
125
+
126
+ if (this.around) {
127
+ layout.positionChanged = layout.matrixChanged = true
128
+ this.__updateWorldMatrix()
129
+ }
130
+ },
131
+
132
+ __updateStrokeBounds(): void {
133
+ copyAndSpread(this.__layout.strokeBounds, this.__layout.boxBounds, this.__layout.strokeSpread)
134
+ },
135
+
136
+ __updateRenderBounds(): void {
137
+ copyAndSpread(this.__layout.renderBounds, this.__layout.strokeBounds, this.__layout.renderSpread)
138
+ },
139
+
140
+ }
@@ -0,0 +1,25 @@
1
+ import { ILeafDataProxyModule } from '@leafer/interface'
2
+ import { PropertyEvent } from '@leafer/event'
3
+
4
+
5
+ export const LeafDataProxy: ILeafDataProxyModule = {
6
+
7
+ __setAttr(name: string, newValue: unknown): void {
8
+ if (this.leafer && this.leafer.created) {
9
+ if (typeof newValue === 'object' || this.__.__getInput(name) !== newValue) {
10
+ this.__[name] = newValue
11
+ const { CHANGE } = PropertyEvent
12
+ const event = new PropertyEvent(CHANGE, this, name, this.__.__get(name), newValue)
13
+ if (this.hasEvent(CHANGE) && !this.isLeafer) this.emitEvent(event)
14
+ this.leafer.emitEvent(event)
15
+ }
16
+ } else {
17
+ this.__[name] = newValue
18
+ }
19
+ },
20
+
21
+ __getAttr(name: string): unknown {
22
+ return this.__.__get(name)
23
+ }
24
+
25
+ }
@@ -0,0 +1,125 @@
1
+ import { IEventListener, IEventListenerOptions, IEventListenerMap, IEventListenerItem, IEventListenerId, IEvent, IObject, IEventTarget, ILeafEventerModule } from '@leafer/interface'
2
+
3
+ const empty = {}
4
+
5
+ export const LeafEventer: ILeafEventerModule = {
6
+
7
+ on(type: string | string[], listener: IEventListener, options?: IEventListenerOptions | boolean): void {
8
+ let capture: boolean, once: boolean
9
+ if (options) {
10
+ if (typeof options === 'boolean') {
11
+ capture = options
12
+ } else {
13
+ capture = options.capture
14
+ once = options.once
15
+ }
16
+ }
17
+
18
+ let events: IEventListenerItem[]
19
+ const map = __getListenerMap(this, capture, true)
20
+ const typeList = typeof type === 'string' ? type.split(' ') : type
21
+ const item = once ? { listener, once } : { listener }
22
+
23
+ typeList.forEach(type => {
24
+ if (type) {
25
+ events = map[type]
26
+ if (events) {
27
+ if (events.findIndex(item => item.listener === listener) === -1) events.push(item)
28
+ } else {
29
+ map[type] = [item]
30
+ }
31
+ }
32
+ })
33
+ },
34
+
35
+ off(type: string | string[], listener: IEventListener, options?: IEventListenerOptions | boolean): void {
36
+ let capture: boolean
37
+ if (options) capture = typeof options === 'boolean' ? options : options.capture
38
+
39
+ let events: IEventListenerItem[], index: number
40
+ const map = __getListenerMap(this, capture)
41
+ const typeList = typeof type === 'string' ? type.split(' ') : type
42
+
43
+ typeList.forEach(type => {
44
+ if (type) {
45
+ events = map[type]
46
+ if (events) {
47
+ index = events.findIndex(item => item.listener === listener)
48
+ if (index > -1) events.splice(index, 1)
49
+ if (!events.length) delete map[type]
50
+ }
51
+ }
52
+ })
53
+ },
54
+
55
+ on_(type: string | string[], listener: IEventListener, bind?: IObject, options?: IEventListenerOptions | boolean): IEventListenerId {
56
+ if (bind) listener = listener.bind(bind)
57
+ this.on(type, listener, options)
58
+ return { type, listener, options }
59
+ },
60
+
61
+ off_(id: IEventListenerId | IEventListenerId[]): void {
62
+ if (!id) return
63
+ const list = id instanceof Array ? id : [id]
64
+ list.forEach(item => this.off(item.type, item.listener, item.options))
65
+ list.length = 0
66
+ },
67
+
68
+ once(type: string | string[], listener: IEventListener, capture?: boolean): void {
69
+ this.on(type, listener, { once: true, capture })
70
+ },
71
+
72
+ emit(type: string, event?: IEvent | IObject, capture?: boolean): void {
73
+ const map = __getListenerMap(this, capture)
74
+ const list = map[type]
75
+ if (list) {
76
+ let item: IEventListenerItem
77
+ for (let i = 0, len = list.length; i < len; i++) {
78
+ item = list[i]
79
+ item.listener(event)
80
+ if (item.once) {
81
+ this.off(type, item.listener, capture)
82
+ i--, len--
83
+ }
84
+ if (event && (event as IEvent).isStopNow) break
85
+ }
86
+ }
87
+ },
88
+
89
+ emitEvent(event: IEvent, capture?: boolean): void {
90
+ event.current = this
91
+ this.emit(event.type, event, capture)
92
+ },
93
+
94
+ hasEvent(type: string, capture?: boolean): boolean {
95
+ const { __bubbleMap: b, __captureMap: c } = this
96
+ if (capture === undefined) {
97
+ return !!((c && c[type]) || (b && b[type]))
98
+ } else {
99
+ return !!(capture ? (c && c[type]) : (b && b[type]))
100
+ }
101
+ },
102
+
103
+ }
104
+
105
+ function __getListenerMap(eventer: IEventTarget, capture?: boolean, create?: boolean): IEventListenerMap {
106
+ if (capture) {
107
+
108
+ const { __captureMap: c } = eventer
109
+ if (c) {
110
+ return c
111
+ } else {
112
+ return create ? eventer.__captureMap = {} : empty
113
+ }
114
+
115
+ } else {
116
+
117
+ const { __bubbleMap: b } = eventer
118
+ if (b) {
119
+ return b
120
+ } else {
121
+ return create ? eventer.__bubbleMap = {} : empty
122
+ }
123
+
124
+ }
125
+ }
package/src/LeafHit.ts ADDED
@@ -0,0 +1,29 @@
1
+ import { ILeafHitModule, IRadiusPointData, ILeaferCanvas } from '@leafer/interface'
2
+ import { PointHelper } from '@leafer/math'
3
+
4
+
5
+ const { toInnerRadiusPointOf, copy, setRadius } = PointHelper
6
+ const inner = {} as IRadiusPointData
7
+
8
+ export const LeafHit: ILeafHitModule = {
9
+
10
+ __hitWorld(point: IRadiusPointData): boolean {
11
+ if (this.__layout.hitCanvasChanged || !this.__hitCanvas) {
12
+ this.__updateHitCanvas()
13
+ this.__layout.hitCanvasChanged = false
14
+ }
15
+
16
+ if (this.__.hitRadius) {
17
+ copy(inner, point), point = inner
18
+ setRadius(point, this.__.hitRadius)
19
+ }
20
+
21
+ toInnerRadiusPointOf(point, this.__world, inner)
22
+ return this.__hit(inner)
23
+ },
24
+
25
+ __drawHitPath(canvas: ILeaferCanvas): void {
26
+ this.__drawRenderPath(canvas)
27
+ }
28
+
29
+ }
@@ -0,0 +1,38 @@
1
+ import { ILeaf, ILeaferCanvas, ILeafMaskModule } from '@leafer/interface'
2
+
3
+
4
+ export const LeafMask: ILeafMaskModule = {
5
+
6
+ __updateEraser(value?: boolean): void {
7
+ this.__hasEraser = value ? true : this.children.some(item => item.__.isEraser)
8
+ },
9
+
10
+ __updateMask(value?: boolean): void {
11
+ this.__hasMask = value ? true : this.children.some(item => item.__.isMask)
12
+ },
13
+
14
+ __renderMask(canvas: ILeaferCanvas, content: ILeaferCanvas, mask: ILeaferCanvas): void {
15
+ content.resetTransform()
16
+ content.useMask(mask)
17
+ canvas.resetTransform()
18
+ canvas.opacity = this.__worldOpacity
19
+ canvas.copyWorld(content)
20
+ },
21
+
22
+ __removeMask(child?: ILeaf): void {
23
+ if (child) {
24
+ child.isMask = false
25
+ this.remove(child)
26
+ } else {
27
+ const { children } = this
28
+ for (let i = 0, len = children.length; i < len; i++) {
29
+ child = children[i]
30
+ if (child.isMask) {
31
+ this.__removeMask(child)
32
+ len--, i--
33
+ }
34
+ }
35
+ }
36
+ }
37
+
38
+ }
@@ -0,0 +1,123 @@
1
+ import { ILeafMatrixModule, IPointData } from '@leafer/interface'
2
+ import { OneRadian, MatrixHelper } from '@leafer/math'
3
+
4
+
5
+ const { sin, cos } = Math
6
+ const defaultWorld = { ...MatrixHelper.defaultMatrix, scaleX: 1, scaleY: 1, rotation: 0, skewX: 0, skewY: 0 }
7
+ const defaultCenter: IPointData = { x: 0.5, y: 0.5 }
8
+
9
+ export const LeafMatrix: ILeafMatrixModule = {
10
+
11
+ __updateWorldMatrix(): void {
12
+
13
+ const pw = this.parent ? this.parent.__world : defaultWorld
14
+ const r = this.__local
15
+ const w = this.__world
16
+
17
+ if (this.__layout.matrixChanged) this.__updateLocalMatrix()
18
+
19
+ if (this.__layout.affectScaleOrRotation) {
20
+ w.a = r.a * pw.a + r.b * pw.c
21
+ w.b = r.a * pw.b + r.b * pw.d
22
+ w.c = r.c * pw.a + r.d * pw.c
23
+ w.d = r.c * pw.b + r.d * pw.d
24
+ w.e = r.e * pw.a + r.f * pw.c + pw.e
25
+ w.f = r.e * pw.b + r.f * pw.d + pw.f
26
+
27
+ const data = this.__
28
+ w.scaleX = pw.scaleX * data.scaleX
29
+ w.scaleY = pw.scaleY * data.scaleY
30
+
31
+ w.rotation = pw.rotation + data.rotation
32
+ w.skewX = pw.skewX + data.skewX
33
+ w.skewY = pw.skewY + data.skewY
34
+ } else {
35
+ w.a = pw.a
36
+ w.b = pw.b
37
+ w.c = pw.c
38
+ w.d = pw.d
39
+ w.e = r.e * pw.a + r.f * pw.c + pw.e
40
+ w.f = r.e * pw.b + r.f * pw.d + pw.f
41
+
42
+ w.scaleX = pw.scaleX
43
+ w.scaleY = pw.scaleY
44
+
45
+ w.rotation = pw.rotation
46
+ w.skewX = pw.skewX
47
+ w.skewY = pw.skewY
48
+ }
49
+ },
50
+
51
+ __updateLocalMatrix(): void {
52
+
53
+ const r = this.__local
54
+ const layout = this.__layout
55
+
56
+ if (layout.affectScaleOrRotation) {
57
+
58
+ const { scaleX, scaleY } = this.__
59
+
60
+ if (layout.affectRotation) {
61
+
62
+ if (layout.scaleChanged || layout.rotationChanged) {
63
+
64
+ let { rotation, skewX, skewY } = this.__
65
+
66
+ if (rotation || skewX || skewY) {
67
+
68
+ rotation *= OneRadian
69
+ if (skewX) skewX *= OneRadian
70
+ if (skewY) skewY *= OneRadian
71
+
72
+ r.a = scaleX * cos(rotation + skewY)
73
+ r.b = scaleX * sin(rotation + skewY)
74
+ r.c = scaleY * -sin(rotation - skewX)
75
+ r.d = scaleY * cos(rotation - skewX)
76
+
77
+ } else {
78
+
79
+ r.a = scaleX
80
+ r.b = 0
81
+ r.c = 0
82
+ r.d = scaleY
83
+
84
+ layout.affectRotation = false
85
+ }
86
+
87
+ layout.scaleChanged = false
88
+ layout.rotationChanged = false
89
+
90
+ }
91
+
92
+ } else {
93
+
94
+ if (layout.scaleChanged) {
95
+ r.a = scaleX
96
+ r.d = scaleY
97
+ layout.scaleChanged = false
98
+ }
99
+
100
+ }
101
+
102
+ }
103
+
104
+ if (layout.positionChanged) {
105
+ r.e = this.__.x
106
+ r.f = this.__.y
107
+ const { width, height, around } = this.__
108
+ if (around && width && height) {
109
+ const origin = (around === 'center') ? defaultCenter : around
110
+ const offsetX = width * origin.x, offsetY = height * origin.y
111
+ r.e -= offsetX * r.a + offsetY * r.c
112
+ r.f -= offsetX * r.b + offsetY * r.d
113
+ }
114
+ layout.positionChanged = false
115
+ }
116
+
117
+ this.__layout.matrixChanged = false
118
+ }
119
+
120
+
121
+ }
122
+
123
+
@@ -0,0 +1,34 @@
1
+ import { ILeaferCanvas, IRenderOptions, ILeafRenderModule } from '@leafer/interface'
2
+
3
+
4
+ export const LeafRender: ILeafRenderModule = {
5
+
6
+ __render(canvas: ILeaferCanvas, options: IRenderOptions): void {
7
+ if (this.__worldOpacity) {
8
+ canvas.setWorld(this.__world, options.matrix)
9
+ canvas.opacity = this.__worldOpacity
10
+
11
+ if (this.__.__single) {
12
+ const tempCanvas = canvas.getSameCanvas(true)
13
+
14
+ this.__draw(tempCanvas, options)
15
+
16
+ const blendMode = this.__.isEraser ? 'destination-out' : this.__.blendMode
17
+ if (options.matrix || this.__hasMirror) {
18
+ canvas.copyWorldByReset(tempCanvas, null, null, blendMode)
19
+ } else {
20
+ canvas.copyWorldToInner(tempCanvas, this.__world, this.__layout.renderBounds, blendMode)
21
+ }
22
+ tempCanvas.recycle()
23
+ } else {
24
+ this.__draw(canvas, options)
25
+ }
26
+ }
27
+ },
28
+
29
+ __updateWorldOpacity(): void {
30
+ this.__worldOpacity = this.__.visible ? (this.parent ? this.parent.__worldOpacity * this.__.opacity : this.__.opacity) : 0
31
+ if (this.__layout.opacityChanged) this.__layout.opacityChanged = false
32
+ }
33
+
34
+ }