diff --git a/ShadowEditor.Web/lang/zh-CN.js b/ShadowEditor.Web/lang/zh-CN.js index 5f290bbd..a7afaa11 100644 --- a/ShadowEditor.Web/lang/zh-CN.js +++ b/ShadowEditor.Web/lang/zh-CN.js @@ -622,4 +622,5 @@ L_KEY_VALUE_LABEL = '键值标签'; L_FORM_PANEL = '表单'; L_GAUGE = '仪表'; L_HISTOGRAM = '柱状图'; -L_LINECHART = '折线图'; \ No newline at end of file +L_LINECHART = '折线图'; +L_SCATTER_PLOT = '散点图'; \ No newline at end of file diff --git a/ShadowEditor.Web/src/editor/menubar/TwoDMenu.js b/ShadowEditor.Web/src/editor/menubar/TwoDMenu.js index 769c7c7a..8c933222 100644 --- a/ShadowEditor.Web/src/editor/menubar/TwoDMenu.js +++ b/ShadowEditor.Web/src/editor/menubar/TwoDMenu.js @@ -15,6 +15,7 @@ import Histogram from '../../visual/component/Histogram'; import LineChart from '../../visual/component/LineChart'; import SideBar from '../../visual/component/SideBar'; import Histogram2 from '../../visual/component/Histogram2'; +import ScatterPlot from '../../visual/component/ScatterPlot'; /** * 二维菜单 @@ -121,6 +122,11 @@ TwoDMenu.prototype.render = function () { cls: 'option', html: L_HISTOGRAM + '2', onClick: this.addHistogram2.bind(this), + }, { + xtype: 'div', + cls: 'option', + html: L_SCATTER_PLOT, + onClick: this.addScatterPlot.bind(this), }] }] }); @@ -288,4 +294,14 @@ TwoDMenu.prototype.addHistogram2 = function () { visual.render(svg); }; +// -------------------------------- 散点图 --------------------------------------------------- + +TwoDMenu.prototype.addScatterPlot = function () { + var visual = this.app.editor.visual; + var svg = this.app.editor.svg; + + visual.add(new ScatterPlot()); + visual.render(svg); +}; + export default TwoDMenu; \ No newline at end of file diff --git a/ShadowEditor.Web/src/language/Language.js b/ShadowEditor.Web/src/language/Language.js index 1a348218..341365a0 100644 --- a/ShadowEditor.Web/src/language/Language.js +++ b/ShadowEditor.Web/src/language/Language.js @@ -621,4 +621,5 @@ Object.assign(window, { L_GAUGE: 'Gauge', L_HISTOGRAM: 'Histogram', L_LINECHART: 'Line Chart', + L_SCATTER_PLOT: 'Scatter Plot', }); \ No newline at end of file diff --git a/ShadowEditor.Web/src/visual/Visualization.js b/ShadowEditor.Web/src/visual/Visualization.js index a4af90e0..2f38d940 100644 --- a/ShadowEditor.Web/src/visual/Visualization.js +++ b/ShadowEditor.Web/src/visual/Visualization.js @@ -15,6 +15,7 @@ import Histogram from './component/Histogram'; import LineChart from './component/LineChart'; import SideBar from './component/SideBar'; import Histogram2 from './component/Histogram2'; +import ScatterPlot from './component/ScatterPlot'; const ComponentTypes = { Button, @@ -32,6 +33,7 @@ const ComponentTypes = { LineChart, SideBar, Histogram2, + ScatterPlot, }; /** diff --git a/ShadowEditor.Web/src/visual/component/ScatterPlot.js b/ShadowEditor.Web/src/visual/component/ScatterPlot.js new file mode 100644 index 00000000..06592b73 --- /dev/null +++ b/ShadowEditor.Web/src/visual/component/ScatterPlot.js @@ -0,0 +1,167 @@ +import BaseComponent from '../BaseComponent'; + +/** + * 散点图 + * @author tengge / https://github.com/tengge1 + */ +function ScatterPlot() { + BaseComponent.call(this); + this.type = 'ScatterPlot'; + + this.width = 400; + this.height = 400; + this.title = 'ScatterPlot'; + + var data1 = []; + var data2 = []; + + var ran1 = d3.randomNormal(29, 8); + var ran2 = d3.randomNormal(72, 20); + + for (var i = 0; i <= 210; i += 10) { + data1.push([8 + i, ran1()]); + data2.push([8 + i, ran2()]); + } + + this.data = [data1, data2]; + + this.transform = null; +} + +ScatterPlot.prototype = Object.create(BaseComponent.prototype); +ScatterPlot.prototype.constructor = ScatterPlot; + +ScatterPlot.prototype.setTranslate = function (dx, dy) { + var xy = this.transform.split(','); + + this.transform = `${parseFloat(xy[0]) + dx},${parseFloat(xy[1]) + dy}`; + + this.dom.attr('transform', `translate(${this.transform})`); +}; + +ScatterPlot.prototype.render = function (parent) { + if (d3.select(`#${this.id}`).size() > 0) { + return; + } + + var g = d3.select(parent) + .append('g') + .attr('id', this.id) + .classed('Visual', true) + .classed('ScatterPlot', true) + .style('pointer-events', 'all'); + + var center = [ + [0.5, 0.5], + [0.7, 0.8], + [0.4, 0.9], + [0.11, 0.32], + [0.88, 0.25], + [0.75, 0.12], + [0.5, 0.1], + [0.2, 0.3], + [0.4, 0.1], + [0.6, 0.7] + ]; + + var xAxisWidth = 400; + var yAxisWidth = 400; + + var xScale = d3.scaleLinear() + .domain([0, 1.2 * d3.max(center, function (d) { + return d[0]; + })]) + .range([0, xAxisWidth]); + + var yScale = d3.scaleLinear() + .domain([0, 1.2 * d3.max(center, function (d) { + return d[1]; + })]) + .range([0, yAxisWidth]); + + var padding = { + top: 30, + right: 30, + bottom: 30, + left: 40 + }; + + var width = xAxisWidth + padding.left + padding.right; + var height = yAxisWidth + padding.top + padding.bottom; + + var circle = g.selectAll('circle') + .data(center) + .enter() + .append('circle') + .attr('data-id', this.id) + .attr('fill', 'black') + .attr('cx', function (d) { + return padding.left + xScale(d[0]); + }) + .attr('cy', function (d) { + return height - padding.bottom - yScale(d[1]); + }) + .attr('r', 5); + + var xAxis = d3.axisBottom() + .scale(xScale); + + g.append('g') + .attr('transform', + `translate(${padding.left}, ${padding.top + yAxisWidth})`) + .call(xAxis); + + yScale.range([yAxisWidth, 0]); + + var yAxis = d3.axisLeft() + .scale(yScale); + + g.append('g') + .attr('transform', + `translate(${padding.left},${padding.top})`) + .call(yAxis); + + if (!this.transform) { + var left = (parent.clientWidth - this.width) / 2; + var top = (parent.clientHeight - this.height) / 2; + this.transform = `${left},${top}`; + } + + g.attr('transform', `translate(${this.transform})`); + + this.dom = g; +}; + +ScatterPlot.prototype.toJSON = function () { + var transform; + if (this.transform) { + transform = this.transform + .replace('translate(', '') + .replace(')', ''); + } + + return { + id: this.id, + type: this.type, + title: this.title, + data: this.data, + transform, + }; +}; + +ScatterPlot.prototype.fromJSON = function (json) { + this.id = json.id; + this.type = json.type; + this.title = json.title; + this.data = data; + this.transform = json.transform || null; +}; + +ScatterPlot.prototype.clear = function () { + this.title = 'ScatterPlot'; + this.transform = null; + + delete this.dom; +}; + +export default ScatterPlot; \ No newline at end of file