declare-render 1.0.4 → 1.0.5

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/README.md CHANGED
@@ -20,7 +20,7 @@ const schema: RenderData = {
20
20
  id: "my-canvas",
21
21
  width: 800,
22
22
  height: 600,
23
- renderers: [
23
+ layers: [
24
24
  {
25
25
  id: "text-1",
26
26
  type: "text",
@@ -45,7 +45,8 @@ const buffer = await renderer.draw();
45
45
 
46
46
  ## Features
47
47
 
48
- - **Text Renderers**: Render text with various styling options
49
- - **Image Renderers**: Render images or solid color rectangles
50
- - **Container Renderers**: Organize renderers with flexbox-like layouts
51
- - **Multiple Output Formats**: PNG and JPG support
48
+ - **Text Layers**: Render text with various styling options
49
+ - **Image Layers**: Render images or solid color rectangles
50
+ - **Container Layers**: Organize layers with flexbox-like layouts
51
+ - **Multiple Output Formats**: PNG and JPG support
52
+
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "declare-render",
3
- "version": "1.0.4",
3
+ "version": "1.0.5",
4
4
  "description": "You can declare canvas shapes by JSON format.",
5
5
  "main": "index.js",
6
6
  "keywords": [
@@ -16,133 +16,174 @@ import { ShapeRender } from "../shape-render/index";
16
16
  export type { ContainerRenderData } from "../../types";
17
17
 
18
18
  export class ContainerRenderer extends BaseRender<ContainerRenderData> {
19
- private ctx: CanvasRenderingContext2D
20
- private renderers: Array<ImgRender | TextRender | ShapeRender | ContainerRenderer> = []
19
+ private ctx: CanvasRenderingContext2D;
20
+ private layers: Array<
21
+ ImgRender | TextRender | ShapeRender | ContainerRenderer
22
+ > = [];
21
23
 
22
24
  constructor(ctx: CanvasRenderingContext2D, data: ContainerRenderData) {
23
- super()
24
- this.ctx = ctx
25
- this.data = cloneDeep(data)
26
- this.ctx.patternQuality = 'best'
27
- this.ctx.quality = 'best'
25
+ super();
26
+ this.ctx = ctx;
27
+ this.data = cloneDeep(data);
28
+ this.ctx.patternQuality = "best";
29
+ this.ctx.quality = "best";
28
30
  }
29
31
 
30
32
  get container() {
31
- return this.getContainerCoordinates()
33
+ return this.getContainerCoordinates();
32
34
  }
33
35
 
34
- private createRenderer = (renderData: ImgRenderData | TextRenderData | ShapeRenderData | ContainerRenderData) => {
35
- switch (renderData.type) {
36
+ private createLayer = (
37
+ layerData:
38
+ | ImgRenderData
39
+ | TextRenderData
40
+ | ShapeRenderData
41
+ | ContainerRenderData,
42
+ ) => {
43
+ switch (layerData.type) {
36
44
  case RendererType.TEXT: {
37
- return new TextRender(this.ctx, renderData, { inFlexFlow: isUndefined(renderData.x) || isUndefined(renderData.y) })
45
+ return new TextRender(this.ctx, layerData, {
46
+ inFlexFlow: isUndefined(layerData.x) || isUndefined(layerData.y),
47
+ });
38
48
  }
39
49
  case RendererType.IMG:
40
- return new ImgRender(this.ctx, renderData)
50
+ return new ImgRender(this.ctx, layerData);
41
51
  case RendererType.SHAPE:
42
- return new ShapeRender(this.ctx, renderData)
52
+ return new ShapeRender(this.ctx, layerData);
43
53
  case RendererType.CONTAINER:
44
- return new ContainerRenderer(this.ctx, renderData)
54
+ return new ContainerRenderer(this.ctx, layerData);
45
55
  default:
46
- throw new Error("[Renderer] unknown renderer type")
56
+ throw new Error("[Renderer] unknown layer type");
47
57
  }
48
- }
58
+ };
49
59
 
50
- // TODO center the renderers, vertical center, left align, wrap the renderers;
60
+ // TODO center the layers, vertical center, left align, wrap the layers;
51
61
  public layout = async () => {
52
- const layoutedRenderers = [] as Array<ImgRender | TextRender | ShapeRender | ContainerRenderer>
53
-
54
- for (const index in this.data.renderers) {
55
- const renderData = cloneDeep(this.data.renderers[index]) as ImgRenderData | TextRenderData | ShapeRenderData | ContainerRenderData
56
-
57
- let renderX = renderData.x
58
- let renderY = renderData.y
59
- // 子 renderer 的 x、y 相对容器的 x、y 进行定位
60
- if (isNumber(renderX)) {
61
- renderX += this.data.x
62
+ const layoutedLayers = [] as Array<
63
+ ImgRender | TextRender | ShapeRender | ContainerRenderer
64
+ >;
65
+
66
+ for (const index in this.data.layers) {
67
+ const layerData = cloneDeep(this.data.layers[index]) as
68
+ | ImgRenderData
69
+ | TextRenderData
70
+ | ShapeRenderData
71
+ | ContainerRenderData;
72
+
73
+ let layerX = layerData.x;
74
+ let layerY = layerData.y;
75
+ // 子 layer 的 x、y 相对容器的 x、y 进行定位
76
+ if (isNumber(layerX)) {
77
+ layerX += this.data.x;
62
78
  }
63
79
 
64
- if (isNumber(renderY)) {
65
- renderY += this.data.y
80
+ if (isNumber(layerY)) {
81
+ layerY += this.data.y;
66
82
  }
67
83
 
68
84
  // justify
69
- if (isUndefined(renderX) || isUndefined(renderY)) {
70
- const preRenderer = layoutedRenderers.at(-1) as undefined | ImgRender | TextRender | ShapeRender | ContainerRenderer
71
- const { x2, y1, x1, y2 } = preRenderer?.container || { x1: this.data.x, x2: this.data.x, y1: this.data.y, y2: this.data.y }
72
-
73
- const gapX = !preRenderer ?
74
- 0 : isObject(this.data.gap) ?
75
- this.data.gap.x : (this.data.gap || 0)
76
-
77
- const gapY = !preRenderer ?
78
- 0 : isObject(this.data.gap) ?
79
- this.data.gap.y : (this.data.gap || 0)
80
-
81
- if (this.data.direction === 'column') {
82
- renderX = renderX || x1
83
- renderY = renderY || (y2 + gapY)
85
+ if (isUndefined(layerX) || isUndefined(layerY)) {
86
+ const preLayer = layoutedLayers.at(-1) as
87
+ | undefined
88
+ | ImgRender
89
+ | TextRender
90
+ | ShapeRender
91
+ | ContainerRenderer;
92
+ const { x2, y1, x1, y2 } = preLayer?.container || {
93
+ x1: this.data.x,
94
+ x2: this.data.x,
95
+ y1: this.data.y,
96
+ y2: this.data.y,
97
+ };
98
+
99
+ const gapX = !preLayer
100
+ ? 0
101
+ : isObject(this.data.gap)
102
+ ? this.data.gap.x
103
+ : this.data.gap || 0;
104
+
105
+ const gapY = !preLayer
106
+ ? 0
107
+ : isObject(this.data.gap)
108
+ ? this.data.gap.y
109
+ : this.data.gap || 0;
110
+
111
+ if (this.data.direction === "column") {
112
+ layerX = layerX || x1;
113
+ layerY = layerY || y2 + gapY;
84
114
  } else {
85
- renderX = renderX || (x2 + gapX)
86
- renderY = renderY || y1
115
+ layerX = layerX || x2 + gapX;
116
+ layerY = layerY || y1;
87
117
  }
88
118
  }
89
119
 
90
- const currentRenderer = this.createRenderer({ ...renderData, x: renderX, y: renderY })
120
+ const currentLayer = this.createLayer({
121
+ ...layerData,
122
+ x: layerX,
123
+ y: layerY,
124
+ });
91
125
 
92
- await currentRenderer.layout()
93
-
94
- layoutedRenderers.push(currentRenderer)
126
+ await currentLayer.layout();
127
+
128
+ layoutedLayers.push(currentLayer);
95
129
  }
96
130
 
97
131
  // align items
98
- if (this.data.itemAlign === 'center') {
99
- const squares = layoutedRenderers.map(r => {
100
- const { x1, x2, y1, y2 } = r.container
101
- return { ...r.container, width: x2 - x1, height: y2 - y1, renderer: r }
102
- })
103
-
104
- const maxWidth = Math.max(...squares.map(s => s.width))
105
- const maxHeight = Math.max(...squares.map(s => s.height))
106
-
107
- squares.forEach(s => {
108
- if (this.data.direction === 'column') {
109
- const offset = (maxWidth - s.width) / 2
110
- s.renderer.setPosition(s.x1 + offset, s.y1)
132
+ if (this.data.itemAlign === "center") {
133
+ const squares = layoutedLayers.map((r) => {
134
+ const { x1, x2, y1, y2 } = r.container;
135
+ return { ...r.container, width: x2 - x1, height: y2 - y1, layer: r };
136
+ });
137
+
138
+ const maxWidth = Math.max(...squares.map((s) => s.width));
139
+ const maxHeight = Math.max(...squares.map((s) => s.height));
140
+
141
+ squares.forEach((s) => {
142
+ if (this.data.direction === "column") {
143
+ const offset = (maxWidth - s.width) / 2;
144
+ s.layer.setPosition(s.x1 + offset, s.y1);
111
145
  } else {
112
- const offset = (maxHeight - s.height) / 2
113
- s.renderer.setPosition(s.x1, s.y1 + offset)
146
+ const offset = (maxHeight - s.height) / 2;
147
+ s.layer.setPosition(s.x1, s.y1 + offset);
114
148
  }
115
- })
149
+ });
116
150
  }
117
151
 
118
- this.renderers = layoutedRenderers
119
- return this
120
- }
152
+ this.layers = layoutedLayers;
153
+ return this;
154
+ };
121
155
 
122
- // TODO rotate the whole renderer
123
- public draw = async() => {
124
- if (!this.renderers.length) {
125
- return this
156
+ // TODO rotate the whole layer
157
+ public draw = async () => {
158
+ if (!this.layers.length) {
159
+ return this;
126
160
  }
127
161
 
128
- const pickedOne = this.renderers.shift()!
129
- await pickedOne.draw()
162
+ const pickedOne = this.layers.shift()!;
163
+ await pickedOne.draw();
130
164
 
131
- await this.draw()
165
+ await this.draw();
132
166
 
133
- return this
134
- }
167
+ return this;
168
+ };
135
169
 
136
- // TODO handle the renderers wrapping, add gap;
170
+ // TODO handle the layers wrapping, add gap;
137
171
  private getContainerCoordinates = () => {
138
- if (!this.renderers.length) throw new Error("can not get container coordinates before layouted")
139
- const firstChild = this.renderers.at(0) as ImgRender | TextRender | ShapeRender
140
- const lastChild = this.renderers.at(-1) as ImgRender | TextRender | ShapeRender
172
+ if (!this.layers.length)
173
+ throw new Error("can not get container coordinates before layouted");
174
+ const firstChild = this.layers.at(0) as
175
+ | ImgRender
176
+ | TextRender
177
+ | ShapeRender;
178
+ const lastChild = this.layers.at(-1) as
179
+ | ImgRender
180
+ | TextRender
181
+ | ShapeRender;
141
182
  return {
142
183
  x1: firstChild.container.x1,
143
184
  y1: firstChild.container.y1,
144
185
  x2: lastChild.container.x2,
145
186
  y2: lastChild.container.y2,
146
- }
147
- }
187
+ };
188
+ };
148
189
  }
package/src/index.ts CHANGED
@@ -25,7 +25,7 @@ export class Renderer {
25
25
 
26
26
  draw = async () => {
27
27
  if (!this.schema.layers.length) {
28
- throw new Error("[Renderer] empty canvas with no renderers");
28
+ throw new Error("[Renderer] empty canvas with no layers");
29
29
  }
30
30
  const container = new ContainerRenderer(this.ctx, {
31
31
  id: this.schema.id,
@@ -34,7 +34,7 @@ export class Renderer {
34
34
  y: 0,
35
35
  width: this.canvas.width,
36
36
  height: this.canvas.height,
37
- renderers: this.schema.layers,
37
+ layers: this.schema.layers,
38
38
  });
39
39
  await container.layout().then((d) => d.draw());
40
40
  return this.toBuffer();
package/src/types.ts CHANGED
@@ -68,7 +68,7 @@ export interface ContainerRenderData {
68
68
  itemAlign?: "center";
69
69
  gap?: number | { x: number; y: number };
70
70
  wrap?: boolean;
71
- renderers: ChildRenderers;
71
+ layers: ChildRenderers;
72
72
  }
73
73
 
74
74
  export interface ShapeRenderData {
@@ -176,7 +176,7 @@ TextRenderData: { "id": string|number, "type": "text", "x": number, "y": number,
176
176
 
177
177
  ImgRenderData: { "id": string, "type": "img", "x": number, "y": number, "width"?: number, "height"?: number, "url"?: string, "color"?: string, "objectFit": "contain"|"cover", "radius"?: number, "rotate"?: number, "globalAlpha"?: number, "shadow"?: { "color": string, "blur": number, "X": number, "Y": number } }
178
178
 
179
- ContainerRenderData: { "id": string|number, "type": "container", "x": number, "y": number, "width": number, "height": number, "renderers": ChildRenderers[], "direction"?: "row"|"column", "gap"?: number|{ "x": number, "y": number }, "itemAlign"?: "center", "wrap"?: boolean }
179
+ ContainerRenderData: { "id": string|number, "type": "container", "x": number, "y": number, "width": number, "height": number, "layers": ChildRenderers[], "direction"?: "row"|"column", "gap"?: number|{ "x": number, "y": number }, "itemAlign"?: "center", "wrap"?: boolean }
180
180
 
181
181
  ShapeRenderData: { "id": string|number, "type": "shape", "x": number, "y": number, "width"?: number, "height"?: number, "rotate"?: number, "style"?: { "fillStyle"?: string, "strokeStyle"?: string, "lineWidth"?: number, "lineCap"?: "butt"|"round"|"square", "lineJoin"?: "bevel"|"round"|"miter", "miterLimit"?: number, "lineDash"?: number[], "lineDashOffset"?: number, "globalAlpha"?: number }, "shadow"?: { "color": string, "blur": number, "X": number, "Y": number }, "shapes": Array<ShapeCommand> }
182
182