@idraw/renderer 0.4.0-beta.8 → 0.4.0-rc.0

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
@@ -64,5 +64,5 @@ renderer.on('drawFrameComplete', (e) => {
64
64
 
65
65
  ## Documents
66
66
 
67
- - [Documents](https://idraw.js.org/docs/en/) | [中文文档](https://idraw.js.org/docs/zh/)
68
- - [Online Playground](https://idraw.js.org/playground/) | [在线API示例](https://idraw.js.org/playground/)
67
+ - [Documents](https://idrawjs.com/docs/en/) | [中文文档](https://idrawjs.com/docs/zh/)
68
+ - [Online Playground](https://idrawjs.com/playground/) | [在线API示例](https://idrawjs.com/playground/)
@@ -0,0 +1,44 @@
1
+ import type { Point, Data, Element, ElementType, ViewCalculator, ViewCalculatorOptions, ViewScaleInfo, ViewSizeInfo, ViewRectInfo, ModifyInfo, VirtualFlatItem } from '@idraw/types';
2
+ export declare class Calculator implements ViewCalculator {
3
+ #private;
4
+ constructor(opts: ViewCalculatorOptions);
5
+ toGridNum(num: number, opts?: {
6
+ ignore?: boolean;
7
+ }): number;
8
+ destroy(): void;
9
+ needRender(elem: Element<ElementType>): boolean;
10
+ getPointElement(p: Point, opts: {
11
+ data: Data;
12
+ viewScaleInfo: ViewScaleInfo;
13
+ viewSizeInfo: ViewSizeInfo;
14
+ }): {
15
+ index: number;
16
+ element: null | Element<ElementType>;
17
+ groupQueueIndex: number;
18
+ };
19
+ resetVirtualFlatItemMap(data: Data, opts: {
20
+ viewScaleInfo: ViewScaleInfo;
21
+ viewSizeInfo: ViewSizeInfo;
22
+ }): void;
23
+ updateVisiableStatus(opts: {
24
+ viewScaleInfo: ViewScaleInfo;
25
+ viewSizeInfo: ViewSizeInfo;
26
+ }): void;
27
+ calcViewRectInfoFromOrigin(uuid: string, opts: {
28
+ checkVisible?: boolean;
29
+ viewScaleInfo: ViewScaleInfo;
30
+ viewSizeInfo: ViewSizeInfo;
31
+ }): ViewRectInfo | null;
32
+ calcViewRectInfoFromRange(uuid: string, opts: {
33
+ checkVisible?: boolean;
34
+ viewScaleInfo: ViewScaleInfo;
35
+ viewSizeInfo: ViewSizeInfo;
36
+ }): ViewRectInfo | null;
37
+ modifyText(element: Element<'text'>): void;
38
+ modifyVirtualFlatItemMap(data: Data, opts: {
39
+ modifyInfo: ModifyInfo;
40
+ viewScaleInfo: ViewScaleInfo;
41
+ viewSizeInfo: ViewSizeInfo;
42
+ }): void;
43
+ getVirtualFlatItem(uuid: string): VirtualFlatItem | null;
44
+ }
@@ -0,0 +1,180 @@
1
+ var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
2
+ if (kind === "m") throw new TypeError("Private method is not writable");
3
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
4
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
5
+ return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
6
+ };
7
+ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
8
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
9
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
10
+ return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
11
+ };
12
+ var _Calculator_opts, _Calculator_store;
13
+ import { is, getViewPointAtElement, Store, calcViewPointSize, findElementFromListByPosition, getGroupQueueByElementPosition, calcElementOriginRectInfo, originRectInfoToRangeRectInfo } from '@idraw/util';
14
+ import { sortElementsViewVisiableInfoMap, updateVirtualFlatItemMapStatus } from './view-visible';
15
+ import { calcVirtualFlatDetail } from './virtual-flat';
16
+ import { calcVirtualTextDetail } from './virtual-flat/text';
17
+ export class Calculator {
18
+ constructor(opts) {
19
+ _Calculator_opts.set(this, void 0);
20
+ _Calculator_store.set(this, void 0);
21
+ __classPrivateFieldSet(this, _Calculator_opts, opts, "f");
22
+ __classPrivateFieldSet(this, _Calculator_store, new Store({
23
+ defaultStorage: {
24
+ virtualFlatItemMap: {},
25
+ visibleCount: 0,
26
+ invisibleCount: 0
27
+ }
28
+ }), "f");
29
+ }
30
+ toGridNum(num, opts) {
31
+ if ((opts === null || opts === void 0 ? void 0 : opts.ignore) === true) {
32
+ return num;
33
+ }
34
+ return Math.round(num);
35
+ }
36
+ destroy() {
37
+ __classPrivateFieldSet(this, _Calculator_opts, null, "f");
38
+ }
39
+ needRender(elem) {
40
+ const virtualFlatItemMap = __classPrivateFieldGet(this, _Calculator_store, "f").get('virtualFlatItemMap');
41
+ const info = virtualFlatItemMap[elem.uuid];
42
+ if (!info) {
43
+ return true;
44
+ }
45
+ return info.isVisibleInView;
46
+ }
47
+ getPointElement(p, opts) {
48
+ const context2d = __classPrivateFieldGet(this, _Calculator_opts, "f").tempContext;
49
+ return getViewPointAtElement(p, Object.assign(Object.assign({}, opts), { context2d }));
50
+ }
51
+ resetVirtualFlatItemMap(data, opts) {
52
+ if (data) {
53
+ const { virtualFlatItemMap, invisibleCount, visibleCount } = sortElementsViewVisiableInfoMap(data.elements, Object.assign(Object.assign({}, opts), {
54
+ tempContext: __classPrivateFieldGet(this, _Calculator_opts, "f").tempContext
55
+ }));
56
+ __classPrivateFieldGet(this, _Calculator_store, "f").set('virtualFlatItemMap', virtualFlatItemMap);
57
+ __classPrivateFieldGet(this, _Calculator_store, "f").set('invisibleCount', invisibleCount);
58
+ __classPrivateFieldGet(this, _Calculator_store, "f").set('visibleCount', visibleCount);
59
+ }
60
+ }
61
+ updateVisiableStatus(opts) {
62
+ const { virtualFlatItemMap, invisibleCount, visibleCount } = updateVirtualFlatItemMapStatus(__classPrivateFieldGet(this, _Calculator_store, "f").get('virtualFlatItemMap'), opts);
63
+ __classPrivateFieldGet(this, _Calculator_store, "f").set('virtualFlatItemMap', virtualFlatItemMap);
64
+ __classPrivateFieldGet(this, _Calculator_store, "f").set('invisibleCount', invisibleCount);
65
+ __classPrivateFieldGet(this, _Calculator_store, "f").set('visibleCount', visibleCount);
66
+ }
67
+ calcViewRectInfoFromOrigin(uuid, opts) {
68
+ const infoData = __classPrivateFieldGet(this, _Calculator_store, "f").get('virtualFlatItemMap')[uuid];
69
+ if (!(infoData === null || infoData === void 0 ? void 0 : infoData.originRectInfo)) {
70
+ return null;
71
+ }
72
+ const { checkVisible, viewScaleInfo, viewSizeInfo } = opts;
73
+ const { center, left, right, bottom, top, topLeft, topRight, bottomLeft, bottomRight } = infoData.originRectInfo;
74
+ if (checkVisible === true && infoData.isVisibleInView === false) {
75
+ return null;
76
+ }
77
+ const calcOpts = { viewScaleInfo, viewSizeInfo };
78
+ const viewRectInfo = {
79
+ center: calcViewPointSize(center, calcOpts),
80
+ left: calcViewPointSize(left, calcOpts),
81
+ right: calcViewPointSize(right, calcOpts),
82
+ bottom: calcViewPointSize(bottom, calcOpts),
83
+ top: calcViewPointSize(top, calcOpts),
84
+ topLeft: calcViewPointSize(topLeft, calcOpts),
85
+ topRight: calcViewPointSize(topRight, calcOpts),
86
+ bottomLeft: calcViewPointSize(bottomLeft, calcOpts),
87
+ bottomRight: calcViewPointSize(bottomRight, calcOpts)
88
+ };
89
+ return viewRectInfo;
90
+ }
91
+ calcViewRectInfoFromRange(uuid, opts) {
92
+ const infoData = __classPrivateFieldGet(this, _Calculator_store, "f").get('virtualFlatItemMap')[uuid];
93
+ if (!(infoData === null || infoData === void 0 ? void 0 : infoData.originRectInfo)) {
94
+ return null;
95
+ }
96
+ const { checkVisible, viewScaleInfo, viewSizeInfo } = opts;
97
+ const { center, left, right, bottom, top, topLeft, topRight, bottomLeft, bottomRight } = infoData.rangeRectInfo;
98
+ if (checkVisible === true && infoData.isVisibleInView === false) {
99
+ return null;
100
+ }
101
+ const calcOpts = { viewScaleInfo, viewSizeInfo };
102
+ const viewRectInfo = {
103
+ center: calcViewPointSize(center, calcOpts),
104
+ left: calcViewPointSize(left, calcOpts),
105
+ right: calcViewPointSize(right, calcOpts),
106
+ bottom: calcViewPointSize(bottom, calcOpts),
107
+ top: calcViewPointSize(top, calcOpts),
108
+ topLeft: calcViewPointSize(topLeft, calcOpts),
109
+ topRight: calcViewPointSize(topRight, calcOpts),
110
+ bottomLeft: calcViewPointSize(bottomLeft, calcOpts),
111
+ bottomRight: calcViewPointSize(bottomRight, calcOpts)
112
+ };
113
+ return viewRectInfo;
114
+ }
115
+ modifyText(element) {
116
+ const virtualFlatItemMap = __classPrivateFieldGet(this, _Calculator_store, "f").get('virtualFlatItemMap');
117
+ const flatItem = virtualFlatItemMap[element.uuid];
118
+ if (element && element.type === 'text') {
119
+ const newVirtualFlatItem = Object.assign(Object.assign({}, flatItem), calcVirtualTextDetail(element, {
120
+ tempContext: __classPrivateFieldGet(this, _Calculator_opts, "f").tempContext
121
+ }));
122
+ virtualFlatItemMap[element.uuid] = newVirtualFlatItem;
123
+ __classPrivateFieldGet(this, _Calculator_store, "f").set('virtualFlatItemMap', virtualFlatItemMap);
124
+ }
125
+ }
126
+ modifyVirtualFlatItemMap(data, opts) {
127
+ const { modifyInfo, viewScaleInfo, viewSizeInfo } = opts;
128
+ const { type, content } = modifyInfo;
129
+ const list = data.elements;
130
+ const virtualFlatItemMap = __classPrivateFieldGet(this, _Calculator_store, "f").get('virtualFlatItemMap');
131
+ if (type === 'deleteElement') {
132
+ const { element } = content;
133
+ const uuids = [];
134
+ const _walk = (e) => {
135
+ uuids.push(e.uuid);
136
+ if (e.type === 'group' && Array.isArray(e.detail.children)) {
137
+ e.detail.children.forEach((child) => {
138
+ _walk(child);
139
+ });
140
+ }
141
+ };
142
+ _walk(element);
143
+ uuids.forEach((uuid) => {
144
+ delete virtualFlatItemMap[uuid];
145
+ });
146
+ __classPrivateFieldGet(this, _Calculator_store, "f").set('virtualFlatItemMap', virtualFlatItemMap);
147
+ }
148
+ else if (type === 'addElement' || type === 'updateElement') {
149
+ const { position } = content;
150
+ const element = findElementFromListByPosition(position, data.elements);
151
+ const groupQueue = getGroupQueueByElementPosition(list, position);
152
+ if (element) {
153
+ if (type === 'updateElement' && element.type === 'group') {
154
+ this.resetVirtualFlatItemMap(data, { viewScaleInfo, viewSizeInfo });
155
+ }
156
+ else {
157
+ const originRectInfo = calcElementOriginRectInfo(element, {
158
+ groupQueue: groupQueue || []
159
+ });
160
+ const newVirtualFlatItem = Object.assign({ type: element.type, originRectInfo, rangeRectInfo: is.angle(element.angle) ? originRectInfoToRangeRectInfo(originRectInfo) : originRectInfo, isVisibleInView: true, position: [...position] }, calcVirtualFlatDetail(element, {
161
+ tempContext: __classPrivateFieldGet(this, _Calculator_opts, "f").tempContext
162
+ }));
163
+ virtualFlatItemMap[element.uuid] = newVirtualFlatItem;
164
+ __classPrivateFieldGet(this, _Calculator_store, "f").set('virtualFlatItemMap', virtualFlatItemMap);
165
+ if (type === 'updateElement') {
166
+ this.updateVisiableStatus({ viewScaleInfo, viewSizeInfo });
167
+ }
168
+ }
169
+ }
170
+ }
171
+ else if (type === 'moveElement') {
172
+ this.resetVirtualFlatItemMap(data, { viewScaleInfo, viewSizeInfo });
173
+ }
174
+ }
175
+ getVirtualFlatItem(uuid) {
176
+ const itemMap = __classPrivateFieldGet(this, _Calculator_store, "f").get('virtualFlatItemMap');
177
+ return itemMap[uuid] || null;
178
+ }
179
+ }
180
+ _Calculator_opts = new WeakMap(), _Calculator_store = new WeakMap();
@@ -1,14 +1,26 @@
1
1
  import { ViewContext2D, Element, ElementType, ElementSize, ViewScaleInfo, ViewSizeInfo } from '@idraw/types';
2
+ import { Calculator } from '../calculator';
2
3
  export declare function getOpacity(elem: Element): number;
3
- export declare function drawBox(ctx: ViewContext2D, viewElem: Element<ElementType>, opts: {
4
- originElem: Element<ElementType>;
4
+ export declare function drawBox(ctx: ViewContext2D, viewElem: Element, opts: {
5
+ originElem: Element;
5
6
  calcElemSize: ElementSize;
6
7
  pattern?: string | CanvasPattern | null;
7
- renderContent: () => void;
8
+ renderContent?: () => void;
8
9
  viewScaleInfo: ViewScaleInfo;
9
10
  viewSizeInfo: ViewSizeInfo;
10
11
  parentOpacity: number;
11
12
  }): void;
13
+ export declare function drawBoxBackground(ctx: ViewContext2D, viewElem: Element<ElementType>, opts: {
14
+ pattern?: string | CanvasPattern | null;
15
+ viewScaleInfo: ViewScaleInfo;
16
+ viewSizeInfo: ViewSizeInfo;
17
+ }): void;
18
+ export declare function drawBoxBorder(ctx: ViewContext2D, viewElem: Element<ElementType>, opts: {
19
+ originElem: Element;
20
+ viewScaleInfo: ViewScaleInfo;
21
+ viewSizeInfo: ViewSizeInfo;
22
+ calculator?: Calculator;
23
+ }): void;
12
24
  export declare function drawBoxShadow(ctx: ViewContext2D, viewElem: Element<ElementType>, opts: {
13
25
  viewScaleInfo: ViewScaleInfo;
14
26
  viewSizeInfo: ViewSizeInfo;
@@ -13,23 +13,41 @@ export function drawBox(ctx, viewElem, opts) {
13
13
  const { pattern, renderContent, originElem, calcElemSize, viewScaleInfo, viewSizeInfo } = opts || {};
14
14
  const { parentOpacity } = opts;
15
15
  const opacity = getOpacity(originElem) * parentOpacity;
16
- drawClipPath(ctx, viewElem, {
17
- originElem,
18
- calcElemSize,
19
- viewScaleInfo,
20
- viewSizeInfo,
21
- renderContent: () => {
22
- ctx.globalAlpha = opacity;
23
- drawBoxBackground(ctx, viewElem, { pattern, viewScaleInfo, viewSizeInfo });
24
- renderContent === null || renderContent === void 0 ? void 0 : renderContent();
25
- drawBoxBorder(ctx, viewElem, { viewScaleInfo, viewSizeInfo });
26
- ctx.globalAlpha = parentOpacity;
16
+ const { clipPath, clipPathStrokeColor, clipPathStrokeWidth } = originElem.detail;
17
+ const mainRender = () => {
18
+ ctx.globalAlpha = opacity;
19
+ drawBoxBackground(ctx, viewElem, { pattern, viewScaleInfo, viewSizeInfo });
20
+ renderContent === null || renderContent === void 0 ? void 0 : renderContent();
21
+ drawBoxBorder(ctx, viewElem, { originElem, viewScaleInfo, viewSizeInfo });
22
+ ctx.globalAlpha = parentOpacity;
23
+ };
24
+ if (clipPath) {
25
+ drawClipPath(ctx, viewElem, {
26
+ originElem,
27
+ calcElemSize,
28
+ viewScaleInfo,
29
+ viewSizeInfo,
30
+ renderContent: () => {
31
+ mainRender();
32
+ }
33
+ });
34
+ if (typeof clipPathStrokeWidth === 'number' && clipPathStrokeWidth > 0 && clipPathStrokeColor) {
35
+ drawClipPathStroke(ctx, viewElem, {
36
+ originElem,
37
+ calcElemSize,
38
+ viewScaleInfo,
39
+ viewSizeInfo,
40
+ parentOpacity
41
+ });
27
42
  }
28
- });
43
+ }
44
+ else {
45
+ mainRender();
46
+ }
29
47
  }
30
48
  function drawClipPath(ctx, viewElem, opts) {
31
- const { renderContent, originElem, calcElemSize, viewScaleInfo, viewSizeInfo } = opts;
32
- const totalScale = viewScaleInfo.scale * viewSizeInfo.devicePixelRatio;
49
+ const { renderContent, originElem, calcElemSize, viewSizeInfo } = opts;
50
+ const totalScale = viewSizeInfo.devicePixelRatio;
33
51
  const { clipPath } = (originElem === null || originElem === void 0 ? void 0 : originElem.detail) || {};
34
52
  if (clipPath && calcElemSize && clipPath.commands) {
35
53
  const { x, y, w, h } = calcElemSize;
@@ -45,7 +63,7 @@ function drawClipPath(ctx, viewElem, opts) {
45
63
  ctx.scale(totalScale * scaleW, totalScale * scaleH);
46
64
  const pathStr = generateSVGPath(clipPath.commands || []);
47
65
  const path2d = new Path2D(pathStr);
48
- ctx.clip(path2d);
66
+ ctx.clip(path2d, 'nonzero');
49
67
  ctx.translate(0 - internalX, 0 - internalY);
50
68
  ctx.setTransform(1, 0, 0, 1, 0, 0);
51
69
  rotateElement(ctx, Object.assign({}, viewElem), () => {
@@ -57,7 +75,45 @@ function drawClipPath(ctx, viewElem, opts) {
57
75
  renderContent === null || renderContent === void 0 ? void 0 : renderContent();
58
76
  }
59
77
  }
60
- function drawBoxBackground(ctx, viewElem, opts) {
78
+ function drawClipPathStroke(ctx, viewElem, opts) {
79
+ const { renderContent, originElem, calcElemSize, viewSizeInfo, parentOpacity } = opts;
80
+ const totalScale = viewSizeInfo.devicePixelRatio;
81
+ const { clipPath, clipPathStrokeColor, clipPathStrokeWidth } = (originElem === null || originElem === void 0 ? void 0 : originElem.detail) || {};
82
+ if (clipPath &&
83
+ calcElemSize &&
84
+ clipPath.commands &&
85
+ typeof clipPathStrokeWidth === 'number' &&
86
+ clipPathStrokeWidth > 0 &&
87
+ clipPathStrokeColor) {
88
+ const { x, y, w, h } = calcElemSize;
89
+ const { originW, originH, originX, originY } = clipPath;
90
+ const scaleW = w / originW;
91
+ const scaleH = h / originH;
92
+ const viewOriginX = originX * scaleW;
93
+ const viewOriginY = originY * scaleH;
94
+ const internalX = x - viewOriginX;
95
+ const internalY = y - viewOriginY;
96
+ ctx.save();
97
+ ctx.globalAlpha = parentOpacity;
98
+ ctx.translate(internalX, internalY);
99
+ ctx.scale(totalScale * scaleW, totalScale * scaleH);
100
+ const pathStr = generateSVGPath(clipPath.commands || []);
101
+ const path2d = new Path2D(pathStr);
102
+ ctx.strokeStyle = clipPathStrokeColor;
103
+ ctx.lineWidth = clipPathStrokeWidth;
104
+ ctx.stroke(path2d);
105
+ ctx.translate(0 - internalX, 0 - internalY);
106
+ ctx.setTransform(1, 0, 0, 1, 0, 0);
107
+ rotateElement(ctx, Object.assign({}, viewElem), () => {
108
+ renderContent === null || renderContent === void 0 ? void 0 : renderContent();
109
+ });
110
+ ctx.restore();
111
+ }
112
+ else {
113
+ renderContent === null || renderContent === void 0 ? void 0 : renderContent();
114
+ }
115
+ }
116
+ export function drawBoxBackground(ctx, viewElem, opts) {
61
117
  var _a, _b;
62
118
  const { pattern, viewScaleInfo, viewSizeInfo } = opts;
63
119
  const transform = [];
@@ -112,13 +168,13 @@ function drawBoxBackground(ctx, viewElem, opts) {
112
168
  }
113
169
  }
114
170
  }
115
- ctx.fill();
171
+ ctx.fill('nonzero');
116
172
  if (transform && transform.length > 0) {
117
173
  ctx.setTransform(1, 0, 0, 1, 0, 0);
118
174
  }
119
175
  }
120
176
  }
121
- function drawBoxBorder(ctx, viewElem, opts) {
177
+ export function drawBoxBorder(ctx, viewElem, opts) {
122
178
  if (viewElem.detail.borderWidth === 0) {
123
179
  return;
124
180
  }
@@ -131,12 +187,17 @@ function drawBoxBorder(ctx, viewElem, opts) {
131
187
  if (isColorStr(viewElem.detail.borderColor) === true) {
132
188
  borderColor = viewElem.detail.borderColor;
133
189
  }
134
- const { borderWidth, borderRadius, borderDash, boxSizing = defaultElemConfig.boxSizing } = viewElem.detail;
135
- let bw = 0;
136
- if (typeof borderWidth === 'number') {
137
- bw = borderWidth || 1;
190
+ const { borderDash, borderWidth, borderRadius, boxSizing = defaultElemConfig.boxSizing } = viewElem.detail;
191
+ let viewBorderDash = [];
192
+ if (Array.isArray(borderDash) && borderDash.length > 0) {
193
+ viewBorderDash = borderDash.map((num) => Math.ceil(num * scale));
194
+ }
195
+ if (viewBorderDash.length > 0) {
196
+ ctx.lineCap = 'butt';
197
+ }
198
+ else {
199
+ ctx.lineCap = 'square';
138
200
  }
139
- bw = bw * scale;
140
201
  let radiusList = [0, 0, 0, 0];
141
202
  if (typeof borderRadius === 'number') {
142
203
  const br = borderRadius * scale;
@@ -145,11 +206,12 @@ function drawBoxBorder(ctx, viewElem, opts) {
145
206
  else if (Array.isArray(borderRadius) && (borderRadius === null || borderRadius === void 0 ? void 0 : borderRadius.length) === 4) {
146
207
  radiusList = [borderRadius[0] * scale, borderRadius[1] * scale, borderRadius[2] * scale, borderRadius[3] * scale];
147
208
  }
148
- ctx.strokeStyle = borderColor;
149
- let viewBorderDash = [];
150
- if (Array.isArray(borderDash) && borderDash.length > 0) {
151
- viewBorderDash = borderDash.map((num) => Math.ceil(num * scale));
209
+ let bw = 0;
210
+ if (typeof borderWidth === 'number') {
211
+ bw = borderWidth || 1;
152
212
  }
213
+ bw = bw * scale;
214
+ ctx.strokeStyle = borderColor;
153
215
  let borderTop = 0;
154
216
  let borderRight = 0;
155
217
  let borderBottom = 0;
@@ -234,12 +296,6 @@ function drawBoxBorder(ctx, viewElem, opts) {
234
296
  w = viewElem.w;
235
297
  h = viewElem.h;
236
298
  }
237
- if (viewBorderDash.length > 0) {
238
- ctx.lineCap = 'butt';
239
- }
240
- else {
241
- ctx.lineCap = 'square';
242
- }
243
299
  w = Math.max(w, 1);
244
300
  h = Math.max(h, 1);
245
301
  radiusList = radiusList.map((r) => {
@@ -272,6 +328,12 @@ export function drawBoxShadow(ctx, viewElem, opts) {
272
328
  ctx.restore();
273
329
  }
274
330
  else {
331
+ ctx.save();
332
+ ctx.shadowColor = 'transparent';
333
+ ctx.shadowOffsetX = 0;
334
+ ctx.shadowOffsetY = 0;
335
+ ctx.shadowBlur = 0;
275
336
  renderContent();
337
+ ctx.restore();
276
338
  }
277
339
  }
@@ -1,9 +1,10 @@
1
- import { rotateElement } from '@idraw/util';
1
+ import { rotateElement, calcViewElementSize } from '@idraw/util';
2
2
  import { createColorStyle } from './color';
3
3
  import { drawBoxShadow, getOpacity } from './box';
4
4
  export function drawCircle(ctx, elem, opts) {
5
5
  const { detail, angle } = elem;
6
- const { background = '#000000', borderColor = '#000000', boxSizing, borderWidth = 0 } = detail;
6
+ const { viewScaleInfo, viewSizeInfo, parentOpacity } = opts;
7
+ const { background = '#000000', borderColor = '#000000', boxSizing, borderWidth = 0, borderDash } = detail;
7
8
  let bw = 0;
8
9
  if (typeof borderWidth === 'number' && borderWidth > 0) {
9
10
  bw = borderWidth;
@@ -11,8 +12,8 @@ export function drawCircle(ctx, elem, opts) {
11
12
  else if (Array.isArray(borderWidth) && typeof borderWidth[0] === 'number' && borderWidth[0] > 0) {
12
13
  bw = borderWidth[0];
13
14
  }
14
- const { calculator, viewScaleInfo, viewSizeInfo, parentOpacity } = opts;
15
- const { x, y, w, h } = (calculator === null || calculator === void 0 ? void 0 : calculator.elementSize({ x: elem.x, y: elem.y, w: elem.w, h: elem.h }, viewScaleInfo, viewSizeInfo)) || elem;
15
+ bw = bw * viewScaleInfo.scale;
16
+ const { x, y, w, h } = calcViewElementSize({ x: elem.x, y: elem.y, w: elem.w, h: elem.h }, { viewScaleInfo }) || elem;
16
17
  const viewElem = Object.assign(Object.assign({}, elem), { x, y, w, h, angle });
17
18
  rotateElement(ctx, { x, y, w, h, angle }, () => {
18
19
  drawBoxShadow(ctx, viewElem, {
@@ -23,10 +24,10 @@ export function drawCircle(ctx, elem, opts) {
23
24
  let b = h / 2;
24
25
  const centerX = x + a;
25
26
  const centerY = y + b;
27
+ const radiusA = a;
28
+ const radiusB = b;
26
29
  if (bw > 0) {
27
- if (boxSizing === 'border-box') {
28
- a = a - bw;
29
- b = b - bw;
30
+ if (boxSizing === 'content-box') {
30
31
  }
31
32
  else if (boxSizing === 'center-line') {
32
33
  a = a - bw / 2;
@@ -37,29 +38,36 @@ export function drawCircle(ctx, elem, opts) {
37
38
  b = b - bw;
38
39
  }
39
40
  }
40
- const opacity = getOpacity(viewElem) * parentOpacity;
41
- ctx.globalAlpha = opacity;
42
- if (typeof borderWidth === 'number' && borderWidth > 0) {
43
- const ba = borderWidth / 2 + a;
44
- const bb = borderWidth / 2 + b;
41
+ if (a >= 0 && b >= 0) {
42
+ const opacity = getOpacity(viewElem) * parentOpacity;
43
+ ctx.globalAlpha = opacity;
45
44
  ctx.beginPath();
46
- ctx.strokeStyle = borderColor;
47
- ctx.lineWidth = borderWidth;
48
- ctx.circle(centerX, centerY, ba, bb, 0, 0, 2 * Math.PI);
45
+ const fillStyle = createColorStyle(ctx, background, {
46
+ viewElementSize: { x, y, w, h },
47
+ viewScaleInfo,
48
+ opacity: ctx.globalAlpha
49
+ });
50
+ ctx.fillStyle = fillStyle;
51
+ ctx.circle(centerX, centerY, radiusA, radiusB, 0, 0, 2 * Math.PI);
49
52
  ctx.closePath();
50
- ctx.stroke();
53
+ ctx.fill('nonzero');
54
+ ctx.globalAlpha = parentOpacity;
55
+ if (typeof bw === 'number' && bw > 0) {
56
+ const ba = bw / 2 + a;
57
+ const bb = bw / 2 + b;
58
+ ctx.beginPath();
59
+ if (borderDash) {
60
+ const lineDash = borderDash.map((n) => n * viewScaleInfo.scale);
61
+ ctx.setLineDash(lineDash);
62
+ }
63
+ ctx.strokeStyle = borderColor;
64
+ ctx.lineWidth = bw;
65
+ ctx.circle(centerX, centerY, ba, bb, 0, 0, 2 * Math.PI);
66
+ ctx.closePath();
67
+ ctx.stroke();
68
+ ctx.setLineDash([]);
69
+ }
51
70
  }
52
- ctx.beginPath();
53
- const fillStyle = createColorStyle(ctx, background, {
54
- viewElementSize: { x, y, w, h },
55
- viewScaleInfo,
56
- opacity: ctx.globalAlpha
57
- });
58
- ctx.fillStyle = fillStyle;
59
- ctx.circle(centerX, centerY, a, b, 0, 0, 2 * Math.PI);
60
- ctx.closePath();
61
- ctx.fill();
62
- ctx.globalAlpha = parentOpacity;
63
71
  }
64
72
  });
65
73
  });
@@ -11,7 +11,7 @@ export function drawElementList(ctx, data, opts) {
11
11
  detail: Object.assign(Object.assign({}, defaultDetail), element === null || element === void 0 ? void 0 : element.detail)
12
12
  });
13
13
  if (opts.forceDrawAll !== true) {
14
- if (!((_a = opts.calculator) === null || _a === void 0 ? void 0 : _a.isElementInView(elem, opts.viewScaleInfo, opts.viewSizeInfo))) {
14
+ if (!((_a = opts.calculator) === null || _a === void 0 ? void 0 : _a.needRender(elem))) {
15
15
  continue;
16
16
  }
17
17
  }
@@ -0,0 +1,2 @@
1
+ import type { RendererDrawElementOptions, ViewContext2D, ElementGlobal } from '@idraw/types';
2
+ export declare function drawGlobalBackground(ctx: ViewContext2D, global: ElementGlobal | undefined, opts: RendererDrawElementOptions): void;
@@ -0,0 +1,9 @@
1
+ export function drawGlobalBackground(ctx, global, opts) {
2
+ if (typeof (global === null || global === void 0 ? void 0 : global.background) === 'string') {
3
+ const { viewSizeInfo } = opts;
4
+ const { width, height } = viewSizeInfo;
5
+ ctx.globalAlpha = 1;
6
+ ctx.fillStyle = global.background;
7
+ ctx.fillRect(0, 0, width, height);
8
+ }
9
+ }
@@ -1,4 +1,4 @@
1
- import { rotateElement, calcViewBoxSize } from '@idraw/util';
1
+ import { rotateElement, calcViewBoxSize, calcViewElementSize } from '@idraw/util';
2
2
  import { drawCircle } from './circle';
3
3
  import { drawRect } from './rect';
4
4
  import { drawImage } from './image';
@@ -7,11 +7,21 @@ import { drawSVG } from './svg';
7
7
  import { drawHTML } from './html';
8
8
  import { drawBox, drawBoxShadow, getOpacity } from './box';
9
9
  import { drawPath } from './path';
10
+ const visiableMinSize = 0.4;
10
11
  export function drawElement(ctx, elem, opts) {
11
- var _a;
12
+ var _a, _b, _c;
12
13
  if (((_a = elem === null || elem === void 0 ? void 0 : elem.operations) === null || _a === void 0 ? void 0 : _a.invisible) === true) {
13
14
  return;
14
15
  }
16
+ const { w, h } = elem;
17
+ const { scale } = opts.viewScaleInfo;
18
+ if ((scale < 1 && (w * scale < visiableMinSize || h * scale < visiableMinSize)) || opts.parentOpacity === 0) {
19
+ return;
20
+ }
21
+ const { overrideElementMap } = opts;
22
+ if ((_c = (_b = overrideElementMap === null || overrideElementMap === void 0 ? void 0 : overrideElementMap[elem.uuid]) === null || _b === void 0 ? void 0 : _b.operations) === null || _c === void 0 ? void 0 : _c.invisible) {
23
+ return;
24
+ }
15
25
  try {
16
26
  switch (elem.type) {
17
27
  case 'rect': {
@@ -57,8 +67,8 @@ export function drawElement(ctx, elem, opts) {
57
67
  }
58
68
  }
59
69
  export function drawGroup(ctx, elem, opts) {
60
- const { calculator, viewScaleInfo, viewSizeInfo, parentOpacity } = opts;
61
- const { x, y, w, h, angle } = (calculator === null || calculator === void 0 ? void 0 : calculator.elementSize({ x: elem.x, y: elem.y, w: elem.w, h: elem.h, angle: elem.angle }, viewScaleInfo, viewSizeInfo)) || elem;
70
+ const { viewScaleInfo, viewSizeInfo, parentOpacity } = opts;
71
+ const { x, y, w, h, angle } = calcViewElementSize({ x: elem.x, y: elem.y, w: elem.w, h: elem.h, angle: elem.angle }, { viewScaleInfo }) || elem;
62
72
  const viewElem = Object.assign(Object.assign({}, elem), { x, y, w, h, angle });
63
73
  rotateElement(ctx, { x, y, w, h, angle }, () => {
64
74
  ctx.globalAlpha = getOpacity(elem) * parentOpacity;
@@ -87,8 +97,8 @@ export function drawGroup(ctx, elem, opts) {
87
97
  ctx.arcTo(x, y + h, x, y, radiusList[3]);
88
98
  ctx.arcTo(x, y, x + w, y, radiusList[0]);
89
99
  ctx.closePath();
90
- ctx.fill();
91
- ctx.clip();
100
+ ctx.fill('nonzero');
101
+ ctx.clip('nonzero');
92
102
  }
93
103
  if (Array.isArray(elem.detail.children)) {
94
104
  const { parentElementSize: parentSize } = opts;
@@ -107,7 +117,7 @@ export function drawGroup(ctx, elem, opts) {
107
117
  y: newParentSize.y + child.y
108
118
  });
109
119
  if (opts.forceDrawAll !== true) {
110
- if (!(calculator === null || calculator === void 0 ? void 0 : calculator.isElementInView(child, opts.viewScaleInfo, opts.viewSizeInfo))) {
120
+ if (!(calculator === null || calculator === void 0 ? void 0 : calculator.needRender(child))) {
111
121
  continue;
112
122
  }
113
123
  }
@@ -1,11 +1,11 @@
1
- import { rotateElement } from '@idraw/util';
1
+ import { rotateElement, calcViewElementSize } from '@idraw/util';
2
2
  import { getOpacity } from './box';
3
3
  export function drawHTML(ctx, elem, opts) {
4
4
  const content = opts.loader.getContent(elem);
5
- const { calculator, viewScaleInfo, viewSizeInfo, parentOpacity } = opts;
6
- const { x, y, w, h, angle } = (calculator === null || calculator === void 0 ? void 0 : calculator.elementSize(elem, viewScaleInfo, viewSizeInfo)) || elem;
5
+ const { viewScaleInfo, parentOpacity } = opts;
6
+ const { x, y, w, h, angle } = calcViewElementSize(elem, { viewScaleInfo }) || elem;
7
7
  rotateElement(ctx, { x, y, w, h, angle }, () => {
8
- if (!content) {
8
+ if (!content && !opts.loader.isDestroyed()) {
9
9
  opts.loader.load(elem, opts.elementAssets || {});
10
10
  }
11
11
  if (elem.type === 'html' && content) {