Created
September 18, 2020 11:05
-
-
Save valex/95a9f2449763a292646164a1a52170a2 to your computer and use it in GitHub Desktop.
This file contains hidden or 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
<template></template> | |
<script> | |
import SVGVector2D from './../../SVGVector2D' | |
export default { | |
data() { | |
return { | |
svg: null, | |
mainGroup: null, | |
scaleX: null, | |
scaleY: null, | |
vectorDefaults:{ | |
dasharray: '', | |
color: '#0000FF', | |
linecap: "butt", | |
width: 2, | |
}, | |
vectors: { | |
a: new SVGVector2D(1.5, 3, { | |
id: 'vector_a', | |
html_label: "a̅", | |
}), | |
b: new SVGVector2D(2.5, 1, { | |
id: 'vector_b', | |
html_label: "b̅", | |
}), | |
a_plus_b: { | |
id: 'a_plus_b_vector', | |
color: '#FF0000', | |
html_label: "a̅ + b̅", | |
}, | |
b_moved: new SVGVector2D(2.5, 1, { | |
id: 'b_moved_vector', | |
color: '#AAAAAA', | |
dasharray: '3 2' | |
}, 1.5, 3) | |
}, | |
options:{ | |
width: 480, | |
height: 480, | |
circlesRadius: 5, | |
margins: { | |
top: 10, | |
right: 10, | |
bottom: 10, | |
left: 10, | |
xAxis: { | |
left: 0, | |
right: 0, | |
}}, | |
}, | |
} | |
}, | |
template: '<div></div>', | |
mounted() { | |
this.$nextTick(function () { | |
// Code that will run only after the | |
// entire view has been rendered | |
this.ready(); | |
}); | |
this.svg = d3.select(this.$el) | |
.append("svg") | |
.attr('xmlns', 'http://www.w3.org/2000/svg') | |
.attr("width", this.options.width) | |
.attr("height", this.options.height) | |
.on('click', function() { | |
console.log('onClickSvg'); | |
}); | |
this.mainGroup = this.svg.append('g') | |
.attr('transform', 'translate(' + this.options.margins.left + ',' + this.options.margins.top + ')'); | |
this.scaleX = d3.scaleLinear() | |
.domain([-5, 5]) | |
.range([0, this.chartWidth - this.options.margins.xAxis.left - this.options.margins.xAxis.right]); | |
this.scaleY = d3.scaleLinear() | |
.domain([-5, 5]) | |
.range([this.chartHeight, 0]); | |
// axes | |
var axisXGroup = this.svg.append('g') | |
.attr('transform', 'translate(' + (this.options.margins.left + this.options.margins.xAxis.left) + ',' + (this.chartHeight/2 + this.options.margins.top) + ')'); | |
var axisX = d3.axisBottom(this.scaleX).tickValues([-5,-4,-3,-2,-1, 1, 2, 3, 4, 5]); | |
axisXGroup.call(axisX); | |
var axisYGroup = this.svg.append('g') | |
.attr('transform', 'translate(' + (this.options.margins.left + this.options.margins.xAxis.left + this.scaleX(0)) + ',' + this.options.margins.top + ')'); | |
var axisY = d3.axisLeft(this.scaleY).tickValues([-5,-4,-3,-2,-1, 1, 2, 3, 4, 5]); | |
axisYGroup.call(axisY); | |
// Vector a info | |
this.mainGroup.append('text') | |
.attr('id', 'a_vector_info') | |
.html(this.a_vectorInfo) | |
.style('text-anchor', 'start') | |
.style('alignment-baseline', 'bottom') | |
.attr('fill',"#333") | |
.attr('x',this.options.margins.left) | |
.attr('y',18) | |
.attr('dx',0) | |
.attr('dy',0) | |
.attr('font-size','16px'); | |
// Vector b info | |
this.mainGroup.append('text') | |
.attr('id', 'b_vector_info') | |
.html(this.b_vectorInfo) | |
.style('text-anchor', 'start') | |
.style('alignment-baseline', 'bottom') | |
.attr('fill',"#333") | |
.attr('x',this.options.margins.left) | |
.attr('y',40) | |
.attr('dx',0) | |
.attr('dy',0) | |
.attr('font-size','16px'); | |
this.mainGroup.append('text') | |
.attr('id', 'a_plus_b_vector_info') | |
.html(this.a_plus_b_vectorInfo) | |
.style('text-anchor', 'start') | |
.style('alignment-baseline', 'bottom') | |
.attr('fill',"#333") | |
.attr('x',this.options.margins.left) | |
.attr('y',62) | |
.attr('dx',0) | |
.attr('dy',0) | |
.attr('font-size','16px'); | |
this.drawVector(this.vectors.a); | |
this.drawVector(this.vectors.b); | |
this.drawVector(this.vectors.b_moved); | |
this.drawVector(this.a_plus_b); | |
// Draggable circles | |
this.mainGroup.append('circle') | |
.classed("draggable", true) | |
.attr("cx", this.scaleX(this.vectors.a.x)) | |
.attr("cy", this.scaleY(this.vectors.a.y)) | |
.attr("r", this.options.circlesRadius) | |
.call( | |
d3.drag() | |
.on("start", this.dragstarted) | |
.on('drag', this.a_dragged) | |
.on("end", this.dragended) | |
); | |
this.mainGroup.append('circle') | |
.classed("draggable", true) | |
.attr("cx", this.scaleX(this.vectors.b.x)) | |
.attr("cy", this.scaleY(this.vectors.b.y)) | |
.attr("r", this.options.circlesRadius) | |
.call( | |
d3.drag() | |
.on("start", this.dragstarted) | |
.on('drag', this.b_dragged) | |
.on("end", this.dragended) | |
); | |
}, | |
methods: { | |
ready(){ | |
}, | |
drawVector(vector){ | |
if( ! vector.hasOption('id')) | |
return; | |
let x1=vector.offsetX; | |
let y1=vector.offsetY; | |
let x2=vector.offsetX + vector.x; | |
let y2=vector.offsetY + vector.y; | |
let dasharray = vector.getOption('dasharray'); | |
let color = vector.getOption('color'); | |
let linecap = vector.getOption('linecap'); | |
let width = vector.getOption('width'); | |
let html_label = vector.getOption('html_label'); | |
// place label | |
if( ! _.isNil(html_label)){ | |
this.mainGroup.append('text') | |
.html(html_label) | |
.attr('id',vector.id+"_label") | |
.style('text-anchor', 'middle') | |
.style('alignment-baseline', 'middle') | |
.attr("transform", "rotate("+(-vector.angle)+","+this.scaleX(x1+((x2-x1) / 2))+","+this.scaleY(y1+((y2-y1) / 2))+")") | |
.attr('fill', color) | |
.attr('x',this.scaleX(x1+((x2-x1) / 2))) | |
.attr('y',this.scaleY(y1+((y2-y1) / 2))) | |
.attr('dx',0) | |
.attr('dy',-14) | |
.attr('font-size','24px'); | |
} | |
// place arrow | |
this.svg.append("svg:defs").append("svg:marker") | |
.attr("id", vector.id+"_arrow") | |
.attr("refX", 7) | |
.attr("refY", 4) | |
.attr("markerWidth", 8) | |
.attr("markerHeight", 8) | |
.attr("markerUnits", "strokeWidth") | |
.attr("viewBox", "0 0 8 8") | |
.attr("orient", "auto") | |
.append("path") | |
.attr("d", "M0,0 L8,4 L0,8 L4,4 L0,0") | |
.style("fill", color); | |
// draw line | |
this.mainGroup.append("line") | |
.attr('id', vector.id) | |
.attr("x1", this.scaleX(x1)) | |
.attr("y1", this.scaleY(y1)) | |
.attr("x2", this.scaleX(x2)) | |
.attr("y2", this.scaleY(y2)) | |
.attr("stroke-dasharray", dasharray) | |
.attr("stroke-width", width) | |
.attr("stroke-linecap", linecap) | |
.attr("stroke", color) | |
.attr("marker-end","url(#"+vector.id+"_arrow)"); | |
}, | |
a_dragged(d, i, els){ | |
let x = d3.event.x, | |
y = d3.event.y; | |
if ((x < 0) || x > this.chartWidth || (y < 0) || y > this.chartHeight) | |
return; | |
d3.select(els[i]) | |
.attr("cx", x) | |
.attr("cy", y); | |
this.vectors.a.x = this.scaleX.invert(x); | |
this.vectors.a.y = this.scaleY.invert(y); | |
this.vectors.b_moved.offsetX = this.scaleX.invert(x); | |
this.vectors.b_moved.offsetY = this.scaleY.invert(y); | |
d3.select('#'+this.vectors.a.id) | |
.attr("x2", this.scaleX(this.vectors.a.x)) | |
.attr("y2", this.scaleY(this.vectors.a.y)); | |
d3.select('#'+this.vectors.b_moved.id) | |
.attr("x1", this.scaleX(this.vectors.b_moved.offsetX)) | |
.attr("y1", this.scaleY(this.vectors.b_moved.offsetY)) | |
.attr("x2", this.scaleX(this.vectors.b_moved.offsetX + this.vectors.b_moved.x)) | |
.attr("y2", this.scaleY(this.vectors.b_moved.offsetY + this.vectors.b_moved.y)); | |
d3.select('#'+this.vectors.a.id + '_label') | |
.attr('x',this.scaleX(this.vectors.a.x/2)) | |
.attr('y',this.scaleY(this.vectors.a.y/2)) | |
.attr("transform", "rotate("+(-this.vectors.a.angle)+","+this.scaleX(this.vectors.a.x / 2)+","+this.scaleY(this.vectors.a.y / 2)+")") | |
d3.select('#'+this.a_plus_b.id) | |
.attr("x2", this.scaleX(this.a_plus_b.x)) | |
.attr("y2", this.scaleY(this.a_plus_b.y)); | |
d3.select('#'+this.a_plus_b.id + '_label') | |
.attr('x',this.scaleX(this.a_plus_b.x/2)) | |
.attr('y',this.scaleY(this.a_plus_b.y/2)) | |
.attr("transform", "rotate("+(-this.a_plus_b.angle)+","+this.scaleX(this.a_plus_b.x / 2)+","+this.scaleY(this.a_plus_b.y / 2)+")") | |
d3.select('#a_vector_info') | |
.html(this.a_vectorInfo); | |
d3.select('#a_plus_b_vector_info') | |
.html(this.a_plus_b_vectorInfo); | |
}, | |
b_dragged(d, i, els){ | |
let x = d3.event.x, | |
y = d3.event.y; | |
if ((x < 0) || x > this.chartWidth || (y < 0) || y > this.chartHeight) | |
return; | |
d3.select(els[i]) | |
.attr("cx", x) | |
.attr("cy", y); | |
this.vectors.b.x = this.scaleX.invert(x); | |
this.vectors.b.y = this.scaleY.invert(y); | |
this.vectors.b_moved.x = this.scaleX.invert(x); | |
this.vectors.b_moved.y = this.scaleY.invert(y); | |
d3.select('#'+this.vectors.b.id) | |
.attr("x2", this.scaleX(this.vectors.b.x)) | |
.attr("y2", this.scaleY(this.vectors.b.y)); | |
d3.select('#'+this.vectors.b_moved.id) | |
.attr("x2", this.scaleX(this.vectors.b_moved.offsetX + this.vectors.b_moved.x)) | |
.attr("y2", this.scaleY(this.vectors.b_moved.offsetY + this.vectors.b_moved.y)); | |
d3.select('#'+this.vectors.b.id + '_label') | |
.attr('x',this.scaleX(this.vectors.b.x/2)) | |
.attr('y',this.scaleY(this.vectors.b.y/2)) | |
.attr("transform", "rotate("+(-this.vectors.b.angle)+","+this.scaleX(this.vectors.b.x / 2)+","+this.scaleY(this.vectors.b.y / 2)+")") | |
d3.select('#'+this.a_plus_b.id) | |
.attr("x2", this.scaleX(this.a_plus_b.x)) | |
.attr("y2", this.scaleY(this.a_plus_b.y)); | |
d3.select('#'+this.a_plus_b.id + '_label') | |
.attr('x',this.scaleX(this.a_plus_b.x/2)) | |
.attr('y',this.scaleY(this.a_plus_b.y/2)) | |
.attr("transform", "rotate("+(-this.a_plus_b.angle)+","+this.scaleX(this.a_plus_b.x / 2)+","+this.scaleY(this.a_plus_b.y / 2)+")") | |
d3.select('#b_vector_info') | |
.html(this.b_vectorInfo); | |
d3.select('#a_plus_b_vector_info') | |
.html(this.a_plus_b_vectorInfo); | |
}, | |
dragstarted(d, i, els){ | |
let circle = d3.select(els[i]).classed("dragging", true); | |
}, | |
dragended(d, i, els){ | |
let circle = d3.select(els[i]).classed("dragging", false); | |
} | |
}, | |
computed: { | |
chartWidth: function () { | |
// `this` указывает на экземпляр vm | |
return this.options.width - this.options.margins.left - this.options.margins.right | |
}, | |
chartHeight: function () { | |
// `this` указывает на экземпляр vm | |
return this.options.height - this.options.margins.top - this.options.margins.bottom; | |
}, | |
a_plus_b :function(){ | |
return new SVGVector2D( | |
this.vectors.a.x+this.vectors.b.x, | |
this.vectors.a.y+this.vectors.b.y, | |
this.vectors.a_plus_b); | |
}, | |
a_vectorInfo: function (){ | |
let info = "a̅ ⟨"; | |
info+=this.vectors.a.x.toFixed(2); | |
info+=", "; | |
info+=this.vectors.a.y.toFixed(2); | |
info+="⟩"; | |
return info; | |
}, | |
b_vectorInfo: function (){ | |
let info = "b̅ ⟨"; | |
info+=this.vectors.b.x.toFixed(2); | |
info+=", "; | |
info+=this.vectors.b.y.toFixed(2); | |
info+="⟩"; | |
return info; | |
}, | |
a_plus_b_vectorInfo: function (){ | |
let info = "a̅ + b̅ ⟨"; | |
info+=this.a_plus_b.x.toFixed(2); | |
info+=", "; | |
info+=this.a_plus_b.y.toFixed(2); | |
info+="⟩"; | |
return info; | |
} | |
} | |
} | |
</script> | |
<style> | |
svg text { | |
-webkit-user-select: none; | |
-moz-user-select: none; | |
-ms-user-select: none; | |
user-select: none; | |
} | |
svg text::selection { | |
background: none; | |
} | |
svg path.domain{ | |
stroke: #AAAAAA; | |
} | |
svg g.tick line{ | |
stroke: #AAAAAA; | |
} | |
svg g.tick text{ | |
font-size: 0.9rem; | |
stroke-width: 0.3px; | |
stroke: #AAAAAA; | |
} | |
circle.draggable { | |
fill: lightsteelblue; | |
stroke: steelblue; | |
stroke-width: 1.5px; | |
opacity: 0.85; | |
fill-opacity: 0.5; | |
cursor: pointer; | |
} | |
circle.draggable.dragging { | |
fill: red; | |
stroke: brown; | |
opacity: 1.0; | |
fill-opacity: 1.0; | |
} | |
</style> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment