Skip to content

Commit 79ba655

Browse files
feat(lines): add curved lines with curvature option (#165)
* feat(lines): add curved lines with curvature option * chore: set lines default options
1 parent a87a1bb commit 79ba655

File tree

6 files changed

+97
-45
lines changed

6 files changed

+97
-45
lines changed
Lines changed: 43 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,55 @@
11
import BaseComponent from './base'
22

3+
const LINE_CLASS = 'jvm-line'
4+
35
class Line extends BaseComponent {
4-
constructor({ index, map, style, x1, y1, x2, y2, group, config }) {
6+
constructor(options, style) {
57
super()
6-
7-
this.config = config
8-
this.shape = map.canvas.createLine({ x1, y1, x2, y2, dataIndex: index }, style, group)
9-
this.shape.addClass('jvm-line')
8+
this._options = options
9+
this._style = style
10+
this._draw()
1011
}
1112

1213
setStyle(property, value) {
1314
this.shape.setStyle(property, value)
1415
}
16+
17+
getConfig() {
18+
return this._options.config
19+
}
20+
21+
_draw() {
22+
const { index, group, map, animate } = this._options
23+
const config = {
24+
d: this._getDAttribute(),
25+
fill: 'none',
26+
dataIndex: index,
27+
}
28+
29+
this.shape = map.canvas.createPath(config, this._style, group)
30+
this.shape.addClass(LINE_CLASS)
31+
32+
if (animate) {
33+
this.shape.setStyle({ animation: true })
34+
}
35+
}
36+
37+
_getDAttribute() {
38+
const { x1, y1, x2, y2 } = this._options
39+
return `M${x1},${y1}${this._getQCommand(x1, y1, x2, y2)}${x2},${y2}`
40+
}
41+
42+
_getQCommand(x1, y1, x2, y2) {
43+
if (!this._options.curvature) {
44+
return ' '
45+
}
46+
47+
const curvature = this._options.curvature || 0.6
48+
const curveX = (x1 + x2) / 2 + curvature * (y2 - y1)
49+
const curveY = (y1 + y2) / 2 - curvature * (x2 - x1)
50+
51+
return ` Q${curveX},${curveY} `
52+
}
1553
}
1654

1755
export default Line
Lines changed: 26 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,26 @@
11
import { merge, getLineUid } from '../util'
22
import Line from '../components/line'
33

4-
export default function createLines(lines, markers, isRecentlyCreated = false) {
4+
const LINES_GROUP_CLASS = 'jvm-lines-group'
5+
6+
export default function createLines(lines, isRecentlyCreated = false) {
7+
// Create group for holding lines we're checking if `linesGroup`
8+
// exists or not becuase we may add lines after the map has
9+
// loaded so we will append the futured lines to this group as well.
10+
this.linesGroup = this.linesGroup || this.canvas.createGroup(LINES_GROUP_CLASS)
11+
12+
const markers = this.params.markers || {}
13+
const { style, elements: _, ...rest } = this.params.lines
14+
515
let point1 = false, point2 = false
616

7-
// Create group for holding lines
8-
// we're checking if `linesGroup` exists or not becuase we may add lines
9-
// after the map has loaded so we will append the futured lines to this group as well.
10-
this.linesGroup = this.linesGroup || this.canvas.createGroup('jvm-lines-group')
11-
1217
for (let index in lines) {
1318
const config = lines[index]
1419

1520
for (let mindex in markers) {
16-
const markerConfig = isRecentlyCreated ? markers[mindex].config : markers[mindex]
21+
const markerConfig = isRecentlyCreated
22+
? markers[mindex].config
23+
: markers[mindex]
1724

1825
if (markerConfig.name === config.from) {
1926
point1 = this.getMarkerPosition(markerConfig)
@@ -25,19 +32,23 @@ export default function createLines(lines, markers, isRecentlyCreated = false) {
2532
}
2633

2734
if (point1 !== false && point2 !== false) {
28-
// Register lines with unique keys
29-
this._lines[getLineUid(config.from, config.to)] = new Line({
30-
index: index,
35+
const options = {
36+
index,
3137
map: this,
32-
// Merge the default `lineStyle` object with the custom `line` config style
33-
style: merge({ initial: this.params.lineStyle }, { initial: config.style || {} }, true),
38+
group: this.linesGroup,
39+
config,
3440
x1: point1.x,
3541
y1: point1.y,
3642
x2: point2.x,
3743
y2: point2.y,
38-
group: this.linesGroup,
39-
config
40-
})
44+
...rest,
45+
}
46+
47+
// Register lines with unique keys
48+
this._lines[getLineUid(config.from, config.to)] = new Line(
49+
options,
50+
merge({ initial: style }, { initial: config.style || {} }, true)
51+
)
4152
}
4253
}
4354
}
Lines changed: 19 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,27 @@
11
export default function repositionLines() {
2-
let point1 = false, point2 = false
2+
const curvature = this.params.lines.curvature || 0.5
33

4-
for (let index in this._lines) {
5-
for (let mindex in this._markers) {
6-
const marker = this._markers[mindex]
4+
Object.values(this._lines).forEach((line) => {
5+
const startMarker = Object.values(this._markers).find(
6+
({ config }) => config.name === line.getConfig().from
7+
)
78

8-
if (marker.config.name === this._lines[index].config.from) {
9-
point1 = this.getMarkerPosition(marker.config)
10-
}
9+
const endMarker = Object.values(this._markers).find(
10+
({ config }) => config.name === line.getConfig().to
11+
)
1112

12-
if (marker.config.name === this._lines[index].config.to) {
13-
point2 = this.getMarkerPosition(marker.config)
14-
}
15-
}
13+
if (startMarker && endMarker) {
14+
const { x: x1, y: y1 } = this.getMarkerPosition(startMarker.config)
15+
const { x: x2, y: y2 } = this.getMarkerPosition(endMarker.config)
16+
17+
const midX = (x1 + x2) / 2
18+
const midY = (y1 + y2) / 2
19+
const curveX = midX + curvature * (y2 - y1)
20+
const curveY = midY - curvature * (x2 - x1)
1621

17-
if (point1 !== false && point2 !== false) {
18-
this._lines[index].setStyle({
19-
x1: point1.x,
20-
y1: point1.y,
21-
x2: point2.x,
22-
y2: point2.y,
22+
line.setStyle({
23+
d: `M${x1},${y1} Q${curveX},${curveY} ${x2},${y2}`,
2324
})
2425
}
25-
}
26+
})
2627
}

packages/jsvectormap/src/js/defaults/options.js

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,12 @@ export default {
1313
bindTouchEvents: true,
1414

1515
// Line options
16-
lineStyle: {
17-
stroke: '#808080',
18-
strokeWidth: 1,
19-
strokeLinecap: 'round',
16+
lines: {
17+
style: {
18+
stroke: '#808080',
19+
strokeWidth: 1,
20+
strokeLinecap: 'round',
21+
}
2022
},
2123

2224
// Marker options

packages/jsvectormap/src/js/map.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ class Map {
7171
this.updateSize()
7272

7373
// Create lines
74-
this._createLines(options.lines || {}, options.markers || {})
74+
this._createLines(options.lines.elements || {})
7575

7676
// Create markers
7777
this._createMarkers(options.markers)

packages/jsvectormap/src/js/svg/canvasElement.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,12 +35,12 @@ class SVGCanvasElement extends SVGElement {
3535
}
3636

3737
// Create `path` element
38-
createPath(config, style) {
38+
createPath(config, style, group) {
3939
const path = new SVGShapeElement('path', config, style)
4040

4141
path.node.setAttribute('fill-rule', 'evenodd')
4242

43-
return this._add(path)
43+
return this._add(path, group)
4444
}
4545

4646
// Create `circle` element

0 commit comments

Comments
 (0)