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