使用 Javascript 和 HTML 制作图形/曲线



我正在尝试制作一个可以输入数字的程序,它基于这些数字创建一个图表(如 Desmos,但您输入的数字不需要格式化为 (1, 0(,您可以控制数字的间距(。我正在用Javascript和HTML编写实际输入和画布。

我尝试使用这个网站作为基础来帮助我创建曲线。

以下是Javascript和HTML代码的组合:

<!DOCTYPE html>
<html>
<body>
<form>
<label></label>
<input id="numbers" value="Numbers">
<button onClick="refresh()">Submit</button>
</form>
<form>
<label></label>
<input id="spacing" value="Spacing">
<button onClick="refresh()">Submit</button>
</form>
<canvas id="myCanvas" width="300" height="150" style="border:1px solid #d3d3d3;"></canvas>
<script>
refresh()
numbers = document.getElementById("numbers").value;
function refresh(){
var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
ctx.beginPath();
ctx.moveTo(20, 20);
ctx.bezierCurveTo(numbers);
ctx.stroke();
}
</script>
</html>

请注意,我知道画布不起作用(变量numbers不起作用,有什么帮助吗?(,但我主要担心的是这段代码不会像我想要的那样创建图形。我想输入数字,让它在"地面"(画布底部(上方绘制不可见的点x数字,然后使用贝塞尔使曲线穿过那些不可见的点(不要做点,这是我能想到的唯一解释方法(。

在实习期间,我被要求为我们的一些内部运营开发一个轻量级的 Web 应用程序。 我想包含的功能之一是绘制所选员工的销售数据的图形/图表。 我还希望能够更改图表 x 轴的分辨率,以便我可以按天、周、月、季度或年查看销售额。

以下是我使用基本的JavaScript和HTML Canvas得出的。

关于fix_dpi()函数(这可能会让像我这样的新程序员感到困惑(,这里有一篇文章解释了它是什么以及为什么要调整它:https://medium.com/wdstack/fixing-html5-2d-canvas-blur-8ebe27db07da 。

另外,请注意,我的drawYTicks()函数有一个特殊情况,用于"50K"和"500K"标签。 如果您需要更通用的内容,只需从drawXTicks()复制代码并对其域进行必要的小更改即可。

法典:

<script>
let canvas = document.getElementById('canvas'), ctx = canvas.getContext('2d'), dpi = window.devicePixelRatio;
let xOrig = canvas.width * 0.15;
let yOrig = canvas.height * 0.85;
let xLen = canvas.width * 0.7;
let yLen = canvas.height * 0.7;
function fix_dpi() {
let style = {
height() {
return +getComputedStyle(canvas).getPropertyValue('height').slice(0, -2);
},
width() {
return +getComputedStyle(canvas).getPropertyValue('width').slice(0, -2);
}
}
canvas.setAttribute('width', style.width() * dpi);
canvas.setAttribute('height', style.height() * dpi);
xOrig = canvas.width * 0.15;
yOrig = canvas.height * 0.85;
xLen = canvas.width * 0.7;
yLen = canvas.height * 0.7;
}
function draw() {
fix_dpi();  //fixes dpi for sharper image...
var dataSet = // int[][] containing the data.
var xLabs = // a string[] containing x-labels.
var yLabs = // a string[] containing y-labels.
var range = // an integer expressing the top of your range (x-axis);
var domain = // an integer expressing the top of your domain (y-axis)
console.log(xLabs);
console.log(xLabs.length);
console.log(yLabs);
console.log(yLabs.length);
drawChartTitle("Employee (Total) Sales History");
fillDataSpace(dataSet, range, domain, '#3F3');
drawDataLine(dataSet, range, domain, '#000');
drawDataPoints(dataSet, range, domain, '#F00');
drawXTicks(xLabs);
drawYTicks(yLabs);
drawAxes();
}
function drawChartTitle(title) {
ctx.save();
ctx.fillStyle = '#000';
ctx.font = '24px Times New Roman';
ctx.textAlign = 'center';
ctx.fillText(title, canvas.width / 2, canvas.height * 0.1);
ctx.restore();
}
function drawAxes(color='#000') {
ctx.save();
ctx.beginPath();
ctx.strokeStyle = color;
ctx.moveTo(xOrig, (yOrig - yLen));
ctx.lineTo(xOrig, yOrig);
ctx.lineTo((xOrig + xLen),yOrig);
ctx.stroke();
ctx.restore();
}
function drawYTicks(yLabs) {
//Note:  two of the ticks have to be handled separately
//for this program, 50K and 500K.
ctx.save();
let tickLen = canvas.height / 50;
let yDivs = yLen / (yLabs.length - 1);
let tickX = xOrig - tickLen / 2;
let labX = xOrig - tickLen;
ctx.beginPath();
ctx.strokeStyle = '#000';
ctx.fillStyle = '#000';
ctx.font = '12px Times New Roman';
ctx.textAlign = 'right';
if (yLabs.length >= 4) {
yDivs = yLen / (yLabs.length - 3);
//Draw million dollar ticks and labels
for (let i = 3; i < yLabs.length; i++) {
let tickY = yOrig - yDivs * (i-2);
ctx.moveTo(tickX, tickY);
ctx.lineTo((tickX + tickLen), tickY);
}
for (let i = 3; i < yLabs.length; i++) {
let labY = yOrig - yDivs * (i-2) + 4;
ctx.fillText(yLabs[i], labX, labY);
}
//Draw 50K and 500K
let fifty = yOrig - yDivs * 0.1;
ctx.moveTo(tickX, fifty);
ctx.lineTo((tickX + tickLen), fifty);
ctx.fillText(yLabs[1], labX, fifty + 4);
let fHundredK = yOrig - yDivs * 0.5;
ctx.moveTo(tickX, fHundredK);
ctx.lineTo((tickX + tickLen), fHundredK);
ctx.fillText(yLabs[2], labX, fHundredK + 4);
}
else if (yLabs.length == 3) {
yDivs = yLen / (yLabs.length - 2);
//Draw 50K and 500K only
let fifty = yOrig - yDivs * 0.1;
ctx.moveTo(tickX, fifty);
ctx.lineTo((tickX + tickLen), fifty);
ctx.fillText(yLabs[1], labX, fifty + 4);
let fHundredK = yOrig - yDivs;
ctx.moveTo(tickX, fHundredK);
ctx.lineTo((tickX + tickLen), fHundredK);
ctx.fillText(yLabs[2], labX, fHundredK + 4);
}
else {
//Draw 50K only
let fifty = yOrig - yDivs;
ctx.moveTo(tickX, fifty);
ctx.lineTo((tickX + tickLen), fifty);
ctx.fillText(yLabs[1], labX, fifty + 4);
}
let zero = yOrig;
ctx.moveTo(tickX, zero);
ctx.lineTo((tickX + tickLen), zero);
ctx.fillText(yLabs[0], labX, zero + 4);
ctx.stroke();
ctx.restore();
}
function drawXTicks(xLabs) {
ctx.save();
let tickLen = canvas.height / 50;
let xDivs = xLen / (xLabs.length - 1);
ctx.beginPath();
ctx.strokeStyle = '#000';
for (let i = 0; i < xLabs.length; i++) {
let tickX = xOrig + xDivs * i;
let tickY = yOrig + tickLen / 2;
ctx.moveTo(tickX, tickY);
ctx.lineTo(tickX, (tickY - tickLen));
}
ctx.stroke();
ctx.restore();
for (let i = 0; i < xLabs.length; i++) {
ctx.save();
ctx.fillStyle = '#000';
ctx.font = '12px Times New Roman';
ctx.textAlign = 'right';
ctx.translate((canvas.width*0.15) + (xDivs * i), canvas.height*0.15);
ctx.rotate(-Math.PI / 4);
let labY = canvas.height * 0.52;
let labX = -canvas.width * 0.38;
ctx.fillText(xLabs[i], labX, labY);
ctx.restore();
}
}
function drawDataPoints(coords, maxX, maxY, color='#000') {
ctx.save();
if (coords.length >= 2) {
let xScale = xLen / maxX;
let yScale = yLen / maxY;
let pointCir = canvas.height / 200;
ctx.beginPath();
for (let i = 0; i < coords.length; i++) {
let xp = xOrig + coords[i][0] * xScale;
let yp = yOrig - coords[i][1] * yScale;
ctx.moveTo(xp, yp);
ctx.arc(xp, yp, pointCir, 0, Math.PI * 2);
}
ctx.fillStyle = color;
ctx.fill();
ctx.strokeStyle = color;
ctx.stroke();
}
else {
ctx.fillStyle = '#000';
ctx.font = '24px Sans-Serif';
ctx.fillText('There is no data to display', (xOrig + xLen * 0.3), (yOrig - yLen * 0.5));
}
ctx.restore();
}
function drawDataLine(coords, maxX, maxY, color='#000') {
ctx.save();
if (coords.length >= 2) {
let xScale = xLen / maxX;
let yScale = yLen / maxY;
let xp = xOrig + coords[0][0] * xScale;
let yp = yOrig - coords[0][1] * yScale;
ctx.beginPath();
ctx.moveTo(xp, yp);
for (let i = 1; i < coords.length; i++) {
xp = xOrig + coords[i][0] * xScale;
yp = yOrig - coords[i][1] * yScale;
ctx.lineTo(xp, yp);
}
ctx.strokeStyle = color;
ctx.stroke();
}
else {
ctx.fillStyle = '#000';
ctx.font = '24px Sans-Serif';
ctx.fillText('There is no data to display', (xOrig + xLen * 0.3), (yOrig - yLen * 0.5));
}
ctx.restore();
}
function fillDataSpace(coords, maxX, maxY, color = '#00F') {
ctx.save();
if (coords.length >= 2) {
let xScale = xLen / maxX;
let yScale = yLen / maxY;
let xp = xOrig + coords[0][0] * xScale;
let yp = yOrig - coords[0][1] * yScale;
var lingrad = ctx.createLinearGradient(xOrig, yOrig - yLen, xOrig + xLen, yOrig);
lingrad.addColorStop(0, '#FFF');
lingrad.addColorStop(0.5, color);
lingrad.addColorStop(1, '#FFF');
ctx.beginPath();
ctx.moveTo(xp, yp);
for (let i = 1; i < coords.length; i++) {
xp = xOrig + coords[i][0] * xScale;
yp = yOrig - coords[i][1] * yScale;
ctx.lineTo(xp, yp);
}
ctx.lineTo(xOrig + xLen, yOrig);
ctx.lineTo(xOrig, yOrig);
ctx.closePath();
ctx.strokeStyle = lingrad;
ctx.stroke();
ctx.fillStyle = lingrad;
ctx.fill();
}
else {
ctx.fillStyle = '#000';
ctx.font = '24px Sans-Serif';
ctx.fillText('There is no data to display', (xOrig + xLen * 0.3), (yOrig - yLen * 0.5));
}
ctx.restore();
}
</script>

下面是一些渲染数据的外观: 员工销售图表

最后说明:我在程序中使用静态类对象处理数据点和数据标签的收集,并将此对象传递给项目的 HTML(Razor(页面。 但是,如果您有办法将数据获取到 JS 函数,则此解决方案应该有效。 如果没有别的,你可以先定义dataSetxLabsyLabsrangedomain变量,然后再弄清楚如何将更大的数据集传递给函数。

最新更新