<html lang="zh">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>隐函数绘图</title>
<link rel="stylesheet" href="styles.css">
</head>
<body>
<div id="inputContainer">
<label for="expression">f(x,y)=</label>
<input type="text" id="expression" value="sin(x+y)-cos(x*y)-sin(x)">
<button id="drawButton">绘制</button>
</div>
<canvas id="canvas" width="512" height="512"></canvas>
<script src="drawFunction.js"></script>
</body>
</html>
// 全局变量
var canvas, ctx, expressionInput, drawButton, scale, centerX, centerY, dragging, lastX, lastY;
// 窗口加载完成后执行的函数
window.onload = function () {
// 获取画布和上下文
canvas = document.getElementById("canvas");
ctx = canvas.getContext("2d");
// 获取表达式输入框和绘制按钮
expressionInput = document.getElementById("expression");
drawButton = document.getElementById("drawButton");
// 初始化放缩倍数、中心坐标和拖动状态
scale = 0.04;
centerX = canvas.width / 2;
centerY = canvas.height / 2;
dragging = false;
// 监听绘制按钮点击事件
drawButton.addEventListener('click', function () {
drawFunction();
});
// 绑定鼠标事件
canvas.addEventListener('mousedown', onMouseDown);
canvas.addEventListener('mousemove', onMouseMove);
canvas.addEventListener('mouseup', onMouseUp);
canvas.addEventListener('wheel', onMouseWheel);
// 初始绘制
drawFunction();
};
// 绘制函数
function drawFunction() {
// 清除画布
ctx.clearRect(0, 0, canvas.width, canvas.height);
// 绘制坐标轴和网格线
drawAxisAndGrid();
// 解析表达式
var evaluateFunction = parseExpressionToFunction(expressionInput.value);
// 绘制函数图像
drawFunctionGraph(evaluateFunction);
}
// 绘制函数图像
function drawFunctionGraph(evaluateFunction) {
// 扫描每个像素并绘制
for (var x = 0; x < canvas.width; x++) {
for (var y = 0; y < canvas.height; y++) {
var scaledX = (x - centerX) * scale;
var scaledY = (centerY - y) * scale;
var fValues = [
evaluateFunction(scaledX, scaledY),
evaluateFunction(scaledX + scale, scaledY),
evaluateFunction(scaledX + scale, scaledY + scale),
evaluateFunction(scaledX, scaledY + scale)
];
var allNaN = fValues.every(function (value) {
return isNaN(value);
});
if (allNaN) {
continue;
}
// 检查函数值符号
var allPositive = fValues.every(function (value) {
return value > 0;
});
var allNegative = fValues.every(function (value) {
return value < 0;
});
// 如果符号不全相等,则填充红色
if (!allPositive && !allNegative) {
ctx.fillStyle = "red";
ctx.fillRect(x, y, 1, 1);
}
}
}
}
// 绘制坐标轴和网格线
function drawAxisAndGrid() {
// 绘制坐标轴
ctx.beginPath();
ctx.moveTo(0, centerY);
ctx.lineTo(canvas.width, centerY);
ctx.moveTo(centerX, 0);
ctx.lineTo(centerX, canvas.height);
ctx.strokeStyle = "black";
ctx.stroke();
// 绘制网格线和刻度值
ctx.beginPath();
for (var x = centerX; x < canvas.width; x += 50) {
ctx.moveTo(x, 0);
ctx.lineTo(x, canvas.height);
drawTick(x, centerY, true, (x - centerX) * scale);
}
for (var x = centerX; x > 0; x -= 50) {
ctx.moveTo(x, 0);
ctx.lineTo(x, canvas.height);
drawTick(x, centerY, true, (x - centerX) * scale);
}
for (var y = centerY; y < canvas.height; y += 50) {
ctx.moveTo(0, y);
ctx.lineTo(canvas.width, y);
drawTick(centerX, y, false, (centerY - y) * scale);
}
for (var y = centerY; y > 0; y -= 50) {
ctx.moveTo(0, y);
ctx.lineTo(canvas.width, y);
drawTick(centerX, y, false, (centerY - y) * scale);
}
ctx.strokeStyle = "#ddd"; // 网格线颜色
ctx.stroke();
}
// 绘制刻度值
function drawTick(x, y, isXAxis, value) {
ctx.font = "10px Arial";
ctx.fillStyle = "black";
ctx.textAlign = "center";
ctx.textBaseline = "middle";
value = value.toFixed(2);
if (isXAxis) {
ctx.fillText(value, x, y + 10);
} else {
ctx.fillText(value, x - 10, y);
}
}
// 解析表达式为函数
function parseExpressionToFunction(expression) {
// 将输入的函数替换为完整的 Math 函数形式
expression = expression.replace(/tan/g, 'Math.tan');
expression = expression.replace(/sin/g, 'Math.sin');
expression = expression.replace(/cos/g, 'Math.cos');
expression = expression.replace(/abs/g, 'Math.abs');
expression = expression.replace(/ln/g, 'Math.log');
expression = expression.replace(/\^/g, '**'); // 将^(异或)替换为**(乘方)
expression = expression.replace(/e/g, 'Math.E');
return new Function('x', 'y', 'return ' + expression + ';');
}
// 鼠标按下事件处理函数
function onMouseDown(event) {
dragging = true;
lastX = event.clientX;
lastY = event.clientY;
}
// 鼠标移动事件处理函数
function onMouseMove(event) {
if (dragging) {
var deltaX = event.clientX - lastX;
var deltaY = event.clientY - lastY;
lastX = event.clientX;
lastY = event.clientY;
// 移动中心点
centerX += deltaX;
centerY += deltaY;
// 清除画布
ctx.clearRect(0, 0, canvas.width, canvas.height);
// 重新绘制
drawFunction();
}
}
// 鼠标松开事件处理函数
function onMouseUp(event) {
dragging = false;
}
// 鼠标滚轮事件处理函数
function onMouseWheel(event) {
// 放缩倍数的调整量
var delta = event.deltaY > 0 ? 10 / 9 : 9 / 10;
scale *= delta;
// 重新绘制
drawFunction();
}
Output
You can jump to the latest bin by adding /latest
to your URL
Keyboard Shortcuts
Shortcut | Action |
---|---|
ctrl + [num] | Toggle nth panel |
ctrl + 0 | Close focused panel |
ctrl + enter | Re-render output. If console visible: run JS in console |
Ctrl + l | Clear the console |
ctrl + / | Toggle comment on selected lines |
ctrl + ] | Indents selected lines |
ctrl + [ | Unindents selected lines |
tab | Code complete & Emmet expand |
ctrl + shift + L | Beautify code in active panel |
ctrl + s | Save & lock current Bin from further changes |
ctrl + shift + s | Open the share options |
ctrl + y | Archive Bin |
Complete list of JS Bin shortcuts |
JS Bin URLs
URL | Action |
---|---|
/ | Show the full rendered output. This content will update in real time as it's updated from the /edit url. |
/edit | Edit the current bin |
/watch | Follow a Code Casting session |
/embed | Create an embeddable version of the bin |
/latest | Load the very latest bin (/latest goes in place of the revision) |
/[username]/last | View the last edited bin for this user |
/[username]/last/edit | Edit the last edited bin for this user |
/[username]/last/watch | Follow the Code Casting session for the latest bin for this user |
/quiet | Remove analytics and edit button from rendered output |
.js | Load only the JavaScript for a bin |
.css | Load only the CSS for a bin |
Except for username prefixed urls, the url may start with http://jsbin.com/abc and the url fragments can be added to the url to view it differently. |