diff --git a/css/iconfont.ttf b/css/iconfont.ttf new file mode 100644 index 0000000000000000000000000000000000000000..2f33db86a098f4c12eee19dacc3ae51b4dc677d5 Binary files /dev/null and b/css/iconfont.ttf differ diff --git a/css/reset.css b/css/reset.css new file mode 100644 index 0000000000000000000000000000000000000000..80ec8c001f9f7b8447712cd4c77ce9fb00c0fb03 --- /dev/null +++ b/css/reset.css @@ -0,0 +1,47 @@ +/* v2.0 | 20110126 + http://meyerweb.com/eric/tools/css/reset/ + License: none (public domain) +*/ +html, body, div, span, applet, object, iframe, +h1, h2, h3, h4, h5, h6, p, blockquote, pre, +a, abbr, acronym, address, big, cite, code, +del, dfn, em, img, ins, kbd, q, s, samp, +small, strike, strong, sub, sup, tt, var, +b, u, i, center, +dl, dt, dd, ol, ul, li, +fieldset, form, label, legend, +table, caption, tbody, tfoot, thead, tr, th, td, +article, aside, canvas, details, embed, +figure, figcaption, footer, header, hgroup, +menu, nav, output, ruby, section, summary, +time, mark, audio, video { + margin: 0; + padding: 0; + border: 0; + font-size: 100%; + font: inherit; + vertical-align: baseline; +} +/* HTML5 display-role reset for older browsers */ +article, aside, details, figcaption, figure, +footer, header, hgroup, menu, nav, section { + display: block; +} +body { + line-height: 1; +} +ol, ul { + list-style: none; +} +blockquote, q { + quotes: none; +} +blockquote:before, blockquote:after, +q:before, q:after { + content: ''; + content: none; +} +table { + border-collapse: collapse; + border-spacing: 0; +} \ No newline at end of file diff --git a/css/style.css b/css/style.css new file mode 100644 index 0000000000000000000000000000000000000000..c0b94cd308b14aa522c445d8e0d0650aa1eaa188 --- /dev/null +++ b/css/style.css @@ -0,0 +1,149 @@ +@font-face { + font-family: 'iconfont'; + src: url('iconfont.ttf?t=1636334307125') format('truetype'); +} + +.display { + position: relative; + background-color: rgb(245, 245, 245); + height: 35.5vh; +} + +#text { + position: absolute; + right: 20px; + bottom: 20px; + font-size: 30px; +} + +.function { + width: 100%; + display: flex; + flex-wrap: wrap; +} + + +/* 所有按键 */ + +.function>* { + height: 9vh; + width: 16.4vw; + margin: 1.5vh 0 0 3vw; + border-radius: 2vw; + background-color: rgb(245, 245, 245); + text-align: center; + line-height: 9vh; + font-size: 26px; +} + +.function>*:hover { + background-color: rgb(235, 235, 235); +} + +.function>*:active { + background-color: rgb(205, 205, 205); +} + +#sin, +#sqrt, +#pow, +#pow2, +#pi, +#cos, +#tan, +#lg, +#ln, +#e { + color: rgb(99, 99, 99); +} + + +/* 功能键 */ + +#clear, +#divide, +#multiply, +#backspace, +#minus, +#plus { + background-color: rgb(233, 240, 255); + color: rgb(79, 118, 239); +} + +#clear:hover, +#divide:hover, +#multiply:hover, +#backspace:hover, +#minus:hover, +#plus:hover { + background-color: rgb(223, 230, 245); +} + +#clear:active, +#divide:active, +#multiply:active, +#backspace:active, +#minus:active, +#plus:active { + background-color: rgb(203, 210, 225); +} + + +/* 除清除以外的功能键 */ + +#divide, +#multiply, +#backspace, +#minus, +#plus { + font-size: 36px; +} + + +/* 等号键 */ + +#equals { + position: absolute; + right: 0; + top: 77.5vh; + right: 3vw; + height: 19.5vh; + line-height: 19.5vh; + font-size: 36px; + background-color: rgb(59, 131, 119); + color: rgb(253, 253, 253); +} + +#equals:hover { + background-color: rgb(49, 121, 109); +} + +#equals:active { + background-color: rgb(29, 101, 89); +} + + +/* 图标字体 */ + +#sqrt, +#backspace { + font-family: "iconfont" !important; + font-size: 26px; + font-style: normal; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +#sqrt { + font-weight: bold; +} + + +/* 上标 */ + +.function sup { + position: relative; + /* background-color: transparent; */ + font-size: 16px; + bottom: 1.5vh; +} \ No newline at end of file diff --git a/index.html b/index.html new file mode 100644 index 0000000000000000000000000000000000000000..d7a79950aa387d13d96ce55e2446a6e4b4644db1 --- /dev/null +++ b/index.html @@ -0,0 +1,57 @@ + + + + + + + + H5计算器 + + + + + + + + + +
+ +
+
+
sin
+
+
xy
+
x2
+
π
+
cos
+
C
+
÷
+
×
+
󰆖
+
tan
+
7
+
8
+
9
+
+
lg
+
4
+
5
+
6
+
+
ln
+
1
+
2
+
3
+ +
+
e
+
%
+
0
+
.
+ +
+ + + + diff --git a/js/events.js b/js/events.js new file mode 100644 index 0000000000000000000000000000000000000000..0600da05ee79421b23e127f1dcfac0690c21cc22 --- /dev/null +++ b/js/events.js @@ -0,0 +1,110 @@ +// 添加按键的响应事件 +window.onload = function() { + document.getElementById("sin").addEventListener('click', function() { + push("sin"); + }); + document.getElementById("sqrt").addEventListener('click', function() { + push("√"); + }); + document.getElementById("pow").addEventListener('click', function() { + push("^"); + }); + document.getElementById("pow2").addEventListener('click', function() { + push("^2"); + }); + document.getElementById("pi").addEventListener('click', function() { + push("π"); + }); + document.getElementById("cos").addEventListener('click', function() { + push("cos"); + }); + document.getElementById("divide").addEventListener('click', function() { + push("÷"); + }); + document.getElementById("multiply").addEventListener('click', function() { + push("×"); + }); + document.getElementById("tan").addEventListener('click', function() { + push("tan"); + }); + document.getElementById("num7").addEventListener('click', function() { + push("7"); + }); + document.getElementById("num8").addEventListener('click', function() { + push("8"); + }); + document.getElementById("num9").addEventListener('click', function() { + push("9"); + }); + document.getElementById("minus").addEventListener('click', function() { + push("-"); + }); + document.getElementById("lg").addEventListener('click', function() { + push("lg"); + }); + document.getElementById("num4").addEventListener('click', function() { + push("4"); + }); + document.getElementById("num5").addEventListener('click', function() { + push("5"); + }); + document.getElementById("num6").addEventListener('click', function() { + push("6"); + }); + document.getElementById("plus").addEventListener('click', function() { + push("+"); + }); + document.getElementById("ln").addEventListener('click', function() { + push("ln"); + }); + document.getElementById("num1").addEventListener('click', function() { + push("1"); + }); + document.getElementById("num2").addEventListener('click', function() { + push("2"); + }); + document.getElementById("num3").addEventListener('click', function() { + push("3"); + }); + document.getElementById("e").addEventListener('click', function() { + push("e"); + }); + document.getElementById("percent").addEventListener('click', function() { + push("%"); + }); + document.getElementById("num0").addEventListener('click', function() { + push("0"); + }); + document.getElementById("dot").addEventListener('click', function() { + push("."); + }); + document.getElementById("backspace").addEventListener('click', function() { + var old_text = document.getElementById("text").innerHTML; + // 如果要删除的是函数(sin, ln等)或“Error”,则删除整体 + if (/[a-z]$/.test(old_text)) { + document.getElementById("text").innerHTML = old_text.match(/.*?(?=(Error|sin|cos|tan|lg|ln)$)/)[0]; + } else { + document.getElementById("text").innerHTML = old_text.substring(0, old_text.length - 1); + } + }); + document.getElementById("clear").addEventListener('click', function() { + document.getElementById("text").innerHTML = ""; + }); + document.getElementById("equals").addEventListener('click', function() { + var text = document.getElementById("text").innerHTML; + document.getElementById("text").innerHTML = calc(text); + document.getElementById("text").classList.add("finish"); + }); +} + +function push(str) { + var old_text = document.getElementById("text").innerHTML; + if (old_text == "Error" || /Infinity/.test(old_text)) { + old_text = ""; + } + if (/^([0-9]|π|e|%|\.)$/.test(str) && document.getElementById("text").classList.contains("finish")) { + old_text = ""; + } + document.getElementById("text").classList.remove("finish"); + document.getElementById("text").innerHTML = old_text + str; +} diff --git a/js/main.js b/js/main.js new file mode 100644 index 0000000000000000000000000000000000000000..2323bd7e48e8fd72f240a12f06423b63117875cb --- /dev/null +++ b/js/main.js @@ -0,0 +1,236 @@ +// 计算 +function calc(str) { + /* + 对象格式 + (Root)[ // 要计算的表达式 + (MathObject){ // 不含乘、除运算的式子 + sign: 1 // 符号 + expression: (SymbolArray)[ + (Symbol){ // 一个数字或运算符 + type: "number", + value: "123" + }, + (Symbol){ + type: "operation" + value: "sin" + }, + (Symbol){...}, + (Symbol){...}, + ... + ] + }, + (MathObject){...}, + (MathObject){...}, + ... + ] + */ + try { + // 无内容,返回空字符串 + if (str == "") { + return ""; + } + // 检查表达式语法 + // 百分号后不允许出现数字 + if (/%([0-9]?\.[0-9]?|[0-9]+)/.test(str)) { + throw new Error("Unexpected number after %"); + } + // 不允许出现连续的加号或减号 + if (/[+-]{2}/.test(str)) { + throw new Error("Plus or minux can't appear together."); + } + // 一个数字中不允许出现两个小数点 + if (/\.[0-9]*\./.test(str)) { + throw new Error("Unexpected number."); + } + // 末尾如果有加号或减号,则加一个0 + if (/[+-]$/.test(str)) { + str += "0"; + } + // 构造Root对象 + var root = split(str); + for (var i = 0; i < root.length; i++) { + root[i].expression = buildSymbolArray(root[i].expression); + } + // console.log(root); + var result = 0; // 计算结果 + // 逐个运算SymbolArray表达式的值 + for (var i = 0; i < root.length; i++) { + // 如果符号是一个百分数,需要特别处理 + if (root[i].expression.length == 1 && root[i].expression[0].type == "number" && /([0-9]+|[0-9]*\.[0-9]*)%/.test(root[i].expression[0].value)) { + // 如果该百分数是第一个式子,则正常运算 + if (i != 0) { + // 此处特别处理,比如100+10%=110 + result *= 1 + root[i].sign * parseNumber(root[i].expression[0].value); + } else { + result += root[i].sign * resolveSymbolArray(root[i].expression); + } + } else { + result += root[i].sign * resolveSymbolArray(root[i].expression); + } + } + // console.log(result); + if (isNaN(result)) { + throw new Error("Invalid calculation result."); + } + // 保留11位小数 + return parseFloat(result.toFixed(11)); + } catch (e) { + console.error(e); + return "Error"; + } +} + +// 拆分+、- +function split(str) { + // 如果第一个字符不是正号或负号,则手动加一个正号 + if (str[0] != '+' && str[0] != '-') { + str = '+' + str; + } + var array = new Array(); + var index = 1; + while (index <= str.length) { + if (index == str.length || str[index] == '+' || str[index] == '-') { + array.push({ + sign: str[0] == '+' ? 1 : -1, + expression: str.substring(1, index) + }); + str = str.substring(index, str.length) + var index = 1; + continue; + } + index++; + } + return array; +} + +// 构造SymbolArray +function buildSymbolArray(str) { + var symbolArray = new Array(); + var number_re = /^(([0-9]*\.[0-9]*|[0-9]+)%?|π|e)/; // 匹配数字的正则表达式 + var operation_re = /^(sin|√|\^|cos|÷|×|tan|-|lg|+|ln)/; // 匹配运算符的正则表达式 + while (str.length != 0) { + if (number_re.test(str)) { + var result = str.match(number_re)[0]; + symbolArray.push({ + type: "number", + value: result + }); + str = str.substring(result.length); + } else if (operation_re.test(str)) { + var result = str.match(operation_re)[0]; + symbolArray.push({ + type: "operation", + value: result + }); + str = str.substring(result.length); + } else { + // 错误 + throw new Error("Building symbol array failed."); + } + } + return symbolArray; +} + +// 计算SymbolArray的值 +function resolveSymbolArray(symbolArray) { + // 如数组为空,返回乘法元1 + if (symbolArray.length == 0) { + return 1; + } + // 第一个符号是数字 + if (symbolArray[0].type == "number") { + var value = parseNumber(symbolArray.shift().value); + // 没有第二个符号 + if (symbolArray.length == 0) { + return value; + } + // 第二个符号也是数字,比如2π + if (symbolArray[0].type == "number") { + return value * resolveSymbolArray(symbolArray); + } else { + var operation = symbolArray[0].value; + // 一元运算符 + if (operation == "sin" || operation == "sqrt" || operation == "cos" || operation == "tan" || operation == "lg" || operation == "ln" || operation == "√") { + return value * resolveSymbolArray(symbolArray); + } + // 二元运算符 + else { + symbolArray.shift(); + if (symbolArray.length == 0) { + // 二元运算符后面没有内容,报错 + throw new Error("No number or operation after symbol " + operation); + } + var object = symbolArray[0]; // 二元运算符后面的第一个符号 + if (object.type == "number") { + // 数字和数字运算 + symbolArray.shift(); + var number = parseNumber(object.value); + if (operation == "^") { + return Math.pow(value, number) * resolveSymbolArray(symbolArray); + } else if (operation == "×") { + return value * number * resolveSymbolArray(symbolArray); + } else if (operation == "÷") { + return value / number * resolveSymbolArray(symbolArray); + } + } else { + // 数字和函数运算,如3×sin6 + if (operation == "^") { + return Math.pow(value, resolveSymbolArray(symbolArray)); + } else if (operation == "×") { + return value * resolveSymbolArray(symbolArray); + } else if (operation == "÷") { + return value / resolveSymbolArray(symbolArray); + } + } + } + } + } + // 第一个符号是运算符 + else { + var operation = symbolArray.shift().value; + // 该符号是一元运算符 + if (operation == "sin" || operation == "sqrt" || operation == "cos" || operation == "tan" || operation == "lg" || operation == "ln" || operation == "√") { + // 运算符后面没有内容,报错 + if (symbolArray.length == 0) { + throw new Error("No number or operation after symbol " + operation); + } + if (operation == "sin") { + return Math.sin(resolveSymbolArray(symbolArray)); + } else if (operation == "sqrt") { + return Math.sqrt(resolveSymbolArray(symbolArray)); + } else if (operation == "cos") { + return Math.cos(resolveSymbolArray(symbolArray)); + } else if (operation == "tan") { + return Math.tan(resolveSymbolArray(symbolArray)); + } else if (operation == "lg") { + return Math.log10(resolveSymbolArray(symbolArray)); + } else if (operation == "ln") { + return Math.log(resolveSymbolArray(symbolArray)); + } else if (operation == "√") { + return Math.sqrt(resolveSymbolArray(symbolArray)); + } + } + // 该符号不是一元运算符,报错 + else { + throw new Error("The first operation number is missing around " + operation); + } + } +} + +// 转换数字 +function parseNumber(str) { + if (str == "e") { + return Math.E; + } + if (str == "π") { + return Math.PI; + } + var number = parseFloat(str); + if (/%$/.test(str)) { + number *= 0.01; + } + if (isNaN(number)) { + throw new Error("Cannot parse number " + str); + } + return number; +}