mobility-toolbox-js 1.6.0-beta.4 → 1.6.0-beta.8
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/api/tralis/TralisAPI.js +1 -1
- package/api/tralis/WebSocketConnector.js +8 -8
- package/common/layers/Layer.js +10 -7
- package/common/layers/Layer.test.js +27 -0
- package/common/mixins/TrackerLayerMixin.js +99 -1
- package/common/mixins/TrajservLayerMixin.js +76 -159
- package/common/mixins/TralisLayerMixin.js +45 -10
- package/common/trackerConfig.js +16 -13
- package/common/trackerConfig.test.js +38 -0
- package/common/utils/createTrackerFilters.js +82 -0
- package/common/utils/createTrackerFilters.test.js +89 -0
- package/common/utils/index.js +1 -0
- package/common/utils/trackerStyle.js +201 -0
- package/index.js +1 -1
- package/index.js.map +1 -1
- package/mapbox/layers/Layer.js +32 -6
- package/mapbox/layers/Layer.test.js +122 -0
- package/mapbox/layers/TrackerLayer.js +3 -3
- package/mapbox/layers/TrajservLayer.js +4 -88
- package/mapbox/layers/TralisLayer.js +0 -1
- package/ol/layers/Layer.js +39 -8
- package/ol/layers/Layer.test.js +81 -0
- package/ol/layers/TrackerLayer.js +24 -7
- package/ol/layers/TrackerLayer.test.js +8 -0
- package/ol/layers/TrajservLayer.js +34 -126
- package/ol/layers/TralisLayer.js +102 -0
- package/package.json +1 -1
package/common/trackerConfig.js
CHANGED
|
@@ -12,19 +12,23 @@ const trackerRadiusMapping = {
|
|
|
12
12
|
};
|
|
13
13
|
|
|
14
14
|
/**
|
|
15
|
+
* Trajserv value: 'Tram', 'Subway / Metro / S-Bahn', 'Train', 'Bus', 'Ferry', 'Cable Car', 'Gondola', 'Funicular', 'Long distance bus', 'Rail',
|
|
16
|
+
* New endpoint use Rail instead of Train.
|
|
17
|
+
* New tracker values: null, "tram", "subway", "rail", "bus", "ferry", "cablecar", "gondola", "funicular", "coach".
|
|
18
|
+
*
|
|
15
19
|
* @ignore
|
|
16
20
|
*/
|
|
17
21
|
export const types = [
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
22
|
+
/^Tram/i,
|
|
23
|
+
/^Subway( \/ Metro \/ S-Bahn)?/i,
|
|
24
|
+
/^Train/i,
|
|
25
|
+
/^Bus/i,
|
|
26
|
+
/^Ferry/i,
|
|
27
|
+
/^Cable ?Car/i,
|
|
28
|
+
/^Gondola/i,
|
|
29
|
+
/^Funicular/i,
|
|
30
|
+
/^(Long distance bus|coach)/i,
|
|
31
|
+
/^Rail/i, // New endpoint use Rail instead of Train.
|
|
28
32
|
];
|
|
29
33
|
|
|
30
34
|
/**
|
|
@@ -89,10 +93,9 @@ export const timeSteps = [
|
|
|
89
93
|
/**
|
|
90
94
|
* @ignore
|
|
91
95
|
*/
|
|
92
|
-
const getTypeIndex = (type) => {
|
|
96
|
+
export const getTypeIndex = (type) => {
|
|
93
97
|
if (typeof type === 'string') {
|
|
94
|
-
|
|
95
|
-
return types.indexOf(matched);
|
|
98
|
+
return types.findIndex((t) => t.test(type));
|
|
96
99
|
}
|
|
97
100
|
return type;
|
|
98
101
|
};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { getTypeIndex } from './trackerConfig';
|
|
2
|
+
|
|
3
|
+
describe('trackerConfig', () => {
|
|
4
|
+
describe('#getTypeIndex()', () => {
|
|
5
|
+
test("retrurn the type is it's not a string", () => {
|
|
6
|
+
const obj = { foo: 'foo' };
|
|
7
|
+
expect(getTypeIndex(obj)).toBe(obj);
|
|
8
|
+
expect(getTypeIndex(0)).toBe(0);
|
|
9
|
+
expect(getTypeIndex(null)).toBe(null);
|
|
10
|
+
expect(getTypeIndex(undefined)).toBe(undefined);
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
test('find good index for old trajserv values', () => {
|
|
14
|
+
expect(getTypeIndex('Tram')).toBe(0);
|
|
15
|
+
expect(getTypeIndex('Subway / Metro / S-Bahn')).toBe(1);
|
|
16
|
+
expect(getTypeIndex('Train')).toBe(2);
|
|
17
|
+
expect(getTypeIndex('Bus')).toBe(3);
|
|
18
|
+
expect(getTypeIndex('Ferry')).toBe(4);
|
|
19
|
+
expect(getTypeIndex('Cable Car')).toBe(5);
|
|
20
|
+
expect(getTypeIndex('Gondola')).toBe(6);
|
|
21
|
+
expect(getTypeIndex('Funicular')).toBe(7);
|
|
22
|
+
expect(getTypeIndex('Long distance bus')).toBe(8);
|
|
23
|
+
expect(getTypeIndex('Rail')).toBe(9);
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
test('find good index for new tracker values', () => {
|
|
27
|
+
expect(getTypeIndex('tram')).toBe(0);
|
|
28
|
+
expect(getTypeIndex('subway')).toBe(1);
|
|
29
|
+
expect(getTypeIndex('bus')).toBe(3);
|
|
30
|
+
expect(getTypeIndex('ferry')).toBe(4);
|
|
31
|
+
expect(getTypeIndex('cablecar')).toBe(5);
|
|
32
|
+
expect(getTypeIndex('gondola')).toBe(6);
|
|
33
|
+
expect(getTypeIndex('funicular')).toBe(7);
|
|
34
|
+
expect(getTypeIndex('coach')).toBe(8);
|
|
35
|
+
expect(getTypeIndex('rail')).toBe(9);
|
|
36
|
+
});
|
|
37
|
+
});
|
|
38
|
+
});
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Return a filter functions based on some parameters of a vehicle.
|
|
3
|
+
*
|
|
4
|
+
* @param {string|Array<string>} line - A list of vehicle's name to filter. Names can be separated by a comma. Ex: 'S1,S2,S3'
|
|
5
|
+
* @param {string|Array<string} route - A list of vehicle's route (contained in routeIdentifier property) to filter. Indentifiers can be separated by a comma. Ex: 'id1,id2,id3'
|
|
6
|
+
* @param {string|Array<string} operator A list of vehicle's operator to filter. Operators can be separated by a comma. Ex: 'SBB,DB'
|
|
7
|
+
* @param {Regexp} regexLine - A regex aplly of vehcile's name.
|
|
8
|
+
* @private
|
|
9
|
+
*/
|
|
10
|
+
const createFilters = (line, route, operator, regexLine) => {
|
|
11
|
+
const filterList = [];
|
|
12
|
+
|
|
13
|
+
if (!line && !route && !operator && !regexLine) {
|
|
14
|
+
return null;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
if (regexLine) {
|
|
18
|
+
const regexLineList =
|
|
19
|
+
typeof regexLine === 'string' ? [regexLine] : regexLine;
|
|
20
|
+
const lineFilter = (item) => {
|
|
21
|
+
const name = item.name || (item.line && item.line.name) || '';
|
|
22
|
+
if (!name) {
|
|
23
|
+
return false;
|
|
24
|
+
}
|
|
25
|
+
return regexLineList.some((regexStr) =>
|
|
26
|
+
new RegExp(regexStr, 'i').test(name),
|
|
27
|
+
);
|
|
28
|
+
};
|
|
29
|
+
filterList.push(lineFilter);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if (line) {
|
|
33
|
+
const lineFiltersList = typeof line === 'string' ? line.split(',') : line;
|
|
34
|
+
const lineList = lineFiltersList.map((l) =>
|
|
35
|
+
l.replace(/\s+/g, '').toUpperCase(),
|
|
36
|
+
);
|
|
37
|
+
const lineFilter = (item) => {
|
|
38
|
+
const name = (
|
|
39
|
+
item.name ||
|
|
40
|
+
(item.line && item.line.name) ||
|
|
41
|
+
''
|
|
42
|
+
).toUpperCase();
|
|
43
|
+
if (!name) {
|
|
44
|
+
return false;
|
|
45
|
+
}
|
|
46
|
+
return lineList.includes(name);
|
|
47
|
+
};
|
|
48
|
+
filterList.push(lineFilter);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (route) {
|
|
52
|
+
const routes = typeof route === 'string' ? route.split(',') : route;
|
|
53
|
+
const routeList = routes.map((item) => parseInt(item, 10));
|
|
54
|
+
const routeFilter = (item) => {
|
|
55
|
+
const routeId = parseInt(item.routeIdentifier.split('.')[0], 10);
|
|
56
|
+
return routeList.includes(routeId);
|
|
57
|
+
};
|
|
58
|
+
filterList.push(routeFilter);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (operator) {
|
|
62
|
+
const operatorList = typeof operator === 'string' ? [operator] : operator;
|
|
63
|
+
const operatorFilter = (item) =>
|
|
64
|
+
operatorList.some((op) => new RegExp(op, 'i').test(item.operator));
|
|
65
|
+
filterList.push(operatorFilter);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (!filterList.length) {
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return (t) => {
|
|
73
|
+
for (let i = 0; i < filterList.length; i += 1) {
|
|
74
|
+
if (!filterList[i](t)) {
|
|
75
|
+
return false;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return true;
|
|
79
|
+
};
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
export default createFilters;
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import createTrackerFilters from './createTrackerFilters';
|
|
2
|
+
|
|
3
|
+
const u1 = {
|
|
4
|
+
routeIdentifier: '001.000827.004:7',
|
|
5
|
+
operator: 'FoO',
|
|
6
|
+
line: {
|
|
7
|
+
name: 'U1',
|
|
8
|
+
},
|
|
9
|
+
};
|
|
10
|
+
const ireta = {
|
|
11
|
+
routeIdentifier: '0022.000827.004:7',
|
|
12
|
+
operator: 'BAR',
|
|
13
|
+
line: {
|
|
14
|
+
name: 'IRETA',
|
|
15
|
+
},
|
|
16
|
+
};
|
|
17
|
+
const arb = {
|
|
18
|
+
routeIdentifier: '00333.000827.004:7',
|
|
19
|
+
operator: 'qux',
|
|
20
|
+
line: {
|
|
21
|
+
name: 'ARB',
|
|
22
|
+
},
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
const trajectories = [u1, ireta, arb];
|
|
26
|
+
|
|
27
|
+
describe('#createTrackerFilter()', () => {
|
|
28
|
+
test('returns null', () => {
|
|
29
|
+
const filterFunc = createTrackerFilters();
|
|
30
|
+
expect(filterFunc).toBe(null);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
describe('using line', () => {
|
|
34
|
+
test('as string', () => {
|
|
35
|
+
const filterFunc = createTrackerFilters('u1,foo');
|
|
36
|
+
expect(trajectories.filter(filterFunc)).toEqual([u1]);
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
test('as array of string', () => {
|
|
40
|
+
const filterFunc = createTrackerFilters(['u1', 'foo', 'IRETA']);
|
|
41
|
+
expect(trajectories.filter(filterFunc)).toEqual([u1, ireta]);
|
|
42
|
+
});
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
describe('using route identifier', () => {
|
|
46
|
+
test('as string', () => {
|
|
47
|
+
const filterFunc = createTrackerFilters(null, '1,foo');
|
|
48
|
+
expect(trajectories.filter(filterFunc)).toEqual([u1]);
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
test('as array of string', () => {
|
|
52
|
+
const filterFunc = createTrackerFilters(null, ['22', 'foo', '1']);
|
|
53
|
+
expect(trajectories.filter(filterFunc)).toEqual([u1, ireta]);
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
describe('using operator', () => {
|
|
58
|
+
test('as string', () => {
|
|
59
|
+
const filterFunc = createTrackerFilters(null, null, 'foo');
|
|
60
|
+
expect(trajectories.filter(filterFunc)).toEqual([u1]);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
test('as array of string', () => {
|
|
64
|
+
const filterFunc = createTrackerFilters(null, null, ['bar', 'foo', '1']);
|
|
65
|
+
expect(trajectories.filter(filterFunc)).toEqual([u1, ireta]);
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
describe('using regexLine', () => {
|
|
70
|
+
test('as string', () => {
|
|
71
|
+
const filterFunc = createTrackerFilters(
|
|
72
|
+
null,
|
|
73
|
+
null,
|
|
74
|
+
null,
|
|
75
|
+
'^(S|R$|RE|PE|D|IRE|RB|TER)',
|
|
76
|
+
);
|
|
77
|
+
expect(trajectories.filter(filterFunc)).toEqual([ireta]);
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
test('as array of string', () => {
|
|
81
|
+
const filterFunc = createTrackerFilters(null, null, null, [
|
|
82
|
+
'^IR',
|
|
83
|
+
'^ARB$',
|
|
84
|
+
'foo',
|
|
85
|
+
]);
|
|
86
|
+
expect(trajectories.filter(filterFunc)).toEqual([ireta, arb]);
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
});
|
package/common/utils/index.js
CHANGED
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
import {
|
|
2
|
+
getRadius,
|
|
3
|
+
getBgColor,
|
|
4
|
+
getDelayColor,
|
|
5
|
+
getDelayText,
|
|
6
|
+
getTextColor,
|
|
7
|
+
getTextSize,
|
|
8
|
+
} from '../trackerConfig';
|
|
9
|
+
|
|
10
|
+
const styleCache = {};
|
|
11
|
+
|
|
12
|
+
const style = (trajectory, viewState, trackerLayer) => {
|
|
13
|
+
const {
|
|
14
|
+
hoverVehicleId,
|
|
15
|
+
selectedVehicleId,
|
|
16
|
+
useDelayStyle,
|
|
17
|
+
delayOutlineColor,
|
|
18
|
+
delayDisplay,
|
|
19
|
+
} = trackerLayer;
|
|
20
|
+
|
|
21
|
+
const {
|
|
22
|
+
zoom,
|
|
23
|
+
pixelRatio,
|
|
24
|
+
operator_provides_realtime_journey: operatorProvidesRealtime,
|
|
25
|
+
} = viewState;
|
|
26
|
+
let { line, type } = trajectory;
|
|
27
|
+
const { id, delay, cancelled = false } = trajectory;
|
|
28
|
+
|
|
29
|
+
if (!type) {
|
|
30
|
+
type = 'Rail';
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (!line) {
|
|
34
|
+
line = {};
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
let { name, text_color: textColor, color } = line;
|
|
38
|
+
|
|
39
|
+
if (!name) {
|
|
40
|
+
name = 'I';
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if (!textColor) {
|
|
44
|
+
textColor = '#000000';
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if (color && color[0] !== '#') {
|
|
48
|
+
color = `#${color}`;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (textColor[0] !== '#') {
|
|
52
|
+
textColor = `#${textColor}`;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const z = Math.min(Math.floor(zoom || 1), 16);
|
|
56
|
+
const hover = hoverVehicleId === id;
|
|
57
|
+
const selected = selectedVehicleId === id;
|
|
58
|
+
|
|
59
|
+
// Calcul the radius of the circle
|
|
60
|
+
let radius = getRadius(type, z) * pixelRatio;
|
|
61
|
+
const isDisplayStrokeAndDelay = radius >= 7 * pixelRatio;
|
|
62
|
+
if (hover || selected) {
|
|
63
|
+
radius = isDisplayStrokeAndDelay
|
|
64
|
+
? radius + 5 * pixelRatio
|
|
65
|
+
: 14 * pixelRatio;
|
|
66
|
+
}
|
|
67
|
+
const mustDrawText = radius > 10 * pixelRatio;
|
|
68
|
+
|
|
69
|
+
// Optimize the cache key, very important in high zoom level
|
|
70
|
+
let key = `${z}${type}${color}${hover}${selected}${cancelled}${delay}`;
|
|
71
|
+
|
|
72
|
+
if (useDelayStyle) {
|
|
73
|
+
key += `${operatorProvidesRealtime}`;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
if (mustDrawText) {
|
|
77
|
+
key += `${name}${textColor}`;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (!styleCache[key]) {
|
|
81
|
+
if (radius === 0) {
|
|
82
|
+
styleCache[key] = null;
|
|
83
|
+
return null;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const margin = 1 * pixelRatio;
|
|
87
|
+
const radiusDelay = radius + 2;
|
|
88
|
+
const markerSize = radius * 2;
|
|
89
|
+
|
|
90
|
+
const canvas = document.createElement('canvas');
|
|
91
|
+
// add space for delay information
|
|
92
|
+
canvas.width = radiusDelay * 2 + margin * 2 + 100 * pixelRatio;
|
|
93
|
+
canvas.height = radiusDelay * 2 + margin * 2 + 100 * pixelRatio;
|
|
94
|
+
const ctx = canvas.getContext('2d');
|
|
95
|
+
const origin = canvas.width / 2;
|
|
96
|
+
|
|
97
|
+
if (isDisplayStrokeAndDelay && delay !== null) {
|
|
98
|
+
// Draw circle delay background
|
|
99
|
+
ctx.save();
|
|
100
|
+
ctx.beginPath();
|
|
101
|
+
ctx.arc(origin, origin, radiusDelay, 0, 2 * Math.PI, false);
|
|
102
|
+
ctx.fillStyle = getDelayColor(delay, cancelled);
|
|
103
|
+
ctx.filter = 'blur(1px)';
|
|
104
|
+
ctx.fill();
|
|
105
|
+
ctx.restore();
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Show delay if feature is hovered or if delay is above 5mins.
|
|
109
|
+
if (
|
|
110
|
+
isDisplayStrokeAndDelay &&
|
|
111
|
+
(hover || delay >= delayDisplay || cancelled)
|
|
112
|
+
) {
|
|
113
|
+
// Draw delay text
|
|
114
|
+
ctx.save();
|
|
115
|
+
ctx.textAlign = 'left';
|
|
116
|
+
ctx.textBaseline = 'middle';
|
|
117
|
+
ctx.font = `bold ${Math.max(
|
|
118
|
+
cancelled ? 19 : 14,
|
|
119
|
+
Math.min(cancelled ? 19 : 17, radius * 1.2),
|
|
120
|
+
)}px arial, sans-serif`;
|
|
121
|
+
ctx.fillStyle = getDelayColor(delay, cancelled, true);
|
|
122
|
+
|
|
123
|
+
ctx.strokeStyle = delayOutlineColor;
|
|
124
|
+
ctx.lineWidth = 1.5 * pixelRatio;
|
|
125
|
+
const delayText = getDelayText(delay, cancelled);
|
|
126
|
+
ctx.strokeText(delayText, origin + radiusDelay + margin, origin);
|
|
127
|
+
ctx.fillText(delayText, origin + radiusDelay + margin, origin);
|
|
128
|
+
ctx.restore();
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Draw colored circle with black border
|
|
132
|
+
let circleFillColor;
|
|
133
|
+
if (useDelayStyle) {
|
|
134
|
+
circleFillColor = getDelayColor(delay, cancelled);
|
|
135
|
+
} else {
|
|
136
|
+
circleFillColor = color || getBgColor(type);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
ctx.save();
|
|
140
|
+
if (isDisplayStrokeAndDelay || hover || selected) {
|
|
141
|
+
ctx.lineWidth = 1 * pixelRatio;
|
|
142
|
+
ctx.strokeStyle = '#000000';
|
|
143
|
+
}
|
|
144
|
+
ctx.fillStyle = circleFillColor;
|
|
145
|
+
ctx.beginPath();
|
|
146
|
+
ctx.arc(origin, origin, radius, 0, 2 * Math.PI, false);
|
|
147
|
+
ctx.fill();
|
|
148
|
+
// Dashed outline if a provider provides realtime but we don't use it.
|
|
149
|
+
if (
|
|
150
|
+
isDisplayStrokeAndDelay &&
|
|
151
|
+
useDelayStyle &&
|
|
152
|
+
delay === null &&
|
|
153
|
+
operatorProvidesRealtime === 'yes'
|
|
154
|
+
) {
|
|
155
|
+
ctx.setLineDash([5, 3]);
|
|
156
|
+
}
|
|
157
|
+
if (isDisplayStrokeAndDelay || hover || selected) {
|
|
158
|
+
ctx.stroke();
|
|
159
|
+
}
|
|
160
|
+
ctx.restore();
|
|
161
|
+
|
|
162
|
+
// Draw text in the circle
|
|
163
|
+
if (mustDrawText) {
|
|
164
|
+
const fontSize = Math.max(radius, 10 * pixelRatio);
|
|
165
|
+
const textSize = getTextSize(ctx, markerSize, name, fontSize);
|
|
166
|
+
|
|
167
|
+
// Draw a stroke to the text only if a provider provides realtime but we don't use it.
|
|
168
|
+
if (
|
|
169
|
+
useDelayStyle &&
|
|
170
|
+
delay === null &&
|
|
171
|
+
operatorProvidesRealtime === 'yes'
|
|
172
|
+
) {
|
|
173
|
+
ctx.save();
|
|
174
|
+
ctx.textBaseline = 'middle';
|
|
175
|
+
ctx.textAlign = 'center';
|
|
176
|
+
ctx.font = `bold ${textSize + 2}px Arial`;
|
|
177
|
+
ctx.strokeStyle = circleFillColor;
|
|
178
|
+
ctx.strokeText(name, origin, origin);
|
|
179
|
+
ctx.restore();
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// Draw a text
|
|
183
|
+
ctx.save();
|
|
184
|
+
ctx.textBaseline = 'middle';
|
|
185
|
+
ctx.textAlign = 'center';
|
|
186
|
+
ctx.fillStyle = !useDelayStyle
|
|
187
|
+
? textColor || getTextColor(type)
|
|
188
|
+
: '#000000';
|
|
189
|
+
ctx.font = `bold ${textSize}px Arial`;
|
|
190
|
+
ctx.strokeStyle = circleFillColor;
|
|
191
|
+
ctx.strokeText(name, origin, origin);
|
|
192
|
+
ctx.fillText(name, origin, origin);
|
|
193
|
+
ctx.restore();
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
styleCache[key] = canvas;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
return styleCache[key];
|
|
200
|
+
};
|
|
201
|
+
export default style;
|