@idraw/renderer 0.4.0-beta.9 → 0.4.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.
@@ -18,7 +18,7 @@ var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (
18
18
  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");
19
19
  return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
20
20
  };
21
- var _Loader_instances, _Loader_loadFuncMap, _Loader_currentLoadItemMap, _Loader_storageLoadItemMap, _Loader_registerLoadFunc, _Loader_getLoadElementSource, _Loader_createLoadItem, _Loader_emitLoad, _Loader_emitError, _Loader_loadResource, _Loader_isExistingErrorStorage;
21
+ var _Loader_instances, _Loader_loadFuncMap, _Loader_currentLoadItemMap, _Loader_storageLoadItemMap, _Loader_hasDestroyed, _Loader_registerLoadFunc, _Loader_getLoadElementSource, _Loader_createLoadItem, _Loader_emitLoad, _Loader_emitError, _Loader_loadResource, _Loader_isExistingErrorStorage;
22
22
  import { loadImage, loadHTML, loadSVG, EventEmitter, createAssetId, isAssetId, createUUID } from '@idraw/util';
23
23
  const supportElementTypes = ['image', 'svg', 'html'];
24
24
  const getAssetIdFromElement = (element) => {
@@ -37,9 +37,9 @@ const getAssetIdFromElement = (element) => {
37
37
  if (isAssetId(source)) {
38
38
  return source;
39
39
  }
40
- return createAssetId(source);
40
+ return createAssetId(source, element.uuid);
41
41
  }
42
- return createAssetId(`${createUUID()}-${element.uuid}-${createUUID()}-${createUUID()}`);
42
+ return createAssetId(`${createUUID()}-${element.uuid}-${createUUID()}-${createUUID()}`, element.uuid);
43
43
  };
44
44
  export class Loader extends EventEmitter {
45
45
  constructor() {
@@ -48,6 +48,7 @@ export class Loader extends EventEmitter {
48
48
  _Loader_loadFuncMap.set(this, {});
49
49
  _Loader_currentLoadItemMap.set(this, {});
50
50
  _Loader_storageLoadItemMap.set(this, {});
51
+ _Loader_hasDestroyed.set(this, false);
51
52
  __classPrivateFieldGet(this, _Loader_instances, "m", _Loader_registerLoadFunc).call(this, 'image', (elem, assets) => __awaiter(this, void 0, void 0, function* () {
52
53
  var _a;
53
54
  const src = ((_a = assets[elem.detail.src]) === null || _a === void 0 ? void 0 : _a.value) || elem.detail.src;
@@ -59,8 +60,8 @@ export class Loader extends EventEmitter {
59
60
  };
60
61
  }));
61
62
  __classPrivateFieldGet(this, _Loader_instances, "m", _Loader_registerLoadFunc).call(this, 'html', (elem, assets) => __awaiter(this, void 0, void 0, function* () {
62
- var _b;
63
- const html = ((_b = assets[elem.detail.html]) === null || _b === void 0 ? void 0 : _b.value) || elem.detail.html;
63
+ var _a;
64
+ const html = ((_a = assets[elem.detail.html]) === null || _a === void 0 ? void 0 : _a.value) || elem.detail.html;
64
65
  const content = yield loadHTML(html, {
65
66
  width: elem.detail.originW || elem.w,
66
67
  height: elem.detail.originH || elem.h
@@ -72,8 +73,8 @@ export class Loader extends EventEmitter {
72
73
  };
73
74
  }));
74
75
  __classPrivateFieldGet(this, _Loader_instances, "m", _Loader_registerLoadFunc).call(this, 'svg', (elem, assets) => __awaiter(this, void 0, void 0, function* () {
75
- var _c;
76
- const svg = ((_c = assets[elem.detail.svg]) === null || _c === void 0 ? void 0 : _c.value) || elem.detail.svg;
76
+ var _a;
77
+ const svg = ((_a = assets[elem.detail.svg]) === null || _a === void 0 ? void 0 : _a.value) || elem.detail.svg;
77
78
  const content = yield loadSVG(svg);
78
79
  return {
79
80
  uuid: elem.uuid,
@@ -82,12 +83,56 @@ export class Loader extends EventEmitter {
82
83
  };
83
84
  }));
84
85
  }
86
+ isDestroyed() {
87
+ return __classPrivateFieldGet(this, _Loader_hasDestroyed, "f");
88
+ }
89
+ reset() {
90
+ if (__classPrivateFieldGet(this, _Loader_hasDestroyed, "f") === true) {
91
+ return;
92
+ }
93
+ __classPrivateFieldSet(this, _Loader_currentLoadItemMap, {}, "f");
94
+ __classPrivateFieldSet(this, _Loader_storageLoadItemMap, {}, "f");
95
+ }
96
+ resetElementAsset(element) {
97
+ var _a, _b, _c;
98
+ if (supportElementTypes.includes(element.type)) {
99
+ let assetId = null;
100
+ let resource = null;
101
+ if (element.type === 'image' && typeof ((_a = element === null || element === void 0 ? void 0 : element.detail) === null || _a === void 0 ? void 0 : _a.src) === 'string') {
102
+ resource = element.detail.src;
103
+ }
104
+ else if (element.type === 'svg' && typeof ((_b = element === null || element === void 0 ? void 0 : element.detail) === null || _b === void 0 ? void 0 : _b.svg) === 'string') {
105
+ resource = element.detail.svg;
106
+ }
107
+ else if (element.type === 'html' && typeof ((_c = element === null || element === void 0 ? void 0 : element.detail) === null || _c === void 0 ? void 0 : _c.html) === 'string') {
108
+ resource = element.detail.html;
109
+ }
110
+ if (typeof resource === 'string') {
111
+ this.load(element, {});
112
+ if (isAssetId(resource)) {
113
+ assetId = resource;
114
+ }
115
+ else if (element.uuid) {
116
+ assetId = createAssetId(resource, element.uuid);
117
+ }
118
+ }
119
+ if (assetId && isAssetId(assetId)) {
120
+ delete __classPrivateFieldGet(this, _Loader_storageLoadItemMap, "f")[assetId];
121
+ delete __classPrivateFieldGet(this, _Loader_currentLoadItemMap, "f")[assetId];
122
+ }
123
+ }
124
+ }
85
125
  destroy() {
126
+ __classPrivateFieldSet(this, _Loader_hasDestroyed, true, "f");
127
+ this.clear();
86
128
  __classPrivateFieldSet(this, _Loader_loadFuncMap, null, "f");
87
129
  __classPrivateFieldSet(this, _Loader_currentLoadItemMap, null, "f");
88
130
  __classPrivateFieldSet(this, _Loader_storageLoadItemMap, null, "f");
89
131
  }
90
132
  load(element, assets) {
133
+ if (__classPrivateFieldGet(this, _Loader_hasDestroyed, "f") === true) {
134
+ return;
135
+ }
91
136
  if (__classPrivateFieldGet(this, _Loader_instances, "m", _Loader_isExistingErrorStorage).call(this, element)) {
92
137
  return;
93
138
  }
@@ -107,7 +152,7 @@ export class Loader extends EventEmitter {
107
152
  __classPrivateFieldSet(this, _Loader_storageLoadItemMap, itemMap, "f");
108
153
  }
109
154
  }
110
- _Loader_loadFuncMap = new WeakMap(), _Loader_currentLoadItemMap = new WeakMap(), _Loader_storageLoadItemMap = new WeakMap(), _Loader_instances = new WeakSet(), _Loader_registerLoadFunc = function _Loader_registerLoadFunc(type, func) {
155
+ _Loader_loadFuncMap = new WeakMap(), _Loader_currentLoadItemMap = new WeakMap(), _Loader_storageLoadItemMap = new WeakMap(), _Loader_hasDestroyed = new WeakMap(), _Loader_instances = new WeakSet(), _Loader_registerLoadFunc = function _Loader_registerLoadFunc(type, func) {
111
156
  __classPrivateFieldGet(this, _Loader_loadFuncMap, "f")[type] = func;
112
157
  }, _Loader_getLoadElementSource = function _Loader_getLoadElementSource(element) {
113
158
  var _a, _b, _c;
@@ -135,42 +180,52 @@ _Loader_loadFuncMap = new WeakMap(), _Loader_currentLoadItemMap = new WeakMap(),
135
180
  }, _Loader_emitLoad = function _Loader_emitLoad(item) {
136
181
  const assetId = getAssetIdFromElement(item.element);
137
182
  const storageItem = __classPrivateFieldGet(this, _Loader_storageLoadItemMap, "f")[assetId];
138
- if (storageItem) {
139
- if (storageItem.startTime < item.startTime) {
183
+ if (!__classPrivateFieldGet(this, _Loader_hasDestroyed, "f")) {
184
+ if (storageItem) {
185
+ if (storageItem.startTime < item.startTime) {
186
+ __classPrivateFieldGet(this, _Loader_storageLoadItemMap, "f")[assetId] = item;
187
+ this.trigger('load', Object.assign(Object.assign({}, item), { countTime: item.endTime - item.startTime }));
188
+ }
189
+ }
190
+ else {
140
191
  __classPrivateFieldGet(this, _Loader_storageLoadItemMap, "f")[assetId] = item;
141
192
  this.trigger('load', Object.assign(Object.assign({}, item), { countTime: item.endTime - item.startTime }));
142
193
  }
143
194
  }
144
- else {
145
- __classPrivateFieldGet(this, _Loader_storageLoadItemMap, "f")[assetId] = item;
146
- this.trigger('load', Object.assign(Object.assign({}, item), { countTime: item.endTime - item.startTime }));
147
- }
148
195
  }, _Loader_emitError = function _Loader_emitError(item) {
196
+ var _a;
149
197
  const assetId = getAssetIdFromElement(item.element);
150
- const storageItem = __classPrivateFieldGet(this, _Loader_storageLoadItemMap, "f")[assetId];
151
- if (storageItem) {
152
- if (storageItem.startTime < item.startTime) {
198
+ const storageItem = (_a = __classPrivateFieldGet(this, _Loader_storageLoadItemMap, "f")) === null || _a === void 0 ? void 0 : _a[assetId];
199
+ if (!__classPrivateFieldGet(this, _Loader_hasDestroyed, "f")) {
200
+ if (storageItem) {
201
+ if (storageItem.startTime < item.startTime) {
202
+ __classPrivateFieldGet(this, _Loader_storageLoadItemMap, "f")[assetId] = item;
203
+ this.trigger('error', Object.assign(Object.assign({}, item), { countTime: item.endTime - item.startTime }));
204
+ }
205
+ }
206
+ else {
153
207
  __classPrivateFieldGet(this, _Loader_storageLoadItemMap, "f")[assetId] = item;
154
208
  this.trigger('error', Object.assign(Object.assign({}, item), { countTime: item.endTime - item.startTime }));
155
209
  }
156
210
  }
157
- else {
158
- __classPrivateFieldGet(this, _Loader_storageLoadItemMap, "f")[assetId] = item;
159
- this.trigger('error', Object.assign(Object.assign({}, item), { countTime: item.endTime - item.startTime }));
160
- }
161
211
  }, _Loader_loadResource = function _Loader_loadResource(element, assets) {
162
212
  const item = __classPrivateFieldGet(this, _Loader_instances, "m", _Loader_createLoadItem).call(this, element);
163
213
  const assetId = getAssetIdFromElement(element);
214
+ if (__classPrivateFieldGet(this, _Loader_currentLoadItemMap, "f")[assetId]) {
215
+ return;
216
+ }
164
217
  __classPrivateFieldGet(this, _Loader_currentLoadItemMap, "f")[assetId] = item;
165
218
  const loadFunc = __classPrivateFieldGet(this, _Loader_loadFuncMap, "f")[element.type];
166
- if (typeof loadFunc === 'function') {
219
+ if (typeof loadFunc === 'function' && !__classPrivateFieldGet(this, _Loader_hasDestroyed, "f")) {
167
220
  item.startTime = Date.now();
168
221
  loadFunc(element, assets)
169
222
  .then((result) => {
170
- item.content = result.content;
171
- item.endTime = Date.now();
172
- item.status = 'load';
173
- __classPrivateFieldGet(this, _Loader_instances, "m", _Loader_emitLoad).call(this, item);
223
+ if (!__classPrivateFieldGet(this, _Loader_hasDestroyed, "f")) {
224
+ item.content = result.content;
225
+ item.endTime = Date.now();
226
+ item.status = 'load';
227
+ __classPrivateFieldGet(this, _Loader_instances, "m", _Loader_emitLoad).call(this, item);
228
+ }
174
229
  })
175
230
  .catch((err) => {
176
231
  console.warn(`Load element source "${item.source}" fail`, err, element);
@@ -184,7 +239,10 @@ _Loader_loadFuncMap = new WeakMap(), _Loader_currentLoadItemMap = new WeakMap(),
184
239
  var _a;
185
240
  const assetId = getAssetIdFromElement(element);
186
241
  const existItem = (_a = __classPrivateFieldGet(this, _Loader_currentLoadItemMap, "f")) === null || _a === void 0 ? void 0 : _a[assetId];
187
- if (existItem && existItem.status === 'error' && existItem.source && existItem.source === __classPrivateFieldGet(this, _Loader_instances, "m", _Loader_getLoadElementSource).call(this, element)) {
242
+ if (existItem &&
243
+ existItem.status === 'error' &&
244
+ existItem.source &&
245
+ existItem.source === __classPrivateFieldGet(this, _Loader_instances, "m", _Loader_getLoadElementSource).call(this, element)) {
188
246
  return true;
189
247
  }
190
248
  return false;
@@ -0,0 +1,22 @@
1
+ import { Elements, ViewScaleInfo, ViewSizeInfo, ViewRectInfo, VirtualFlatItemMap, ViewContext2D } from '@idraw/types';
2
+ export declare function sortElementsViewVisiableInfoMap(elements: Elements, opts: {
3
+ viewScaleInfo: ViewScaleInfo;
4
+ viewSizeInfo: ViewSizeInfo;
5
+ tempContext: ViewContext2D;
6
+ }): {
7
+ virtualFlatItemMap: VirtualFlatItemMap;
8
+ visibleCount: number;
9
+ invisibleCount: number;
10
+ };
11
+ export declare function updateVirtualFlatItemMapStatus(virtualFlatItemMap: VirtualFlatItemMap, opts: {
12
+ viewScaleInfo: ViewScaleInfo;
13
+ viewSizeInfo: ViewSizeInfo;
14
+ }): {
15
+ virtualFlatItemMap: VirtualFlatItemMap;
16
+ visibleCount: number;
17
+ invisibleCount: number;
18
+ };
19
+ export declare function calcVisibleOriginCanvasRectInfo(opts: {
20
+ viewScaleInfo: ViewScaleInfo;
21
+ viewSizeInfo: ViewSizeInfo;
22
+ }): ViewRectInfo;
@@ -0,0 +1,63 @@
1
+ import { calcElementCenter } from '@idraw/util';
2
+ import { elementsToVirtualFlatMap } from '../virtual-flat';
3
+ export function sortElementsViewVisiableInfoMap(elements, opts) {
4
+ const { viewScaleInfo, viewSizeInfo, tempContext } = opts;
5
+ const visibleInfoMap = elementsToVirtualFlatMap(elements, { tempContext });
6
+ return updateVirtualFlatItemMapStatus(visibleInfoMap, { viewScaleInfo, viewSizeInfo });
7
+ }
8
+ function isRangeRectInfoCollide(info1, info2) {
9
+ const rect1MinX = Math.min(info1.topLeft.x, info1.topRight.x, info1.bottomLeft.x, info1.bottomRight.x);
10
+ const rect1MaxX = Math.max(info1.topLeft.x, info1.topRight.x, info1.bottomLeft.x, info1.bottomRight.x);
11
+ const rect1MinY = Math.min(info1.topLeft.y, info1.topRight.y, info1.bottomLeft.y, info1.bottomRight.y);
12
+ const rect1MaxY = Math.max(info1.topLeft.y, info1.topRight.y, info1.bottomLeft.y, info1.bottomRight.y);
13
+ const rect2MinX = Math.min(info2.topLeft.x, info2.topRight.x, info2.bottomLeft.x, info2.bottomRight.x);
14
+ const rect2MaxX = Math.max(info2.topLeft.x, info2.topRight.x, info2.bottomLeft.x, info2.bottomRight.x);
15
+ const rect2MinY = Math.min(info2.topLeft.y, info2.topRight.y, info2.bottomLeft.y, info2.bottomRight.y);
16
+ const rect2MaxY = Math.max(info2.topLeft.y, info2.topRight.y, info2.bottomLeft.y, info2.bottomRight.y);
17
+ if ((rect1MinX <= rect2MaxX && rect1MaxX >= rect2MinX && rect1MinY <= rect2MaxY && rect1MaxY >= rect2MinY) ||
18
+ (rect2MaxX <= rect1MaxY && rect2MaxX >= rect1MaxY && rect2MaxX <= rect1MaxY && rect2MaxX >= rect1MaxY)) {
19
+ return true;
20
+ }
21
+ return false;
22
+ }
23
+ export function updateVirtualFlatItemMapStatus(virtualFlatItemMap, opts) {
24
+ const canvasRectInfo = calcVisibleOriginCanvasRectInfo(opts);
25
+ let visibleCount = 0;
26
+ let invisibleCount = 0;
27
+ Object.keys(virtualFlatItemMap).forEach((uuid) => {
28
+ const info = virtualFlatItemMap[uuid];
29
+ info.isVisibleInView = isRangeRectInfoCollide(info.rangeRectInfo, canvasRectInfo);
30
+ info.isVisibleInView ? visibleCount++ : invisibleCount++;
31
+ });
32
+ return { virtualFlatItemMap, visibleCount, invisibleCount };
33
+ }
34
+ export function calcVisibleOriginCanvasRectInfo(opts) {
35
+ const { viewScaleInfo, viewSizeInfo } = opts;
36
+ const { scale, offsetTop, offsetLeft } = viewScaleInfo;
37
+ const { width, height } = viewSizeInfo;
38
+ const x = 0 - offsetLeft / scale;
39
+ const y = 0 - offsetTop / scale;
40
+ const w = width / scale;
41
+ const h = height / scale;
42
+ const center = calcElementCenter({ x, y, w, h });
43
+ const topLeft = { x, y };
44
+ const topRight = { x: x + w, y };
45
+ const bottomLeft = { x, y: y + h };
46
+ const bottomRight = { x: x + w, y: y + h };
47
+ const left = { x, y: center.y };
48
+ const top = { x: center.x, y };
49
+ const right = { x: x + w, y: center.y };
50
+ const bottom = { x: center.x, y: y + h };
51
+ const rectInfo = {
52
+ center,
53
+ topLeft,
54
+ topRight,
55
+ bottomLeft,
56
+ bottomRight,
57
+ left,
58
+ top,
59
+ right,
60
+ bottom
61
+ };
62
+ return rectInfo;
63
+ }
@@ -0,0 +1,7 @@
1
+ import { Element, Elements, VirtualFlatItemMap, VirtualFlatDetail, ViewContext2D } from '@idraw/types';
2
+ export declare function calcVirtualFlatDetail(elem: Element, opts: {
3
+ tempContext: ViewContext2D;
4
+ }): VirtualFlatDetail;
5
+ export declare function elementsToVirtualFlatMap(elements: Elements, opts: {
6
+ tempContext: ViewContext2D;
7
+ }): VirtualFlatItemMap;
@@ -0,0 +1,45 @@
1
+ import { is, getGroupQueueByElementPosition, calcElementOriginRectInfo, originRectInfoToRangeRectInfo } from '@idraw/util';
2
+ import { calcVirtualTextDetail } from './text';
3
+ export function calcVirtualFlatDetail(elem, opts) {
4
+ let virtualDetail = {};
5
+ if (elem.type === 'text') {
6
+ virtualDetail = calcVirtualTextDetail(elem, opts);
7
+ }
8
+ return virtualDetail;
9
+ }
10
+ export function elementsToVirtualFlatMap(elements, opts) {
11
+ const virtualFlatMap = {};
12
+ const currentPosition = [];
13
+ const _walk = (elem) => {
14
+ const baseInfo = {
15
+ type: elem.type,
16
+ isVisibleInView: true,
17
+ position: [...currentPosition]
18
+ };
19
+ let originRectInfo = null;
20
+ const groupQueue = getGroupQueueByElementPosition(elements, currentPosition);
21
+ originRectInfo = calcElementOriginRectInfo(elem, {
22
+ groupQueue: groupQueue || []
23
+ });
24
+ const virtualItem = Object.assign(Object.assign(Object.assign({}, baseInfo), {
25
+ originRectInfo: originRectInfo,
26
+ rangeRectInfo: is.angle(elem.angle)
27
+ ? originRectInfoToRangeRectInfo(originRectInfo)
28
+ : originRectInfo
29
+ }), calcVirtualFlatDetail(elem, opts));
30
+ virtualFlatMap[elem.uuid] = virtualItem;
31
+ if (elem.type === 'group') {
32
+ elem.detail.children.forEach((ele, i) => {
33
+ currentPosition.push(i);
34
+ _walk(ele);
35
+ currentPosition.pop();
36
+ });
37
+ }
38
+ };
39
+ elements.forEach((elem, index) => {
40
+ currentPosition.push(index);
41
+ _walk(elem);
42
+ currentPosition.pop();
43
+ });
44
+ return virtualFlatMap;
45
+ }
@@ -0,0 +1,2 @@
1
+ import type { Element, CalcVirtualDetailOptions, VirtualFlatTextDetail } from '@idraw/types';
2
+ export declare function calcVirtualTextDetail(elem: Element<'text'>, opts: CalcVirtualDetailOptions): VirtualFlatTextDetail;
@@ -0,0 +1,151 @@
1
+ import { enhanceFontFamliy, getDefaultElementDetailConfig } from '@idraw/util';
2
+ const detailConfig = getDefaultElementDetailConfig();
3
+ function isTextWidthWithinErrorRange(w0, w1, scale) {
4
+ if (scale < 0.5) {
5
+ if (w0 < w1 && (w0 - w1) / w0 > -0.15) {
6
+ return true;
7
+ }
8
+ }
9
+ return w0 >= w1;
10
+ }
11
+ export function calcVirtualTextDetail(elem, opts) {
12
+ const { w, h } = elem;
13
+ const x = 0;
14
+ const y = 0;
15
+ const ctx = opts.tempContext;
16
+ const lines = [];
17
+ const detail = Object.assign(Object.assign({}, detailConfig), elem.detail);
18
+ const originFontSize = detail.fontSize || detailConfig.fontSize;
19
+ const fontSize = originFontSize;
20
+ if (fontSize < 2) {
21
+ return {};
22
+ }
23
+ const originLineHeight = detail.lineHeight || originFontSize;
24
+ const lineHeight = originLineHeight;
25
+ ctx.textBaseline = 'top';
26
+ ctx.$setFont({
27
+ fontWeight: detail.fontWeight,
28
+ fontSize: fontSize,
29
+ fontFamily: enhanceFontFamliy(detail.fontFamily)
30
+ });
31
+ let detailText = detail.text.replace(/\r\n/gi, '\n');
32
+ if (detail.textTransform === 'lowercase') {
33
+ detailText = detailText.toLowerCase();
34
+ }
35
+ else if (detail.textTransform === 'uppercase') {
36
+ detailText = detailText.toUpperCase();
37
+ }
38
+ const fontHeight = lineHeight;
39
+ const detailTextList = detailText.split('\n');
40
+ let lineNum = 0;
41
+ detailTextList.forEach((itemText, idx) => {
42
+ if (detail.minInlineSize === 'maxContent') {
43
+ lines.push({
44
+ x,
45
+ y: 0,
46
+ text: itemText,
47
+ width: ctx.$undoPixelRatio(ctx.measureText(itemText).width)
48
+ });
49
+ }
50
+ else {
51
+ let lineText = '';
52
+ let splitStr = '';
53
+ let tempStrList = itemText.split(splitStr);
54
+ if (detail.wordBreak === 'normal') {
55
+ splitStr = ' ';
56
+ const wordList = itemText.split(splitStr);
57
+ tempStrList = [];
58
+ wordList.forEach((word, idx) => {
59
+ tempStrList.push(word);
60
+ if (idx < wordList.length - 1) {
61
+ tempStrList.push(splitStr);
62
+ }
63
+ });
64
+ }
65
+ if (tempStrList.length === 1 && detail.overflow === 'visible') {
66
+ lines.push({
67
+ x,
68
+ y: 0,
69
+ text: tempStrList[0],
70
+ width: ctx.$undoPixelRatio(ctx.measureText(tempStrList[0]).width)
71
+ });
72
+ }
73
+ else if (tempStrList.length > 0) {
74
+ for (let i = 0; i < tempStrList.length; i++) {
75
+ if (isTextWidthWithinErrorRange(ctx.$doPixelRatio(w), ctx.measureText(lineText + tempStrList[i]).width, 1)) {
76
+ lineText += tempStrList[i] || '';
77
+ }
78
+ else {
79
+ lines.push({
80
+ x,
81
+ y: 0,
82
+ text: lineText,
83
+ width: ctx.$undoPixelRatio(ctx.measureText(lineText).width)
84
+ });
85
+ lineText = tempStrList[i] || '';
86
+ lineNum++;
87
+ }
88
+ if ((lineNum + 1) * fontHeight > h && detail.overflow === 'hidden') {
89
+ break;
90
+ }
91
+ if (tempStrList.length - 1 === i) {
92
+ if ((lineNum + 1) * fontHeight <= h) {
93
+ lines.push({
94
+ x,
95
+ y: 0,
96
+ text: lineText,
97
+ width: ctx.$undoPixelRatio(ctx.measureText(lineText).width)
98
+ });
99
+ if (idx < detailTextList.length - 1) {
100
+ lineNum++;
101
+ }
102
+ break;
103
+ }
104
+ }
105
+ }
106
+ }
107
+ else {
108
+ lines.push({
109
+ x,
110
+ y: 0,
111
+ text: '',
112
+ width: 0
113
+ });
114
+ }
115
+ }
116
+ });
117
+ let startY = 0;
118
+ let eachLineStartY = 0;
119
+ if (fontHeight > fontSize) {
120
+ eachLineStartY = (fontHeight - fontSize) / 2;
121
+ }
122
+ if (lines.length * fontHeight < h) {
123
+ if (detail.verticalAlign === 'top') {
124
+ startY = 0;
125
+ }
126
+ else if (detail.verticalAlign === 'bottom') {
127
+ startY += h - lines.length * fontHeight;
128
+ }
129
+ else {
130
+ startY += (h - lines.length * fontHeight) / 2;
131
+ }
132
+ }
133
+ {
134
+ const _y = y + startY;
135
+ lines.forEach((line, i) => {
136
+ let _x = x;
137
+ if (detail.textAlign === 'center') {
138
+ _x = x + (w - line.width) / 2;
139
+ }
140
+ else if (detail.textAlign === 'right') {
141
+ _x = x + (w - line.width);
142
+ }
143
+ lines[i].x = _x;
144
+ lines[i].y = _y + fontHeight * i + eachLineStartY;
145
+ });
146
+ }
147
+ const virtualTextDetail = {
148
+ textLines: lines
149
+ };
150
+ return virtualTextDetail;
151
+ }