@leafer/canvas 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.
package/package.json CHANGED
@@ -1,12 +1,13 @@
1
1
  {
2
2
  "name": "@leafer/canvas",
3
- "version": "1.0.0-beta.15",
3
+ "version": "1.0.0-beta.17",
4
4
  "description": "@leafer/canvas",
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,15 +22,15 @@
21
22
  "leaferjs"
22
23
  ],
23
24
  "dependencies": {
24
- "@leafer/file": "1.0.0-beta.15",
25
- "@leafer/list": "1.0.0-beta.15",
26
- "@leafer/math": "1.0.0-beta.15",
27
- "@leafer/data": "1.0.0-beta.15",
28
- "@leafer/path": "1.0.0-beta.15",
29
- "@leafer/debug": "1.0.0-beta.15",
30
- "@leafer/platform": "1.0.0-beta.15"
25
+ "@leafer/file": "1.0.0-beta.17",
26
+ "@leafer/list": "1.0.0-beta.17",
27
+ "@leafer/math": "1.0.0-beta.17",
28
+ "@leafer/data": "1.0.0-beta.17",
29
+ "@leafer/path": "1.0.0-beta.17",
30
+ "@leafer/debug": "1.0.0-beta.17",
31
+ "@leafer/platform": "1.0.0-beta.17"
31
32
  },
32
33
  "devDependencies": {
33
- "@leafer/interface": "1.0.0-beta.15"
34
+ "@leafer/interface": "1.0.0-beta.17"
34
35
  }
35
36
  }
package/src/Canvas.ts ADDED
@@ -0,0 +1,289 @@
1
+ import { ICanvasAttr, ITextMetrics, ICanvasContext2D, IPath2D, IObject, InnerId, IMatrixData, IFunction, IWindingRule, IBlendMode } from '@leafer/interface'
2
+
3
+ function contextAttr(realName?: string) {
4
+ return (target: Canvas, key: string) => {
5
+ if (!realName) realName = key
6
+ const property: IObject & ThisType<Canvas> = {
7
+ get() { return (this.context as IObject)[realName] },
8
+ set(value: unknown) { (this.context as IObject)[realName] = value }
9
+ }
10
+ Object.defineProperty(target, key, property)
11
+ }
12
+ }
13
+
14
+ const contextMethodNameList: string[] = []
15
+ function contextMethod() {
16
+ return (_target: Canvas, key: string) => {
17
+ contextMethodNameList.push(key)
18
+ }
19
+ }
20
+
21
+ const emptyArray: number[] = []
22
+
23
+
24
+ export class Canvas implements ICanvasAttr {
25
+
26
+ public readonly innerId: InnerId
27
+
28
+ public width: number
29
+ public height: number
30
+
31
+ public context: ICanvasContext2D
32
+
33
+ // canvas attr
34
+
35
+ @contextAttr('imageSmoothingEnabled')
36
+ public smooth: boolean
37
+
38
+ @contextAttr('imageSmoothingQuality')
39
+ public smoothLevel: ImageSmoothingQuality
40
+
41
+ @contextAttr('globalAlpha')
42
+ public opacity: number
43
+
44
+ public set blendMode(value: IBlendMode) {
45
+ if (value === 'normal') value = 'source-over'
46
+ this.context.globalCompositeOperation = value as any
47
+ }
48
+
49
+ public get blendMode(): IBlendMode {
50
+ return this.context.globalCompositeOperation as IBlendMode
51
+ }
52
+
53
+ @contextAttr()
54
+ public fillStyle: string | object
55
+
56
+ @contextAttr()
57
+ public strokeStyle: string | object
58
+
59
+
60
+ @contextAttr('lineWidth')
61
+ public strokeWidth: number
62
+
63
+ @contextAttr('lineCap')
64
+ public strokeCap: string
65
+
66
+ @contextAttr('lineJoin')
67
+ public strokeJoin: string
68
+
69
+ public set dashPattern(value: number[]) {
70
+ this.context.setLineDash(value || emptyArray)
71
+ }
72
+ public get dashPattern(): number[] {
73
+ return this.context.getLineDash()
74
+ }
75
+
76
+ @contextAttr('lineDashOffset')
77
+ public dashOffset: number
78
+
79
+ @contextAttr()
80
+ public miterLimit: number
81
+
82
+
83
+ @contextAttr()
84
+ public shadowBlur: number
85
+
86
+ @contextAttr()
87
+ public shadowColor: string
88
+
89
+ @contextAttr()
90
+ public shadowOffsetX: number
91
+
92
+ @contextAttr()
93
+ public shadowOffsetY: number
94
+
95
+ @contextAttr()
96
+ public filter: string
97
+
98
+
99
+ @contextAttr()
100
+ public font: string
101
+
102
+ @contextAttr()
103
+ public fontKerning: string
104
+
105
+ @contextAttr()
106
+ public fontStretch: string
107
+
108
+ @contextAttr()
109
+ public fontVariantCaps: string
110
+
111
+
112
+ @contextAttr()
113
+ public textAlign: string
114
+
115
+ @contextAttr()
116
+ public textBaseline: string
117
+
118
+ @contextAttr()
119
+ public textRendering: string
120
+
121
+ @contextAttr()
122
+ public wordSpacing: string
123
+
124
+ @contextAttr()
125
+ public letterSpacing: string
126
+
127
+
128
+ @contextAttr()
129
+ public direction: string
130
+
131
+ // end
132
+
133
+ public __bindContext(): void {
134
+ let method: IFunction
135
+ contextMethodNameList.forEach(name => {
136
+ method = (this.context as IObject)[name]
137
+ if (method) (this as IObject)[name] = method.bind(this.context)
138
+ })
139
+ this.textBaseline = "alphabetic"
140
+ }
141
+
142
+ // canvas method
143
+
144
+ @contextMethod()
145
+ public setTransform(_a: number | IMatrixData, _b?: number, _c?: number, _d?: number, _e?: number, _f?: number): void { }
146
+
147
+ @contextMethod()
148
+ public resetTransform(): void { }
149
+
150
+ @contextMethod()
151
+ public getTransform(): IMatrixData { return void 0 }
152
+
153
+ @contextMethod()
154
+ public save(): void { }
155
+
156
+ @contextMethod()
157
+ public restore(): void { }
158
+
159
+ public transform(a: number | IMatrixData, b?: number, c?: number, d?: number, e?: number, f?: number): void {
160
+ if (typeof a === 'object') {
161
+ this.context.transform(a.a, a.b, a.c, a.d, a.e, a.f)
162
+ } else {
163
+ this.context.transform(a, b, c, d, e, f)
164
+ }
165
+ }
166
+
167
+ @contextMethod()
168
+ public translate(_x: number, _y: number): void { }
169
+
170
+ @contextMethod()
171
+ public scale(_x: number, _y: number): void { }
172
+
173
+ @contextMethod()
174
+ public rotate(_angle: number): void { }
175
+
176
+ @contextMethod()
177
+ public fill(_path2d?: IPath2D | IWindingRule, _rule?: IWindingRule): void { }
178
+
179
+ @contextMethod()
180
+ public stroke(_path2d?: IPath2D): void { }
181
+
182
+ @contextMethod()
183
+ public clip(_path2d?: IPath2D | IWindingRule, _rule?: IWindingRule): void { }
184
+
185
+ @contextMethod()
186
+ public fillRect(_x: number, _y: number, _width: number, _height: number): void { }
187
+
188
+ @contextMethod()
189
+ public strokeRect(_x: number, _y: number, _width: number, _height: number): void { }
190
+
191
+ @contextMethod()
192
+ public clearRect(_x: number, _y: number, _width: number, _height: number): void { }
193
+
194
+ public drawImage(image: CanvasImageSource, sx: number, sy: number, sw?: number, sh?: number, dx?: number, dy?: number, dw?: number, dh?: number): void {
195
+ switch (arguments.length) {
196
+ case 9:
197
+
198
+ // safari: drawimage裁剪画布外的坐标会有问题, 必须是不小于0的坐标点
199
+ if (sx < 0) {
200
+ const d = (-sx / sw) * dw
201
+ sw += sx
202
+ sx = 0
203
+ dx += d
204
+ dw -= d
205
+ }
206
+
207
+ if (sy < 0) {
208
+ const d = (-sy / sh) * dh
209
+ sh += sy
210
+ sy = 0
211
+ dy += d
212
+ dh -= d
213
+ }
214
+
215
+ this.context.drawImage(image, sx, sy, sw, sh, dx, dy, dw, dh)
216
+ break
217
+ case 5:
218
+ this.context.drawImage(image, sx, sy, sw, sh) // = dx, dy, dw, dh
219
+ break
220
+ case 3:
221
+ this.context.drawImage(image, sx, sy)
222
+ }
223
+ }
224
+
225
+
226
+ // canvas draw
227
+ @contextMethod()
228
+ public beginPath(): void { }
229
+
230
+ @contextMethod()
231
+ public moveTo(_x: number, _y: number): void { }
232
+
233
+ @contextMethod()
234
+ public lineTo(_x: number, _y: number): void { }
235
+
236
+ @contextMethod()
237
+ public bezierCurveTo(_cp1x: number, _cp1y: number, _cp2x: number, _cp2y: number, _x: number, _y: number): void { }
238
+
239
+ @contextMethod()
240
+ public quadraticCurveTo(_cpx: number, _cpy: number, _x: number, _y: number): void { }
241
+
242
+ @contextMethod()
243
+ public closePath(): void { }
244
+
245
+ @contextMethod()
246
+ public arc(_x: number, _y: number, _radius: number, _startAngle: number, _endAngle: number, _anticlockwise?: boolean): void { }
247
+
248
+ @contextMethod()
249
+ public arcTo(_x1: number, _y1: number, _x2: number, _y2: number, _radius: number): void { }
250
+
251
+ @contextMethod()
252
+ public ellipse(_x: number, _y: number, _radiusX: number, _radiusY: number, _rotation: number, _startAngle: number, _endAngle: number, _anticlockwise?: boolean): void { }
253
+
254
+ @contextMethod()
255
+ public rect(_x: number, _y: number, _width: number, _height: number): void { }
256
+
257
+ @contextMethod()
258
+ public roundRect(_x: number, _y: number, _width: number, _height: number, _radius?: number | number[]): void { }
259
+
260
+ // end canvas draw
261
+
262
+ // paint
263
+
264
+ @contextMethod()
265
+ public createConicGradient(_startAngle: number, _x: number, _y: number): CanvasGradient { return void 0 }
266
+
267
+ @contextMethod()
268
+ public createLinearGradient(_x0: number, _y0: number, _x1: number, _y1: number): CanvasGradient { return void 0 }
269
+
270
+ @contextMethod()
271
+ public createPattern(_image: CanvasImageSource, _repetition: string | null): CanvasPattern | null { return void 0 }
272
+
273
+ @contextMethod()
274
+ public createRadialGradient(_x0: number, _y0: number, _r0: number, _x1: number, _y1: number, _r1: number): CanvasGradient { return void 0 }
275
+
276
+ // text
277
+ @contextMethod()
278
+ public fillText(_text: string, _x: number, _y: number, _maxWidth?: number): void { }
279
+
280
+ @contextMethod()
281
+ public measureText(_text: string): ITextMetrics { return void 0 }
282
+
283
+ @contextMethod()
284
+ public strokeText(_text: string, _x: number, _y: number, _maxWidth?: number): void { }
285
+
286
+ public destroy(): void {
287
+ this.context = null
288
+ }
289
+ }
@@ -0,0 +1,56 @@
1
+ import { ILeaferCanvas, IScreenSizeData, ICanvasManager } from '@leafer/interface'
2
+ import { Creator } from '@leafer/platform'
3
+
4
+ export class CanvasManager implements ICanvasManager {
5
+
6
+ public list: ILeaferCanvas[] = []
7
+
8
+ public add(canvas: ILeaferCanvas): void {
9
+ canvas.manager = this
10
+ this.list.push(canvas)
11
+ }
12
+
13
+ public get(size: IScreenSizeData): ILeaferCanvas {
14
+ let old: ILeaferCanvas
15
+ const { list } = this
16
+ for (let i = 0, len = list.length; i < len; i++) {
17
+ old = list[i]
18
+ if (old.recycled && old.isSameSize(size)) {
19
+ old.recycled = false
20
+ old.manager || (old.manager = this)
21
+ return old
22
+ }
23
+ }
24
+
25
+ const canvas = Creator.canvas(size)
26
+ this.add(canvas)
27
+ return canvas
28
+ }
29
+
30
+ public recycle(old: ILeaferCanvas): void {
31
+ if (!old.recycled) {
32
+ old.clear()
33
+ old.recycled = true
34
+ }
35
+ }
36
+
37
+ public clearRecycled(): void {
38
+ let canvas: ILeaferCanvas
39
+ const filter: ILeaferCanvas[] = []
40
+ for (let i = 0, len = this.list.length; i < len; i++) {
41
+ canvas = this.list[i]
42
+ canvas.recycled ? canvas.destroy() : filter.push(canvas)
43
+ }
44
+ this.list = filter
45
+ }
46
+
47
+ public clear(): void {
48
+ this.list.forEach(item => { item.destroy() })
49
+ this.list.length = 0
50
+ }
51
+
52
+ public destroy(): void {
53
+ this.clear()
54
+ }
55
+
56
+ }
@@ -0,0 +1,48 @@
1
+ import { IScreenSizeData, IHitCanvasManager, ILeaf, IHitCanvas, ILeafList } from '@leafer/interface'
2
+ import { LeafList } from '@leafer/list'
3
+ import { Creator } from '@leafer/platform'
4
+
5
+ import { CanvasManager } from './CanvasManager'
6
+
7
+
8
+ export class HitCanvasManager extends CanvasManager implements IHitCanvasManager {
9
+
10
+ protected pathTypeList: ILeafList = new LeafList()
11
+ protected imageTypeList: ILeafList = new LeafList()
12
+
13
+ public getImageType(leaf: ILeaf, size: IScreenSizeData): IHitCanvas {
14
+ this.imageTypeList.push(leaf)
15
+ return Creator.hitCanvas(size)
16
+ }
17
+
18
+ public getPathType(leaf: ILeaf): IHitCanvas {
19
+ this.pathTypeList.push(leaf)
20
+ return Creator.hitCanvas()
21
+ }
22
+
23
+ public clearImageType(): void {
24
+ this.__clearLeafList(this.imageTypeList)
25
+ }
26
+
27
+ public clearPathType(): void {
28
+ this.__clearLeafList(this.pathTypeList)
29
+ }
30
+
31
+ protected __clearLeafList(leafList: ILeafList): void {
32
+ if (leafList.length) {
33
+ leafList.forEach(leaf => {
34
+ if (leaf.__hitCanvas) {
35
+ leaf.__hitCanvas.destroy()
36
+ leaf.__hitCanvas = null
37
+ }
38
+ })
39
+ leafList.reset()
40
+ }
41
+ }
42
+
43
+ public clear(): void {
44
+ this.clearPathType()
45
+ this.clearImageType()
46
+ }
47
+
48
+ }
@@ -0,0 +1,366 @@
1
+ import { IBounds, ILeaferCanvas, ICanvasStrokeOptions, ILeaferCanvasConfig, IMatrixData, IBoundsData, IAutoBounds, IScreenSizeData, IResizeEventListener, IMatrixWithBoundsData, IPointData, InnerId, ICanvasManager, IWindingRule, IBlendMode, IExportImageType, IExportFileType, IBlob, ICursorType } from '@leafer/interface'
2
+ import { Bounds, BoundsHelper, IncrementId } from '@leafer/math'
3
+ import { Creator, Platform } from '@leafer/platform'
4
+ import { DataHelper } from '@leafer/data'
5
+ import { FileHelper } from '@leafer/file'
6
+ import { Debug } from '@leafer/debug'
7
+
8
+ import { Canvas } from './Canvas'
9
+
10
+
11
+ const temp = new Bounds()
12
+ const minSize: IScreenSizeData = { width: 1, height: 1, pixelRatio: 1 }
13
+ const debug = Debug.get('LeaferCanvasBase')
14
+
15
+ export const canvasSizeAttrs = ['width', 'height', 'pixelRatio']
16
+
17
+ export class LeaferCanvasBase extends Canvas implements ILeaferCanvas {
18
+
19
+ declare public readonly innerId: InnerId
20
+
21
+ public name: string
22
+
23
+ public manager: ICanvasManager
24
+
25
+ public pixelRatio: number
26
+ public get pixelWidth(): number { return this.width * this.pixelRatio }
27
+ public get pixelHeight(): number { return this.height * this.pixelRatio }
28
+
29
+ public get allowBackgroundColor(): boolean { return this.view && this.parentView }
30
+
31
+ public bounds: IBounds
32
+ public clientBounds: IBoundsData
33
+
34
+ public config: ILeaferCanvasConfig
35
+
36
+ public autoLayout: boolean
37
+
38
+ public view: any
39
+ public parentView: any
40
+
41
+ public unreal?: boolean
42
+
43
+ public recycled?: boolean
44
+
45
+ public worldTransform: IMatrixData = {} as IMatrixData
46
+
47
+ protected savedBlendMode: IBlendMode
48
+
49
+ constructor(config?: ILeaferCanvasConfig, manager?: ICanvasManager) {
50
+ super()
51
+ if (!config) config = minSize
52
+ if (!config.pixelRatio) config.pixelRatio = Platform.devicePixelRatio
53
+
54
+ this.manager = manager
55
+ this.innerId = IncrementId.create(IncrementId.CNAVAS)
56
+
57
+ const { width, height, pixelRatio } = config
58
+ this.autoLayout = !width || !height
59
+
60
+ this.pixelRatio = pixelRatio
61
+ this.config = config
62
+
63
+ this.init()
64
+ }
65
+
66
+ public init(): void { }
67
+
68
+ public __createContext(): void {
69
+ this.context = this.view.getContext('2d')
70
+ this.__bindContext()
71
+ }
72
+
73
+ public toBlob(type?: IExportFileType, quality?: number): Promise<IBlob> {
74
+ return new Promise((resolve) => {
75
+ const canvas = this.getSaveCanvas(type)
76
+ Platform.origin.canvasToBolb(canvas.view, type, quality).then((blob) => {
77
+ canvas.recycle()
78
+ resolve(blob)
79
+ }).catch((e) => {
80
+ debug.error(e)
81
+ resolve(null)
82
+ })
83
+ })
84
+ }
85
+
86
+ public toDataURL(type?: IExportImageType, quality?: number): string | Promise<string> {
87
+ const canvas = this.getSaveCanvas(type)
88
+ const data = Platform.origin.canvasToDataURL(canvas.view, type, quality)
89
+ canvas.recycle()
90
+ return data
91
+ }
92
+
93
+ public saveAs(filename: string, quality?: number): Promise<boolean> {
94
+ return new Promise((resolve) => {
95
+ const canvas = this.getSaveCanvas(FileHelper.fileType(filename))
96
+ Platform.origin.canvasSaveAs(canvas.view, filename, quality).then(() => {
97
+ canvas.recycle()
98
+ resolve(true)
99
+ }).catch((e) => {
100
+ debug.error(e)
101
+ resolve(false)
102
+ })
103
+ })
104
+ }
105
+
106
+ public getSaveCanvas(type: string): ILeaferCanvas {
107
+ const { backgroundColor, bounds } = this as ILeaferCanvas
108
+ const canvas = this.getSameCanvas()
109
+ if (['jpg', 'jpeg'].includes(type)) canvas.fillWorld(bounds, '#FFFFFF')
110
+ if (backgroundColor) canvas.fillWorld(bounds, backgroundColor)
111
+ canvas.copyWorld(this)
112
+ return canvas
113
+ }
114
+
115
+
116
+ public resize(size: IScreenSizeData): void {
117
+ if (this.isSameSize(size)) return
118
+
119
+ let takeCanvas: ILeaferCanvas
120
+ if (this.context && !this.unreal && this.width) {
121
+ takeCanvas = this.getSameCanvas()
122
+ takeCanvas.copyWorld(this)
123
+ }
124
+
125
+ DataHelper.copyAttrs(this, size, canvasSizeAttrs)
126
+ this.bounds = new Bounds(0, 0, this.width, this.height)
127
+ this.pixelRatio || (this.pixelRatio = 1)
128
+
129
+ if (!this.unreal) {
130
+ this.updateViewSize()
131
+ this.smooth = this.config.smooth
132
+ }
133
+
134
+ this.updateClientBounds()
135
+
136
+ if (this.context && !this.unreal && takeCanvas) {
137
+ this.clearWorld(takeCanvas.bounds)
138
+ this.copyWorld(takeCanvas)
139
+ takeCanvas.recycle()
140
+ }
141
+ }
142
+
143
+ public updateViewSize(): void { }
144
+ public updateClientBounds(): void { }
145
+
146
+ public startAutoLayout(_autoBounds: IAutoBounds, _listener: IResizeEventListener): void { }
147
+ public stopAutoLayout(): void { }
148
+
149
+ public setCursor(_cursor: ICursorType | ICursorType[]): void { }
150
+
151
+ public setWorld(matrix: IMatrixData, parentMatrix?: IMatrixData, onlyTranslate?: boolean): void {
152
+ const { pixelRatio } = this
153
+ const w = this.worldTransform
154
+ if (parentMatrix) {
155
+
156
+ if (onlyTranslate) {
157
+ this.setTransform(
158
+ w.a = matrix.a * pixelRatio,
159
+ w.b = matrix.b * pixelRatio,
160
+ w.c = matrix.c * pixelRatio,
161
+ w.d = matrix.d * pixelRatio,
162
+ w.e = (matrix.e + parentMatrix.e) * pixelRatio,
163
+ w.f = (matrix.f + parentMatrix.f) * pixelRatio
164
+ )
165
+ } else {
166
+ const { a, b, c, d, e, f } = parentMatrix
167
+ this.setTransform(
168
+ w.a = ((matrix.a * a) + (matrix.b * c)) * pixelRatio,
169
+ w.b = ((matrix.a * b) + (matrix.b * d)) * pixelRatio,
170
+ w.c = ((matrix.c * a) + (matrix.d * c)) * pixelRatio,
171
+ w.d = ((matrix.c * b) + (matrix.d * d)) * pixelRatio,
172
+ w.e = (((matrix.e * a) + (matrix.f * c) + e)) * pixelRatio,
173
+ w.f = (((matrix.e * b) + (matrix.f * d) + f)) * pixelRatio
174
+ )
175
+ }
176
+
177
+ } else {
178
+
179
+ this.setTransform(
180
+ w.a = matrix.a * pixelRatio,
181
+ w.b = matrix.b * pixelRatio,
182
+ w.c = matrix.c * pixelRatio,
183
+ w.d = matrix.d * pixelRatio,
184
+ w.e = matrix.e * pixelRatio,
185
+ w.f = matrix.f * pixelRatio
186
+ )
187
+ }
188
+ }
189
+
190
+ public useWorldTransform(worldTransform?: IMatrixData): void {
191
+ if (worldTransform) this.worldTransform = worldTransform
192
+ const w = this.worldTransform
193
+ if (w) this.setTransform(w.a, w.b, w.c, w.d, w.e, w.f)
194
+ }
195
+
196
+ public setStroke(color: string | object, strokeWidth: number, options?: ICanvasStrokeOptions): void {
197
+ if (strokeWidth) this.strokeWidth = strokeWidth
198
+ if (color) this.strokeStyle = color
199
+ if (options) this.setStrokeOptions(options)
200
+ }
201
+
202
+ public setStrokeOptions(options: ICanvasStrokeOptions): void {
203
+ this.strokeCap = options.strokeCap
204
+ this.strokeJoin = options.strokeJoin
205
+ this.dashPattern = options.dashPattern
206
+ this.dashOffset = options.dashOffset
207
+ this.miterLimit = options.miterLimit
208
+ }
209
+
210
+ public saveBlendMode(blendMode: IBlendMode): void {
211
+ this.savedBlendMode = this.blendMode
212
+ this.blendMode = blendMode
213
+ }
214
+
215
+ public restoreBlendMode(): void {
216
+ this.blendMode = this.savedBlendMode
217
+ }
218
+
219
+ public hitFill(point: IPointData, fillRule?: IWindingRule): boolean {
220
+ return fillRule ? this.context.isPointInPath(point.x, point.y, fillRule) : this.context.isPointInPath(point.x, point.y)
221
+ }
222
+
223
+ public hitStroke(point: IPointData, strokeWidth?: number): boolean {
224
+ this.strokeWidth = strokeWidth
225
+ return this.context.isPointInStroke(point.x, point.y)
226
+ }
227
+
228
+
229
+ public setWorldShadow(x: number, y: number, blur: number, color?: string): void {
230
+ const { pixelRatio } = this
231
+ this.shadowOffsetX = x * pixelRatio
232
+ this.shadowOffsetY = y * pixelRatio
233
+ this.shadowBlur = blur * pixelRatio
234
+ this.shadowColor = color || 'black'
235
+ }
236
+
237
+ public setWorldBlur(blur: number): void {
238
+ const { pixelRatio } = this
239
+ this.filter = `blur(${blur * pixelRatio}px)`
240
+ }
241
+
242
+
243
+ public copyWorld(canvas: ILeaferCanvas, from?: IBoundsData, to?: IBoundsData, blendMode?: IBlendMode): void {
244
+ if (blendMode) this.blendMode = blendMode
245
+ if (from) {
246
+ const { pixelRatio } = this
247
+ if (!to) to = from
248
+ this.drawImage(canvas.view as HTMLCanvasElement, from.x * pixelRatio, from.y * pixelRatio, from.width * pixelRatio, from.height * pixelRatio, to.x * pixelRatio, to.y * pixelRatio, to.width * pixelRatio, to.height * pixelRatio)
249
+ } else {
250
+ this.drawImage(canvas.view as HTMLCanvasElement, 0, 0)
251
+ }
252
+ if (blendMode) this.blendMode = 'source-over'
253
+ }
254
+
255
+ public copyWorldToInner(canvas: ILeaferCanvas, fromWorld: IMatrixWithBoundsData, toInnerBounds: IBoundsData, blendMode?: IBlendMode): void {
256
+ if (blendMode) this.blendMode = blendMode
257
+ if (fromWorld.b || fromWorld.c) {
258
+ this.save()
259
+ this.resetTransform()
260
+ this.copyWorld(canvas, fromWorld, BoundsHelper.tempToOuterOf(toInnerBounds, fromWorld))
261
+ this.restore()
262
+ } else {
263
+ const { pixelRatio } = this
264
+ this.drawImage(canvas.view as HTMLCanvasElement, fromWorld.x * pixelRatio, fromWorld.y * pixelRatio, fromWorld.width * pixelRatio, fromWorld.height * pixelRatio, toInnerBounds.x, toInnerBounds.y, toInnerBounds.width, toInnerBounds.height)
265
+ }
266
+ if (blendMode) this.blendMode = 'source-over'
267
+ }
268
+
269
+ public copyWorldByReset(canvas: ILeaferCanvas, from?: IBoundsData, to?: IBoundsData, blendMode?: IBlendMode): void {
270
+ this.resetTransform()
271
+ this.copyWorld(canvas, from, to, blendMode)
272
+ this.useWorldTransform()
273
+ }
274
+
275
+ public useMask(maskCanvas: ILeaferCanvas, fromBounds?: IBoundsData, toBounds?: IBoundsData): void {
276
+ this.copyWorld(maskCanvas, fromBounds, toBounds, 'destination-in')
277
+ }
278
+
279
+ public useEraser(eraserCanvas: ILeaferCanvas, fromBounds?: IBoundsData, toBounds?: IBoundsData): void {
280
+ this.copyWorld(eraserCanvas, fromBounds, toBounds, 'destination-out')
281
+ }
282
+
283
+ public fillWorld(bounds: IBoundsData, color: string | object, blendMode?: IBlendMode): void {
284
+ if (blendMode) this.blendMode = blendMode
285
+ this.fillStyle = color
286
+ temp.copy(bounds).scale(this.pixelRatio)
287
+ this.fillRect(temp.x, temp.y, temp.width, temp.height)
288
+ if (blendMode) this.blendMode = 'source-over'
289
+ }
290
+
291
+ public strokeWorld(bounds: IBoundsData, color: string | object, blendMode?: IBlendMode): void {
292
+ if (blendMode) this.blendMode = blendMode
293
+ this.strokeStyle = color
294
+ temp.copy(bounds).scale(this.pixelRatio)
295
+ this.strokeRect(temp.x, temp.y, temp.width, temp.height)
296
+ if (blendMode) this.blendMode = 'source-over'
297
+ }
298
+
299
+ public clearWorld(bounds: IBoundsData, ceilPixel?: boolean): void {
300
+ temp.copy(bounds).scale(this.pixelRatio)
301
+ if (ceilPixel) temp.ceil()
302
+ this.clearRect(temp.x, temp.y, temp.width, temp.height)
303
+ }
304
+
305
+ public clipWorld(bounds: IBoundsData, ceilPixel?: boolean): void {
306
+ this.beginPath()
307
+ temp.copy(bounds).scale(this.pixelRatio)
308
+ if (ceilPixel) temp.ceil()
309
+ this.rect(temp.x, temp.y, temp.width, temp.height)
310
+ this.clip()
311
+
312
+ }
313
+
314
+ public clear(): void {
315
+ const { pixelRatio } = this
316
+ this.clearRect(0, 0, this.width * pixelRatio, this.height * pixelRatio)
317
+ }
318
+
319
+
320
+ // other
321
+
322
+ public isSameSize(size: IScreenSizeData): boolean {
323
+ return this.width === size.width && this.height === size.height && this.pixelRatio === size.pixelRatio
324
+ }
325
+
326
+ // 需要有 manager变量
327
+ public getSameCanvas(useSameWorldTransform?: boolean): ILeaferCanvas {
328
+ const { width, height, pixelRatio } = this
329
+
330
+ const options = { width, height, pixelRatio }
331
+ const canvas = this.manager ? this.manager.get(options) : Creator.canvas(options)
332
+
333
+ canvas.save()
334
+
335
+ if (useSameWorldTransform) canvas.useWorldTransform({ ...this.worldTransform })
336
+
337
+ return canvas
338
+ }
339
+
340
+ public getBiggerCanvas(addWidth: number, addHeight: number): ILeaferCanvas {
341
+ let { width, height, pixelRatio } = this
342
+ if (addWidth) width += addWidth
343
+ if (addHeight) height += addHeight
344
+
345
+ const options = { width, height, pixelRatio }
346
+ const canvas = this.manager ? this.manager.get(options) : Creator.canvas(options)
347
+
348
+ canvas.save()
349
+ return canvas
350
+ }
351
+
352
+ public recycle(): void {
353
+ if (!this.recycled) {
354
+ this.restore()
355
+ this.manager ? this.manager.recycle(this) : this.destroy()
356
+ }
357
+ }
358
+
359
+ public updateRender(): void { }
360
+
361
+ public unrealCanvas(): void { }
362
+
363
+ public destroy(): void {
364
+ this.manager = this.view = this.parentView = null
365
+ }
366
+ }
@@ -0,0 +1,7 @@
1
+ import { IPathDrawer } from '@leafer/interface'
2
+
3
+ import { roundRect } from './roundRect'
4
+
5
+ export function canvasPatch(drawer: IPathDrawer): void {
6
+ roundRect(drawer)
7
+ }
@@ -0,0 +1,18 @@
1
+ import { IPathDrawer } from '@leafer/interface'
2
+ import { RectHelper } from '@leafer/path'
3
+
4
+
5
+ const { drawRoundRect } = RectHelper
6
+
7
+ export function roundRect(drawer: IPathDrawer): void {
8
+
9
+ if (drawer && !drawer.roundRect) {
10
+
11
+ drawer.roundRect = function (x: number, y: number, width: number, height: number, cornerRadius: number | number[]): void {
12
+
13
+ drawRoundRect(this as IPathDrawer, x, y, width, height, cornerRadius)
14
+ }
15
+ }
16
+
17
+ }
18
+