@leafer/image 1.0.0-beta.9 → 1.0.0-rc.10

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,15 @@
1
1
  {
2
2
  "name": "@leafer/image",
3
- "version": "1.0.0-beta.9",
3
+ "version": "1.0.0-rc.10",
4
4
  "description": "@leafer/image",
5
5
  "author": "Chao (Leafer) Wan",
6
6
  "license": "MIT",
7
7
  "main": "src/index.ts",
8
+ "types": "types/index.d.ts",
8
9
  "files": [
9
- "src"
10
+ "src",
11
+ "types",
12
+ "dist"
10
13
  ],
11
14
  "repository": {
12
15
  "type": "git",
@@ -19,10 +22,11 @@
19
22
  "leaferjs"
20
23
  ],
21
24
  "dependencies": {
22
- "@leafer/task": "1.0.0-beta.9",
23
- "@leafer/platform": "1.0.0-beta.9"
25
+ "@leafer/task": "1.0.0-rc.10",
26
+ "@leafer/file": "1.0.0-rc.10",
27
+ "@leafer/platform": "1.0.0-rc.10"
24
28
  },
25
29
  "devDependencies": {
26
- "@leafer/interface": "1.0.0-beta.9"
30
+ "@leafer/interface": "1.0.0-rc.10"
27
31
  }
28
32
  }
@@ -1,43 +1,68 @@
1
- import { ILeafer, ILeaferCanvasConfig, IImageManager, ILeaferImageConfig, ILeaferImage, ITaskProcessor, IFunction } from '@leafer/interface'
1
+ import { IImageManager, ILeaferImageConfig, ILeaferImage, IExportFileType } from '@leafer/interface'
2
2
  import { Creator } from '@leafer/platform'
3
+ import { FileHelper } from '@leafer/file'
3
4
  import { TaskProcessor } from '@leafer/task'
4
5
 
5
6
 
6
- interface ILeaferImageMap {
7
- [name: string]: ILeaferImage
8
- }
9
-
7
+ export const ImageManager: IImageManager = {
10
8
 
11
- export class ImageManager implements IImageManager {
9
+ map: {},
12
10
 
13
- public leafer: ILeafer
11
+ recycledList: [],
14
12
 
15
- public tasker: ITaskProcessor
13
+ tasker: new TaskProcessor(),
16
14
 
17
- public map: ILeaferImageMap = {}
15
+ patternTasker: new TaskProcessor(),
18
16
 
19
- constructor(leafer: ILeafer, _config: ILeaferCanvasConfig) {
20
- this.leafer = leafer
21
- this.tasker = new TaskProcessor()
22
- }
17
+ get isComplete() { return I.tasker.isComplete },
23
18
 
24
- public get(config: ILeaferImageConfig): ILeaferImage {
25
- let image = this.map[config.url]
19
+ get(config: ILeaferImageConfig): ILeaferImage {
20
+ let image = I.map[config.url]
26
21
  if (!image) {
27
22
  image = Creator.image(config)
28
- this.map[config.url] = image
23
+ I.map[config.url] = image
29
24
  }
25
+ image.use++
30
26
  return image
31
- }
27
+ },
32
28
 
33
- public load(image: ILeaferImage, onSuccess: IFunction, onError: IFunction): void {
34
- this.tasker.addParallel(async () => await image.load(onSuccess, onError))
35
- if (!this.tasker.running) this.tasker.start()
36
- }
29
+ recycle(image: ILeaferImage): void {
30
+ image.use--
31
+ setTimeout(() => { if (!image.use) I.recycledList.push(image) })
32
+ },
33
+
34
+ clearRecycled(): void {
35
+ const list = I.recycledList
36
+ if (list.length) {
37
+ list.forEach(image => {
38
+ if (!image.use && image.url) {
39
+ delete I.map[image.url]
40
+ image.destroy()
41
+ }
42
+ })
43
+ list.length = 0
44
+ }
45
+ },
46
+
47
+ hasOpacityPixel(config: ILeaferImageConfig): boolean {
48
+ return FileHelper.opacityTypes.some(item => I.isFormat(item, config))
49
+ },
50
+
51
+ isFormat(format: IExportFileType, config: ILeaferImageConfig): boolean {
52
+ if (config.format === format) return true
53
+ const { url } = config
54
+ if (url.startsWith('data:')) {
55
+ if (url.startsWith('data:' + FileHelper.mineType(format))) return true
56
+ } else {
57
+ if (url.includes('.' + format) || url.includes('.' + FileHelper.upperCaseTypeMap[format])) return true
58
+ }
59
+ return false
60
+ },
37
61
 
38
- public destroy(): void {
39
- this.leafer = null
40
- this.map = null
62
+ destroy(): void {
63
+ I.map = {}
41
64
  }
42
65
 
43
- }
66
+ }
67
+
68
+ const I = ImageManager
@@ -0,0 +1,125 @@
1
+ import { ILeaferImage, ILeaferImageConfig, IFunction, IObject, InnerId, IMatrixData, ICanvasPattern, ILeaferImageCacheCanvas, ILeaferImagePatternPaint } from '@leafer/interface'
2
+ import { Platform } from '@leafer/platform'
3
+ import { IncrementId } from '@leafer/math'
4
+
5
+ import { ImageManager } from './ImageManager'
6
+
7
+
8
+ const { IMAGE, create } = IncrementId
9
+
10
+ export class LeaferImage implements ILeaferImage {
11
+
12
+ public readonly innerId: InnerId
13
+ public get url() { return this.config.url }
14
+
15
+ public view: any
16
+
17
+ public width: number
18
+ public height: number
19
+
20
+ public isSVG: boolean
21
+ public hasOpacityPixel: boolean // check png / svg / webp
22
+
23
+ public get completed() { return this.ready || !!this.error }
24
+
25
+ public ready: boolean
26
+ public error: IObject
27
+ public loading: boolean
28
+
29
+ public use = 0
30
+
31
+ public config: ILeaferImageConfig
32
+
33
+ protected waitComplete: IFunction[] = []
34
+
35
+ protected cache: ILeaferImageCacheCanvas
36
+
37
+ constructor(config: ILeaferImageConfig) {
38
+ this.innerId = create(IMAGE)
39
+ this.config = config || { url: '' }
40
+ this.isSVG = ImageManager.isFormat('svg', config)
41
+ this.hasOpacityPixel = ImageManager.hasOpacityPixel(config)
42
+ }
43
+
44
+ public load(onSuccess: IFunction, onError: IFunction): number {
45
+ if (!this.loading) {
46
+ this.loading = true
47
+ ImageManager.tasker.add(async () => await Platform.origin.loadImage(this.url).then((img) => {
48
+ this.ready = true
49
+ this.width = img.naturalWidth || img.width
50
+ this.height = img.naturalHeight || img.height
51
+ this.view = img
52
+ this.onComplete(true)
53
+ }).catch((e) => {
54
+ this.error = e
55
+ this.onComplete(false)
56
+ }))
57
+ }
58
+ this.waitComplete.push(onSuccess, onError)
59
+ return this.waitComplete.length - 2
60
+ }
61
+
62
+ public unload(index: number, stopEvent?: boolean): void {
63
+ const l = this.waitComplete
64
+ if (stopEvent) {
65
+ const error = l[index + 1]
66
+ if (error) error({ type: 'stop' })
67
+ }
68
+ l[index] = l[index + 1] = undefined
69
+ }
70
+
71
+ protected onComplete(isSuccess: boolean): void {
72
+ let odd: number
73
+ this.waitComplete.forEach((item, index) => {
74
+ odd = index % 2
75
+ if (item) {
76
+ if (isSuccess) {
77
+ if (!odd) item(this)
78
+ } else {
79
+ if (odd) item(this.error)
80
+ }
81
+ }
82
+ })
83
+ this.waitComplete.length = 0
84
+ this.loading = false
85
+ }
86
+
87
+ public getCanvas(width: number, height: number, opacity?: number, _filters?: IObject): any {
88
+ width || (width = this.width)
89
+ height || (height = this.height)
90
+
91
+ if (this.cache) { // when use > 1, check cache
92
+ let { params, data } = this.cache
93
+ for (let i in params) { if (params[i] !== arguments[i]) { data = null; break } }
94
+ if (data) return data
95
+ }
96
+
97
+ const canvas = Platform.origin.createCanvas(width, height)
98
+ const ctx = canvas.getContext('2d')
99
+ if (opacity) ctx.globalAlpha = opacity
100
+ ctx.drawImage(this.view, 0, 0, width, height)
101
+
102
+ this.cache = this.use > 1 ? { data: canvas, params: arguments } : null
103
+
104
+ return canvas
105
+ }
106
+
107
+ public getPattern(canvas: any, repeat: string | null, transform?: IMatrixData, paint?: ILeaferImagePatternPaint): ICanvasPattern {
108
+ const pattern = Platform.canvas.createPattern(canvas, repeat)
109
+ try {
110
+ if (transform && pattern.setTransform) {
111
+ pattern.setTransform(transform) // maybe error
112
+ transform = null
113
+ }
114
+ } catch { }
115
+ if (paint) paint.transform = transform
116
+ return pattern
117
+ }
118
+
119
+ public destroy(): void {
120
+ this.config = { url: '' }
121
+ this.cache = null
122
+ this.waitComplete.length = 0
123
+ }
124
+
125
+ }
package/src/index.ts CHANGED
@@ -1,2 +1,2 @@
1
- export { LeaferImageBase } from './LeaferImageBase'
1
+ export { LeaferImage } from './LeaferImage'
2
2
  export { ImageManager } from './ImageManager'
@@ -0,0 +1,30 @@
1
+ import { ILeaferImage, InnerId, IObject, ILeaferImageConfig, IFunction, ILeaferImageCacheCanvas, IMatrixData, ILeaferImagePatternPaint, ICanvasPattern, IImageManager } from '@leafer/interface';
2
+
3
+ declare class LeaferImage implements ILeaferImage {
4
+ readonly innerId: InnerId;
5
+ get url(): string;
6
+ view: any;
7
+ width: number;
8
+ height: number;
9
+ isSVG: boolean;
10
+ hasOpacityPixel: boolean;
11
+ get completed(): boolean;
12
+ ready: boolean;
13
+ error: IObject;
14
+ loading: boolean;
15
+ use: number;
16
+ config: ILeaferImageConfig;
17
+ protected waitComplete: IFunction[];
18
+ protected cache: ILeaferImageCacheCanvas;
19
+ constructor(config: ILeaferImageConfig);
20
+ load(onSuccess: IFunction, onError: IFunction): number;
21
+ unload(index: number, stopEvent?: boolean): void;
22
+ protected onComplete(isSuccess: boolean): void;
23
+ getCanvas(width: number, height: number, opacity?: number, _filters?: IObject): any;
24
+ getPattern(canvas: any, repeat: string | null, transform?: IMatrixData, paint?: ILeaferImagePatternPaint): ICanvasPattern;
25
+ destroy(): void;
26
+ }
27
+
28
+ declare const ImageManager: IImageManager;
29
+
30
+ export { ImageManager, LeaferImage };
@@ -1,49 +0,0 @@
1
- import { ILeaferImage, ILeaferImageConfig, IFunction, IObject } from '@leafer/interface'
2
- import { Platform } from '@leafer/platform'
3
-
4
-
5
- export class LeaferImageBase implements ILeaferImage {
6
-
7
- public view: any
8
-
9
- public width: number
10
- public height: number
11
-
12
- public ready: boolean
13
- public error: IObject
14
-
15
- public options: ILeaferImageConfig
16
-
17
- constructor(_options: ILeaferImageConfig) {
18
- this.options = _options
19
- }
20
-
21
- public async load(onSuccess: IFunction, onError: IFunction): Promise<void> {
22
- return Platform.origin.loadImage(this.options.url).then((img) => {
23
- this.ready = true
24
- this.width = img.naturalWidth || img.width
25
- this.height = img.naturalHeight || img.height
26
- this.view = img
27
- if (onSuccess) onSuccess(this)
28
- }).catch((e) => {
29
- this.error = e
30
- if (onError) onError(e)
31
- })
32
- }
33
-
34
- public getCanvas(width: number, height: number, opacity?: number, _filters?: IObject): any {
35
- width || (width = this.width)
36
- height || (height = this.height)
37
- const canvas = Platform.origin.createCanvas(width, height)
38
- const ctx = canvas.getContext('2d')
39
- if (opacity) ctx.globalAlpha = opacity
40
- ctx.drawImage(this.view, 0, 0, width, height)
41
- return canvas
42
- }
43
-
44
- public destroy(): void {
45
- this.view = null
46
- this.options = null
47
- }
48
-
49
- }