ng实现计算器-绑定键盘

本文大纲
  1. 1. 样式:calculator.component.css
  2. 2. 模板:calculator.component.html
  3. 3. TS:calculator.component.ts
  4. 4. TS:calc.ts
  5. 5. 使用:

在此记录一个简单的计算器实现 github action try12。

样式:calculator.component.css

代码折叠 代码展开
1
2
3
4
5
6
7
8
9
10
11
12
13
14
.calc{ position: fixed; right: 0; bottom: 0; z-index: 999; }
.chacha{ cursor: pointer; color:#fff; position:absolute;text-decoration:none; font-weight: 600;right:0;top:-17px;text-decoration:none;font-weight: 600;background: #555;padding: 0 2px; }
.calc td { user-select: none; cursor: pointer;border-bottom:1px solid #fff; background-color:#303133; width: 70px; height: 44px; color: white; text-align: center; font-size: 20px; padding:0!important; }
.calc td ~ td{border-left:1px solid #fff;}

.calc .td_orange { background-color: #FA6F14; }
.calc .td_content_calc{
font-weight:normal;border-bottom:1px solid #fff;
height: 44px; color: white; text-align: center; font-size: 20px; padding:0!important;
}
.calc .td_content_calc:empty:before{ content: attr(placeholder); color:#8c8888; font-size:14px; }
.calc .td_content_calc:focus:before{ content:none; }
.calc td.active,.calc td:active{background: blue;color:red;font-weight:bold;}
.bsbio{background:#555;font-size:13px;font-weight:normal;height:30px;color:#eee;}

模板:calculator.component.html

代码折叠 代码展开
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
<div class="calc" tabindex="-1" [hidden]="!calcShow">
<span class="chacha" (click)="evClose(false)">×</span>
<table align="center" (click)="clickCalc($event)" >
<tr>
<th class="td_orange td_content_calc" colspan="5" placeholder="支持键盘操作" contenteditable="true">{{calcStatus}}</th>
</tr>
<tr>
<td [class.active]="viewActObj.if7" data-s="7">7</td>
<td [class.active]="viewActObj.if8" data-s="8">8</td>
<td [class.active]="viewActObj.if9" data-s="9">9</td>
<td [class.active]="viewActObj.ifA" data-s="+">+</td>
<td [class.active]="viewActObj.ifB" data-s="-">-</td>
</tr>
<tr>
<td [class.active]="viewActObj.if4" data-s="4">4</td>
<td [class.active]="viewActObj.if5" data-s="5">5</td>
<td [class.active]="viewActObj.if6" data-s="6">6</td>
<td [class.active]="viewActObj.ifC" data-s="*">×</td>
<td [class.active]="viewActObj.ifD" data-s="/">÷</td>
</tr>
<tr>
<td [class.active]="viewActObj.if1" data-s="1">1</td>
<td [class.active]="viewActObj.if2" data-s="2">2</td>
<td [class.active]="viewActObj.if3" data-s="3">3</td>
<td [class.active]="viewActObj.ifE" data-s="(">(</td>
<td [class.active]="viewActObj.ifF" data-s=")">)</td>
</tr>
<tr>
<td [class.active]="viewActObj.if0" data-s="0">0</td>
<td [class.active]="viewActObj.ifG" data-s=".">.</td>
<td [class.active]="viewActObj.ifH" data-s="Backspace">←</td>
<td [class.active]="viewActObj.ifI" data-s="Delete" class="td_orange">dels</td>
<td [class.active]="viewActObj.ifJ" data-s="Enter" class="td_orange">=</td>
</tr>
<tr>
<th class="bsbio" colspan="5">{{calcInfo}}</th>
</tr>
</table>
</div>

TS:calculator.component.ts

代码折叠 代码展开
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
import { Component, OnInit, HostListener, Input, Output, EventEmitter } from '@angular/core';
import { Calc } from './calc';

@Component({
selector: 'app-calculator',
templateUrl: './calculator.component.html',
styleUrls: ['./calculator.component.css']
})
export class CalculatorComponent implements OnInit {

// tslint:disable-next-line:no-input-rename
@Input('show') calcShow = false;
// tslint:disable-next-line:no-output-on-prefix
@Output() onClose: EventEmitter<boolean> = new EventEmitter();
calcStatus = '';
calcInfo = '';
timer: any;
calcc: Calc;
viewActObj = { if7: false, if8: false, if9: false, ifA: false, ifB: false, if4: false, if5: false, if6: false, ifC: false, ifD: false, if1: false, if2: false, if3: false, ifE: false, ifF: false, if0: false, ifG: false, ifH: false, ifI: false, ifJ: false };
keysMap = {
1: '1', 2: '2', 3: '3', 4: '4', 5: '5', 6: '6', 7: '7', 8: '8', 9: '9', 0: '0',
'+': 'A', '-': 'B', '*': 'C', '/': 'D',
'(': 'E', ')': 'F', '.': 'G', Backspace: 'H', Delete: 'I', '=': 'J', Enter: 'J'
};
keys = ['1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '(', ')', '+', '-', '*', '/', '.'];
keysAll = ['1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '(', ')', '+', '-', '*', '/', '.', 'Backspace', 'Delete', 'Enter'];
constructor() {
this.calcc = new Calc();
}

ngOnInit() { }

@HostListener('document:keyup', ['$event'])
writeInput(event: KeyboardEvent): void {
if (event.key === 'J' && event.shiftKey) {
this.evClose(this.calcShow = !this.calcShow);
return;
}
if (!this.calcShow) { return; }
if (this.keysAll.find((v: string) => v === event.key)) {
this.calcViewAct(event.key);
this.goCalc(event.key);
}
}
goCalc(eventName: string) {
if (this.keys.find((v: string) => v === eventName)) {
let wd = eventName;
if (wd === '*') { wd = '×'; }
if (wd === '/') { wd = '÷'; }
this.calcStatus += wd;
return;
}
switch (eventName) {
case 'Backspace': {
const len = this.calcStatus.length;
if (len >= 1) { this.calcStatus = this.calcStatus.substring(0, len - 1); }
break;
}
case 'Delete': this.calcStatus = ''; break;
case 'Enter' : {
if (this.calcStatus === '') { return; }
const pre = this.calcStatus;
this.calcStatus = '' + this.calcc.parse(pre);
this.calcInfo = pre + '=' + this.calcStatus;
break;
}
}
}
calcViewAct(str: string) {
clearTimeout(this.timer);
for (const item of Object.keys(this.viewActObj)) {
this.viewActObj[item] = false;
}
const key = `if${this.keysMap[str]}`;
this.viewActObj[key] = true;
this.timer = setTimeout(() => this.viewActObj[key] = false, 80);
}
clickCalc(event: any) {
if (event.target.tagName.toLowerCase() === 'td') {
const str = event.target.getAttribute('data-s');
this.goCalc(str);
}
}
evClose(blean: boolean) {
this.onClose.emit(this.calcShow = blean);
}

}

TS:calc.ts

代码折叠 代码展开
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
export class Calc {
parse(content: string) {
let index = content.lastIndexOf('(');
if (index > -1) {
const endIndex = content.indexOf(')', index);
if (endIndex > -1) {
const result = this.parse(content.substring(index + 1, endIndex));
return this.parse(content.substring(0, index) + ('' + result) + content.substring(endIndex + 1));
}
}
let x: string | number;
let y: string | number;
index = content.indexOf('+');
if (index > -1) {
x = content.substring(0, index);
y = content.substring(index + 1);
if (typeof x !== 'number') { x = this.parse(x); }
if (typeof y !== 'number') { y = this.parse(y); }
return this.floatAdd(x as number, y as number);
}
index = content.lastIndexOf('-');
if (index > -1) {
x = content.substring(0, index);
y = content.substring(index + 1);
if (typeof x !== 'number') { x = this.parse(x); }
if (typeof y !== 'number') { y = this.parse(y); }
return this.floatSub(x as number, y as number);
}
index = content.lastIndexOf('×');
if (index > -1) {
x = content.substring(0, index);
y = content.substring(index + 1);
if (typeof x !== 'number') { x = this.parse(x); }
if (typeof y !== 'number') { y = this.parse(y); }
return this.floatMul(x as number, y as number);
}
index = content.lastIndexOf('÷');
if (index > -1) {
x = content.substring(0, index);
y = content.substring(index + 1);
if (typeof x !== 'number') { x = this.parse(x); }
if (typeof y !== 'number') { y = this.parse(y); }
return this.floatDiv(x as number, y as number);
}
if ('' === content) {
return 0;
} else {
return Number(content);
}
}
floatAdd(arg1: number, arg2: number) {
let r1: number;
let r2: number;
try {
r1 = arg1.toString().split('.')[1].length;
} catch (e) {
r1 = 0;
}
try {
r2 = arg2.toString().split('.')[1].length;
} catch (e) {
r2 = 0;
}
const m = Math.pow(10, Math.max(r1, r2));
return (arg1 * m + arg2 * m) / m;
}
floatSub(arg1: number, arg2: number) {
let r1: number;
let r2: number;
try {
r1 = arg1.toString().split('.')[1].length;
} catch (e) {
r1 = 0;
}
try {
r2 = arg2.toString().split('.')[1].length;
} catch (e) {
r2 = 0;
}
const m = Math.pow(10, Math.max(r1, r2));
const n = (r1 >= r2) ? r1 : r2;
return ((arg1 * m - arg2 * m) / m).toFixed(n);
}
floatMul(arg1: number, arg2: number) {
let m = 0;
const s1 = arg1.toString();
const s2 = arg2.toString();
try {
m += s1.split('.')[1].length;
} catch (e) { }
try {
m += s2.split('.')[1].length;
} catch (e) { }
return Number(s1.replace('.', '')) * Number(s2.replace('.', '')) / Math.pow(10, m)
}
floatDiv(arg1: number, arg2: number) {
let t1 = 0;
let t2 = 0;
try {
t1 = arg1.toString().split('.')[1].length;
} catch (e) { }
try {
t2 = arg2.toString().split('.')[1].length;
} catch (e) { }
const r1 = Number(arg1.toString().replace('.', ''));
const r2 = Number(arg2.toString().replace('.', ''));
const v = (r1 / r2) * Math.pow(10, t2 - t1);
return Number(v.toFixed(4));
}
}

使用:

1
<app-calculator [show]="calcShow" (onClose)="onCalcClose($event)"></app-calculator>
1
2
calcShow: boolean = true;
onCalcClose(show: boolean) { this.calcShow = show; }