用Canvas画函数图像
评论 0 热度 811
需要用到几天前写的Mathast,或者修改一下用eval也可以。
必要说明:
id 为canvas的id
exp 为函数 (如sin X 大写X为自变量)
color 为颜色(若不存在会自动生成)
max_x / max_y 为图像显示域,若分别输入10和20,则显示的范围为X∈[-10, 10] Y∈[-20, 20]
用法:
draw('c', 'sin X', '#272727', 4*Math.PI, 3);
注意:Y=2X+5
等函数应该写成2*X+5
,应遵守Mathast的语法原则。还有部分函数由于取值问题使曲线没有连接到位,请脑部 :)
正态分布表达式: pow E (-squ(X)/2*squ10) / sqr(2*PI) * 10
下图为tanX
和 X*sinX+cosX
(不同域下) 的图像
在线实例:foxnes.github.com/mathexp.html
上代码:
function draw(id, exp, color, max_x){
if (typeof color == 'undefined' || color == '') {
var color = (function(m, s, c) {return (c ? arguments.callee(m, s, c - 1) : '#') + s[m.floor(m.random() * 16)]})(Math, '0123456789abcdef', 5);
}
if (typeof max_x == 'undefined' || max_x == 0 || max_x == '') {
var max_x = 10;
}
function transcoo(x, y) {
return {
x: ((w / 2) + (x * division_x)),
y: ((h / 2) - (y * division_y))
};
}
var c = document.getElementById(id),
ctx = c.getContext('2d');
if (exp == false) {
ctx.clearRect(0,0,c.width,c.height);
return true;
}
var w = c.width,
h = c.height,
division_x = w / max_x,
division_y = h / max_x,
sort = [],
sort_ = [],
tmp,
tmp_,
trend = ['asc', 0],
index = 0,
addx = max_x / 150,
sign_ = false,
calc = {
Add: function (arg1, arg2) {
arg1 = arg1.toString();
arg2 = arg2.toString();
var arg1Arr = arg1.split("."), arg2Arr = arg2.split("."), d1 = arg1Arr.length == 2 ? arg1Arr[1] : "", d2 = arg2Arr.length == 2 ? arg2Arr[1] : "";
var maxLen = Math.max(d1.length, d2.length);
var m = Math.pow(10, maxLen);
var result = Number(((arg1 * m + arg2 * m) / m).toFixed(maxLen));
var d = arguments[2];
return typeof d === "number" ? Number((result).toFixed(d)) : result;
},
Sub: function (arg1, arg2) {
return calc.Add(arg1, -Number(arg2), arguments[2]);
},
Mul: function (arg1, arg2) {
var r1 = arg1.toString(), r2 = arg2.toString(), m, resultVal, d = arguments[2];
m = (r1.split(".")[1] ? r1.split(".")[1].length : 0) + (r2.split(".")[1] ? r2.split(".")[1].length : 0);
resultVal = Number(r1.replace(".", "")) * Number(r2.replace(".", "")) / Math.pow(10, m);
return typeof d !== "number" ? Number(resultVal) : Number(resultVal.toFixed(parseInt(d)));
},
Div: function (arg1, arg2) {
var r1 = arg1.toString(), r2 = arg2.toString(), m, resultVal, d = arguments[2];
m = (r2.split(".")[1] ? r2.split(".")[1].length : 0) - (r1.split(".")[1] ? r1.split(".")[1].length : 0);
resultVal = Number(r1.replace(".", "")) / Number(r2.replace(".", "")) * Math.pow(10, m);
return typeof d !== "number" ? Number(resultVal) : Number(resultVal.toFixed(parseInt(d)));
}
};
ctx.beginPath();
ctx.strokeStyle = "#0f0f0f";
ctx.lineWidth = 1;
ctx.moveTo(w / 2, 0);
ctx.lineTo(w / 2, h);
ctx.moveTo(0, h / 2);
ctx.lineTo(w, h / 2);
ctx.stroke();
ctx.closePath();
ctx.beginPath();
ctx.strokeStyle = color;
ctx.lineWidth = 2;
var i = calc.Div(-max_x, 2);
while ( i <= calc.Div(max_x, 2) ) {
tmp = Mathast.run(exp.replace(/x/ig, '(' + i + ')')).result.value;
if (typeof tmp == 'number' && !isNaN(tmp)) {
sort.push({
y: Number(tmp),
x: Number(i)
});
if (index > 0)
tmp_ = calc.Sub(sort[(index - 1)].y, sort[index].y);
else
tmp_ = false;
if (tmp_ <= 0 && trend[0] == 'desc' && tmp_ !== false) {
trend = ['asc', tmp_];
tmp_ = true;
}else if (tmp_ >= 0 && trend[0] == 'asc' && tmp_ !== false){
trend = ['desc', tmp_];
tmp_ = true;
}else{
tmp_ = false;
}
if (tmp_ !== true) {
if (index - 2 >= 0){
tmp = calc.Div(calc.Sub(sort[(index - 2)].y, sort[(index - 1)].y), 2);
tmp_ = calc.Sub(sort[(index - 1)].y, sort[index].y);
}else{
tmp = 0;
tmp_ = 0;
}
if (Math.abs(tmp_) > Math.abs(tmp)){
tmp_ = true;
}else{
tmp_ = false;
}
}
if (tmp_ !== false) {
for (var j = sort[(index - 1)].x; j <= sort[index].x; j = calc.Add(j, calc.Div(addx, 50))) {
tmp = Mathast.run(exp.replace(/X/ig, '(' + j + ')')).result.value;
if (typeof tmp == 'number' && !isNaN(tmp)){
sort.push({
y: Number(tmp),
x: Number(j)
});
}else{
i = calc.Add(i, calc.Div(addx, 50));
continue;
}
}
}
index = sort.length;
}else{
i = calc.Add(i, calc.Div(addx, 50));
continue;
}
i = calc.Add(i, addx);
}
sort.sort(function(a, b){
return a.x - b.x;
});
for (var i = 0; i < sort.length; i++) {
tmp = sort[i];
tmp = transcoo(tmp.x, tmp.y);
if (i > 0 && (Math.abs(tmp.y) < (c.height))){
ctx.lineTo(tmp.x, tmp.y);
}else{
ctx.moveTo(tmp.x, tmp.y);
}
tmp_++;
}
ctx.stroke();
console.log(sort.length)
return {r: true, d: sort.length};
}