htm+JS实现绘制数学函数绘图

时间:2022-10-30 17:55:23

htm+JS实现绘制数学函数绘图

其中常用的函数可参见,JS中常用的Math方法 https://blog.51cto.com/u_10725738/5253407

https://www.jb51.net/article/9151.htm

JS(JavaScript)常用Math函数与用法说明

方法 描述 示例
abs(x) 返回数的绝对值。 Math.abs(-10) //返回10
acos(x) 返回数的反余弦值。 Math.acos(1) //返回0
asin(x) 返回数的反正弦值。 Math.asin(1) //返回1.5707963267948965
atan(x) 以介于 -PI/2 与 PI/2 弧度之间的数值来返回 x 的反正切值。 Math.atan(0.50) //返回0.4636476090008061
atan2(y,x) 返回从 x 轴到点 (x,y) 的角度(介于 -PI/2 与 PI/2 弧度之间)。 Math.atan2(5,5) //输出0.7853981633974483
ceil(x) 对数进行上舍入。

Math.ceil(0.60)
Math.ceil(-5.1) 

//返回1
//返回-5
cos(x) 返回数的余弦。 Math.cos(0) //返回1
exp(x) 返回 e 的指数。 Math.exp(5) //返回148.4131591025766
floor(x) 对数进行下舍入。 Math.floor(0.60)
Math.floor(-5.1)
//返回0
//返回-6
log(x) 返回数的自然对数(底为e)。 Math.log(1) //返回0
max(x,y) 返回 x 和 y 中的最高值。 Math.max(5,7) //返回7
min(x,y) 返回 x 和 y 中的最低值。 Math.min(5,7) //返回5
pow(x,y) 返回 x 的 y 次幂。 Math.pow(2,4) //返回16
random() 返回 0 ~ 1 之间的随机数。 Math.random() //返回类似0.6654807284142312的随机数
round(x) 把数四舍五入为最接近的整数。 Math.round(0.60)
Math.round(-4.40)
//返回1
//返回-4
sin(x) 返回数的正弦。 Math.sin(0) //返回0
sqrt(x) 返回数的平方根。 Math.sqrt(0.64) //返回0.8
tan(x) 返回角的正切。 Math.tan(10) //返回0.6483608274590866

运行效果

htm+JS实现绘制数学函数绘图

 项目目录结构

htm+JS实现绘制数学函数绘图

 特别说明源码取自网络。

数学函数绘图.html 源码:

<!DOCTYPE html>
<html>
	<head>
		<meta charset="UTF-8">
		<title>函数图像绘制工具</title>
		<script type="text/javascript" src="js/funcImg.js"></script>
		<style>
			#div-img {
				/* 此决定画布的宽高 */
				width: 500px;
				height: 500px;
			}
			#input-controller {
				padding: 10px;
				background-color: azure;
			}
		</style>
	</head>
	<body>
		<div id="input-controller">
			<div id="function">
				<button onclick="Add()">添加函数</button>
				<span id="mod" style="display:none" name="0">
					<input type="color"/>y=
					<input type="text" value="x^3" name="Fun"/>
					<button onclick="Delete(this.parentNode)">Delete</button>
	                <input type="checkbox" onclick="reDraw()" checked="checked"/>Draw Line
				</span>
			</div>
			<div id="option">
				X:<input id="xLeftValue" /> ~
				<input id="xRightValue" />
				<br> Y:
				<input id="yLeftValue" /> ~
				<input id="yRightValue" />
				<br>
				<span id="show-size">Size:</span>
			</div>
			<button onclick="change()">画图</button>
		</div>
		<div id="main">
			<h1>函数图像绘制工具</h1>
			<div id="div-img">
				<canvas id="graph"></canvas>
			</div>
		</div>
	</body>
	<script>
		const FONT_STYLE = "10px 黑体";
		const MIN = 1e-4;
		const MAX = 1e8;
		const EPS = 1e-12;
		const CANVAS = $("graph");
		const CONTEXT_2D = CANVAS.getContext("2d");
		const FUN_IMG_WIDTH = CANVAS.parentNode.clientWidth;
		const FUN_IMG_HEIGHT = CANVAS.parentNode.clientHeight;
		var xLeftValue = -FUN_IMG_WIDTH / 100; // x最左的值
		var xRightValue = FUN_IMG_WIDTH / 100;
		var yLeftValue = -FUN_IMG_HEIGHT / 100;
		var yRightValue = FUN_IMG_HEIGHT / 100;
		var tableX, tableY, countX, countY;
		var funStage = 0,
			mouseX, mouseY;
		var tmp;
	</script>
	<script>
		CANVAS.width = FUN_IMG_WIDTH;
		CANVAS.height = FUN_IMG_HEIGHT;
		$("show-size").innerHTML = "Size:" + FUN_IMG_WIDTH + "*" + FUN_IMG_HEIGHT;
		CANVAS.onmousedown = function(ob) {
			mouseX = ob.layerX;
			mouseY = ob.layerY;
			funStage = 1;
		}
		CANVAS.onmousemove = function(ob) {
			if(funStage != 1) {
				return;
			}
			var NoX, NoY, det;
			NoX = ob.layerX;
			NoY = ob.layerY;
			det = (mouseX - NoX) / FUN_IMG_WIDTH * (xRightValue - xLeftValue);
			xLeftValue += det;
			xRightValue += det;
			det = (NoY - mouseY) / FUN_IMG_HEIGHT * (yRightValue - yLeftValue);
			yLeftValue += det;
			yRightValue += det;
			mouseX = NoX;
			mouseY = NoY;
			reDraw();
			update();
		}
		CANVAS.onmouseup = function(ob) {
			if(funStage == 1) {
				funStage = 0;
				reDraw();
			}
		}
		CANVAS.onmouseleave = function(ob) {
			if(funStage == 1) {
				funStage = 0;
				reDraw();
			}
		}
		CANVAS.onmousewheel = function(ob) {
			// 取消事件的默认动作
			ob.preventDefault();
			// 放大的比例
			var ScaleRate = 0.9;
			var detail;
			if(ob.wheelDelta) {
				detail = ob.wheelDelta;
			} else if(ob.detail) {
				detail = ob.detail;
			}
			if(detail > 0) {
				scale(ob.layerX, FUN_IMG_HEIGHT - 1 - ob.layerY, ScaleRate);
			} else {
				scale(ob.layerX, FUN_IMG_HEIGHT - 1 - ob.layerY, 1 / ScaleRate);
			}
			reDraw();
			update();
		}
		// 初始化
		reDraw();
		update();
		Add();
	</script>
</html>

funcImg.js 源码:

function $(id) {
	return document.getElementById(id);
}
function getRandomColor() {
	var color = '#' + ('00000' + (Math.random() * 0x1000000 << 0).toString(16)).substr(-6);
	return color;
}
function FunWork(f, x) {
	switch(f) {
		case "":
			{
				return x;
				break;
			}
		case "sin":
			{
				return Math.sin(x);
				break;
			}
		case "cos":
			{
				return Math.cos(x);
				break;
			}
		case "tan":
			{
				return Math.tan(x);
				break;
			}
		case "abs":
			{
				return Math.abs(x);
				break;
			}
		case "sqrt":
			{
				return Math.sqrt(x);
				break;
			}
		case "ln":
			{
				return Math.log(x);
				break;
			}
		case "log":
			{
				return Math.log(x) / Math.LN2;
				break;
			} //2为底
		case "lg":
			{
				return Math.log(x) / Math.LN10;
				break;
			} //10为底
		case "floor":
			{
				return Math.floor(x);
				break;
			}
		case "ceil":
			{
				return Math.ceil(x);
				break;
			}
		case "int":
			{
				return parseInt(x);
				break;
			}
		default:
			{
				return NaN;
				break;
			}
	}
}
function ChangeToPointY(y) {
	return FUN_IMG_HEIGHT - 1 - parseInt((y - yLeftValue) / (yRightValue - yLeftValue) * FUN_IMG_HEIGHT);
}
function isChar(c) {
	return(c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
}
function isDigit(c) {
	return c >= '0' && c <= '9';
}
function priority(c) {
	switch(c) {
		case '(':
			return 0;
			break;
		case '+':
			return 1;
			break;
		case '-':
			return 1;
			break;
		case '*':
			return 2;
			break;
		case '/':
			return 2;
			break;
		case '^':
			return 3;
			break;
		default:
			return -1;
			break;
	}
}
// 是运算符
function isOpt(c) {
	return priority(c) != -1;
}
function singleCalc(c, a, b) {
	if(c == '+') {
		return a + b;
	} else
	if(c == '-') {
		return a - b;
	} else
	if(c == '*') {
		return a * b;
	} else
	if(c == '/') {
		return a / b;
	} else
	if(c == '^') {
		return Math.pow(a, b);
	} else {
		return NaN;
	}
}
function getTable() {
	tmp = (xRightValue - xLeftValue + EPS) / 20;
	tableX = 1;
	countX = 0;
	countY = 0;
	while(tableX < tmp) {
		tableX *= 10;
	}
	while(tableX / 10 > tmp) {
		tableX /= 10;
		countX++;
	}
	if(tableX >= 1) {
		countX = 0;
	}
	if(tableX / 5 > tmp) {
		tableX /= 5;
		countX++;
	} else if(tableX / 2 > tmp) {
		tableX /= 2;
		countX++;
	}
	var i = parseInt(xLeftValue / tableX) + (xLeftValue > 0)
	for(; i * tableX < xRightValue; i++) {
		if(i == 0) {
			// y轴
			CONTEXT_2D.fillStyle = "black";
		} else {
			// 普通竖线
			CONTEXT_2D.fillStyle = "#CDB7B5";
		}
		tmp = (i * tableX - xLeftValue) / (xRightValue - xLeftValue) * FUN_IMG_WIDTH;
		var _width = 1;
		var _height = FUN_IMG_HEIGHT;
		CONTEXT_2D.fillRect(tmp, 0, _width, _height);
		// 竖线上的数字
		CONTEXT_2D.fillStyle = "red";
		CONTEXT_2D.font = FONT_STYLE;
		var _text = (i * tableX).toFixed(countX);
		var _x = tmp + 2;
		var _y = 10;
		CONTEXT_2D.fillText(_text, _x, _y);
	}
	tmp = (yRightValue - yLeftValue + EPS) / 20;
	tableY = 1;
	while(tableY < tmp) {
		tableY *= 10;
	}
	while(tableY / 10 > tmp) {
		tableY /= 10, countY++;
	}
	if(tableY / 5 > tmp) {
		tableY /= 5, countY++;
	} else if(tableY / 2 > tmp) {
		tableY /= 2, countY++;
	}
	if(tableY >= 1) {
		countY = 0;
	}
	var i = parseInt(yLeftValue / tableY) + (yLeftValue > 0);
	for(; i * tableY < yRightValue; i++) {
		// 横线
		if(i == 0) {
			// x轴
			CONTEXT_2D.fillStyle = "black";
		} else {
			// 普通横线
			CONTEXT_2D.fillStyle = "#CDB7B5";
		}
		tmp = (i * tableY - yLeftValue) / (yRightValue - yLeftValue) * FUN_IMG_HEIGHT;
		CONTEXT_2D.fillRect(0, FUN_IMG_HEIGHT - 1 - tmp, FUN_IMG_WIDTH, 1);
		// 横线上的数字
		CONTEXT_2D.fillStyle = "blue";
		CONTEXT_2D.font = FONT_STYLE;
		CONTEXT_2D.fillText((i * tableY).toFixed(countY), 0, FUN_IMG_HEIGHT - 1 - tmp);
	}
}
function drawArc(x, y) {
	CONTEXT_2D.beginPath();
	// arc(弧形),画圆
	CONTEXT_2D.arc(x, y, 1, 0, Math.PI * 2);
	CONTEXT_2D.closePath();
	CONTEXT_2D.fill();
}
function drawLine(lx, ly, px, py) {
	CONTEXT_2D.beginPath();
	CONTEXT_2D.moveTo(lx, ly);
	CONTEXT_2D.lineTo(px, py);
	CONTEXT_2D.closePath();
	CONTEXT_2D.stroke(); // 绘制
}
function reDraw() {
	CONTEXT_2D.clearRect(0, 0, FUN_IMG_WIDTH, FUN_IMG_HEIGHT);
	getTable();
	getFunction();
}
function change() {
	xLeftValue = parseFloat($("xLeftValue").value);
	xRightValue = parseFloat($("xRightValue").value);
	yLeftValue = parseFloat($("yLeftValue").value);
	yRightValue = parseFloat($("yRightValue").value);
	reDraw();
}
function update() {
	$("xLeftValue").value = xLeftValue;
	$("xRightValue").value = xRightValue;
	$("yLeftValue").value = yLeftValue;
	$("yRightValue").value = yRightValue;
}
function scale(x, y, times) {
	if(x < 0 || x >= FUN_IMG_WIDTH || y < 0 || y >= FUN_IMG_HEIGHT) return;
	if(times < 1 && (xRightValue - xLeftValue < MIN || yRightValue - yLeftValue < MIN)) {
		return;
	}
	if(times > 1 && (xRightValue - xLeftValue > MAX || yRightValue - yLeftValue > MAX)) {
		return;
	}
	x = xLeftValue + (xRightValue - xLeftValue) / FUN_IMG_WIDTH * x;
	y = yLeftValue + (yRightValue - yLeftValue) / FUN_IMG_HEIGHT * y;
	xLeftValue = x - (x - xLeftValue) * times;
	xRightValue = x + (xRightValue - x) * times;
	yLeftValue = y - (y - yLeftValue) * times;
	yRightValue = y + (yRightValue - y) * times;
}
function Calc(fun, X, Value) {
	var number = new Array(),
		opt = new Array(),
		F = new Array(),
		now = 0,
		f = "",
		tmp, a, b, sign = 1,
		base = 0,
		j;
	for(var i = 0; i < fun.length; i++) {
		for(j = 0; j < X.length; j++)
			if(X[j] == fun[i]) {
				if(i == 0 || isOpt(fun[i - 1])) now = Value[j];
				else now *= Value[j];
				break;
			}
		if(j == X.length)
			if(fun[i] == '(') F.push(f), f = "", opt.push('(');
			else
		if(fun[i] == ')') {
			number.push(now * sign);
			now = 0;
			sign = 1;
			base = 0;
			while((tmp = opt.pop()) != '(') {
				b = number.pop();
				a = number.pop();
				tmp = singleCalc(tmp, a, b);
				number.push(tmp);
			}
			now = FunWork(F.pop(), number.pop());
		} else
		if(fun[i] == '.') base = 1;
		else
		if(fun[i] == '+' && (i == 0 || priority(fun[i - 1]) != -1));
		else
		if(fun[i] == '-' && (i == 0 || priority(fun[i - 1]) != -1)) sign = -1;
		else
		if(fun[i] == 'e')
			if(i == 0 || isOpt(fun[i - 1])) now = Math.E;
			else now *= Math.E;
		else
		if(fun[i] == 'p' && fun[i + 1] == 'i') {
			if(i == 0 || isOpt(fun[i - 1])) now = Math.PI;
			else now *= Math.PI;
			i += 1;
		} else
		if(isDigit(fun[i]))
			if(base == 0) now = now * 10 + (fun[i] - '0');
			else base /= 10, now += base * (fun[i] - '0');
		else
		if(isChar(fun[i])) f += fun[i];
		else if(isOpt(fun[i])) {
			number.push(now * sign);
			now = 0;
			sign = 1;
			base = 0;
			var s = priority(fun[i]);
			if(s == -1) return 0;
			while(s <= priority(opt[opt.length - 1])) {
				b = number.pop();
				a = number.pop();
				tmp = singleCalc(opt.pop(), a, b);
				number.push(tmp);
			}
			opt.push(fun[i]);
		}
	}
	number.push(now * sign);
	while(opt.length > 0) {
		b = number.pop();
		a = number.pop();
		tmp = singleCalc(opt.pop(), a, b);
		number.push(tmp);
	}
	return number.pop();
}
function getFunction() {
	// group:函数(可能是复数)
	var group = document.getElementsByName("Fun");
	var x, y;
	var lax, lay;
	var px, py
	var color, outSide, type
	var ValueL, ValueR, ValueS, isDrawLine, tmp, TMP;
	for(var k = 1; k < group.length; k++) {
		var _funcItem = group[k].parentNode;
		outSide = 1;
		//type = _funcItem.children[0].value;
		// 颜色
		color = _funcItem.children[0].value;
		// 函数表达式
		funcExpression = group[k].value;
		// 是否画线(默认画点)
		isDrawLine = _funcItem.children[3].checked;
		CONTEXT_2D.fillStyle = CONTEXT_2D.strokeStyle = color;
		for(var i = 0; i < FUN_IMG_WIDTH; i++) {
			x = xLeftValue + (xRightValue - xLeftValue) / FUN_IMG_WIDTH * i;
			y = Calc(funcExpression, ['x'], [x]);
			if(isNaN(y)) {
				continue;
			}
			px = i;
			py = ChangeToPointY(y);
			if(y >= yLeftValue && y < yRightValue) {
				// 画圆
				drawArc(px, py);
				if(isDrawLine) {
					drawLine(lax, lay, px, py);
				}
				outSide = 0;
			} else {
				if(isDrawLine) {
					if(!outSide) {
						drawLine(lax, lay, px, py);
					}
				} else {}
				outSide = 1;
			}
			lax = px;
			lay = py;
		}
	}
}
function Add() {
	var newInput = $("mod").cloneNode(true);
	newInput.style.display = "block";
	newInput.children[0].value = getRandomColor();
	$("function").appendChild(newInput);
}
function Delete(node) {
	node.parentNode.removeChild(node);
}

附学习资料
https://blog.csdn.net/Mingyueyixi/article/details/80964430