纯Javascript做的表达式可绘图函数计算器

浅浅地学习了JavaScript和基本html语法。觉得自己的安卓手机的计算器比较简陋,参考csdn某些大佬的文章,自己改写了一个可绘图函数表达式计算器。

支持+ - * / ^和 sin,cos,tan,asin,acos,atan,abs,sqrt,ln,lg,floor,ceil,int 数学函数
如果表达式包含x就做函数画图,没有x就直接计算。

效果图如下:

 

附代码:

<!DOCTYPE html>
<html>
    <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1"> 
    <title>函数图像绘制工具</title>
        
<script language="javascript">
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) {
    //支持+ - * / ^和 sin,cos,tan,asin,acos,atan,abs,sqrt,ln,lg,floor,ceil,int 数学函数
    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 "asin":
            {
                return Math.asin(x);
                break;
            }
        case "acos":
            {
                return Math.acos(x);
                break;
            }
        case "atan":
            {
                return Math.atan(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为底 lhlnote 比较奇怪
        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 clearDraw() {
    // lhlnote 清空画布
    CONTEXT_2D.clearRect(0, 0, FUN_IMG_WIDTH, FUN_IMG_HEIGHT);
    getTable();
}
function increaseDraw() {
    // lhlnote 放大画布
    var times = 0.9;
    xLeftValue = xLeftValue * times;
    xRightValue = xRightValue * times;
    yLeftValue = yLeftValue * times;
    yRightValue = yRightValue * times;
    reDraw();
    update();
}
function decreaseDraw() {
    // lhlnote 缩小画布
    var times = 0.9;
    xLeftValue = xLeftValue / times;
    xRightValue = xRightValue / times;
    yLeftValue = yLeftValue / times;
    yRightValue = yRightValue / times;
    reDraw();
    update();
}
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) {
    //y = Calc(funcExpression, ['x'], [x]);
    //console.log("Value--"+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");
    //console.log("group.length--"+group.length);
    var x, y;
    var lax, lay;
    var px, py
    var color, outSide, type
    var ValueL, ValueR, ValueS, isDrawLine, tmp, TMP;
    var haveX = "";
    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;
        //正则表达式方法2 在数字和字母之间插入* 例如2sin(x)改为2*sin(x)。$1表示第一个括号匹配字符串,以此类推
        funcExpression = funcExpression.replace(/([0-9])([a-z])/gi, '$1*$2');
        //console.log("funExp--" + funcExpression);
        // 是否画线(默认画点)
        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]);
            my1=y;
            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;
        }
        //console.log("py--"+my1);
        //判断是否含有x lhlnote
        //console.log("funcExpression--"+funcExpression);
        if(funcExpression.includes('x')){
            haveX = haveX + "[" + k + "]" +":含x; \n";
        }
        else{
            haveX = haveX + "[" + k + "]" + "无x,结果是 " + my1 +"; \n";
        }
        showResult.innerText = haveX;
    }
}
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);
}
</script>
        <style>
            #div-img {
                /* 此决定画布的宽高 */
                width: 500px;
                height: 500px;
            }
            #input-controller {
                padding: 10px;
                background-color: azure;
            }
        </style>
    </head>
    <body>
    
    
        <div id="input-controller">
        <div style="color:#00d">支持+ - * / ^和 sin,cos,tan,asin,acos,atan,abs,sqrt,ln,lg,floor,ceil,int 数学函数<br>
        如果表达式包含x就做函数画图,没有x就直接计算。</div>
            <div id="function">
                <button οnclick="Add()">添加函数</button>
                <span id="mod" style="display:none" name="0">
                    <input type="color" />y=
                    <input type="text" value="x^3" name="Fun"/>
                    <button οnclick="Delete(this.parentNode)">Delete</button>
                    <input type="checkbox" οnclick="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><br>
                <span id="showResult" style="font-size:18px;color:red">Result</span>
            </div>
            <button οnclick="change()">画图/计算</button>
            <button οnclick="clearDraw()">清空画布</button>
            <button οnclick="increaseDraw()">放大</button>
            <button οnclick="decreaseDraw()">缩小</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>

//如果需要下载完整源代码,请到:sbdlhl/JavaScriptCalandDraw - 码云 - 开源中国 (gitee.com)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值