Last active
April 16, 2020 12:31
-
-
Save AnicetR/3129eabc4413deb7e3cc37899bc929ee to your computer and use it in GitHub Desktop.
VueJS with D3 : Component to draw path with "angle radius" bezel
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<script> | |
import * as d3 from 'd3'; | |
import select from 'd3-selection-multi'; | |
import * as tinyColor from 'tinycolor2'; | |
export default { | |
props: { | |
lineData: { | |
Type: Array, | |
default: () => [ | |
{x: 10, y: 10}, | |
{x: 124, y: 10}, | |
{x: 124, y: 120}, | |
{x: 10, y: 120}, | |
] | |
}, | |
color: { | |
Type: String, | |
default: "black" | |
}, | |
strokeWidth: { | |
Type: Number, | |
default: 10 | |
}, | |
opacity: { | |
type: Number, | |
default: 0.3 | |
}, | |
radius: { | |
type: Number, | |
default: 15 | |
} | |
}, | |
data() { | |
return { | |
generateLineFromCoords: d3.line() | |
.x(function (d) { | |
return d.x; | |
}) | |
.y(function (d) { | |
return d.y; | |
}) | |
.curve(d3.curveCardinal.tension(7)) | |
} | |
}, | |
computed: { | |
lastPointIndex() { | |
return this.lineData.length - 1; | |
} | |
}, | |
methods: { | |
// Correct last point to add the arrow | |
correctLineData(lineData) { | |
var data = this.lineData[this.lastPointIndex]; | |
data.x = data.x - 10; | |
data.y = data.y; | |
this.lineData[this.lastPointIndex] = data; | |
}, | |
// Slice the lineData 3 by 3 | |
sliceLineDataByAngle() { | |
var slices = []; | |
for (var i = 0; i < this.lineData.length; i++) { | |
var slice = this.lineData.slice(i, i + 3) | |
if (slice.length === 3) { | |
slices.push(slice) | |
} | |
} | |
return slices; | |
}, | |
//create a new path with injected "angle radius" | |
lineDataWithInjectedAngles() { | |
var newPath = []; | |
let slices = this.sliceLineDataByAngle() | |
//If there is no slice (and so, no angles), we return the original line | |
if(slices.length === 0){ | |
return this.lineData | |
} | |
//We put the first point of the path | |
newPath.push(slices[0][0]); | |
let radius = this.radius; | |
slices.forEach(function(angle){ | |
let first = angle[0]; | |
let second = angle[1]; | |
let third = angle[2]; | |
// pop the last point to avoid duplicated points | |
if(newPath.length > 1){ | |
newPath.pop(newPath.length-1) | |
} | |
// +90° | |
// 1 2 | |
// 3 | |
if ( | |
(first.y === second.y && first.x < second.x) | |
&& (second.y < third.y && second.x === third.x) | |
) { | |
var injectedPoint1 = {x: second.x - radius, y: second.y}; | |
var injectedPoint2 = {x: second.x, y: second.y + radius}; | |
newPath.push(injectedPoint1, injectedPoint2, third) | |
return; | |
} | |
// -90 | |
// 2 3 | |
// 1 | |
if ( | |
(first.y > second.y && first.x === second.x) | |
&& (second.y === third.y && second.x < third.x) | |
) { | |
var injectedPoint1 = {x: second.x, y: second.y + radius}; | |
var injectedPoint2 = {x: second.x + radius, y: second.y}; | |
newPath.push(injectedPoint1, injectedPoint2, third) | |
return; | |
} | |
//-180° | |
// 1 | |
// 2 3 | |
if ( | |
(first.y < second.y && first.x === second.x) | |
&& (second.y === third.y && second.x < third.x) | |
) { | |
var injectedPoint1 = {x: second.x, y: second.y - radius}; | |
var injectedPoint2 = {x: second.x + radius, y: second.y}; | |
newPath.push(injectedPoint1, injectedPoint2, third) | |
return; | |
} | |
//+180° | |
// 3 | |
// 1 2 | |
if ( | |
(first.y === second.y && first.x < second.x) | |
&& (second.y > third.y && second.x === third.x) | |
) { | |
var injectedPoint1 = {x: second.x - radius, y: second.y}; | |
var injectedPoint2 = {x: second.x, y: second.y - radius}; | |
newPath.push(injectedPoint1, injectedPoint2, third) | |
return; | |
} | |
// -90° | |
// 1 | |
// 3 2 | |
if ( | |
(first.y < second.y && first.x === second.x) | |
&& (second.y === third.y && second.x > third.x) | |
) { | |
var injectedPoint1 = {x: second.x, y: second.y - radius}; | |
var injectedPoint2 = {x: second.x - radius, y: second.y}; | |
newPath.push(injectedPoint1, injectedPoint2, third) | |
return | |
} | |
// 2 1 | |
// 3 | |
if ( | |
(first.y === second.y && first.x > second.x) | |
&& (second.y < third.y && second.x === third.x) | |
) { | |
var injectedPoint1 = {x: second.x + radius, y: second.y}; | |
var injectedPoint2 = {x: second.x, y: second.y + radius}; | |
newPath.push(injectedPoint1, injectedPoint2, third) | |
return | |
} | |
newPath.push(first, second, third) | |
return; | |
}) | |
return newPath | |
} | |
}, | |
mounted() { | |
var container = d3.select(this.$el) | |
this.correctLineData() | |
var lineData = this.lineDataWithInjectedAngles() | |
//draw path & arrow | |
container.append('path') | |
.attrs({ | |
d: this.generateLineFromCoords(lineData), | |
stroke: this.color, | |
"stroke-width": this.strokeWidth, | |
fill: "none", | |
opacity: this.opacity | |
}) | |
container.append('path') | |
.attrs({ | |
d: d3.symbol().size(40).type(d3.symbolTriangle), | |
transform: | |
"translate(" + (this.lineData[this.lastPointIndex].x + 2.6) + "," + (this.lineData[this.lastPointIndex].y) + ")" | |
+ "rotate(90)", | |
fill: this.color, | |
opacity: this.opacity | |
}) | |
} | |
} | |
</script> | |
<template> | |
<g> | |
</g> | |
</template> | |
<style lang="scss" module> | |
@import '@design'; | |
</style> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment