はじめに
Vue.jsでアプリを作る課題が出たので、せっかくなので記録として残しておく。
計算機アプリって基本的なイメージだけど、いまいち参考になるものがなかったので。。。
他の方の少しでも参考になれば良いかなと思います。
内容
Vue.jsを使って計算機アプリケーションを作ることになりました。
計算機としての機能があれば問題なしです。
シンプルな計算機を作成していきます。
シンプルな計算機の機能
そもそも電卓の機能とは何かを考える。
- 四則演算ができる(足し算とか掛け算とかですね)今回は%は除きます。
- 入力と計算結果が画面に表示される
C
を押したら数字がキャンセルされるAC
を押したら全部消える.
を押すと小数を入力できて、計算できる
こんなところでしょうか。
(今回は%は省きます。電卓の%は60の後に%
のように入力すると0.6と60%の意味になります)
ちょっと調べてみたら電卓にはまだまだ知らない便利機能がたくさんあるようです。
そもそも電卓って最近買わなくなりましたよね。。。
それは置いておいてVue.jsでプロジェクトを作成していきます。
計算機アプリを作成する
それではVue CLIを使いながら計算機アプリを作成します。
Vue CLIはアプリケーション開発に必要なツールやライブラリを簡単に構築できるコマンドラインインターフェイスです。
Vue CLIでプロジェクトを作成する。
% vue create calculator
この画面が出ますが、今回はVuex
などは使わないのでDefault ([Vue 2] babel, eslint)
で作成していきます。
🎉 Successfully created project calculator.
👉 Get started with the following commands:
$ cd calculator
$ yarn serve
作成できました。
確認のためローカルサーバーを立ち上げます。
Vue.jsの画面が表示されました。
次にcomponents内に新しくCalculator.vue
を作成します。
次にApp.vue
を少し変更してCalculator.vueを呼び出せるようにします。
<template>
<div id="app">
<img alt="Vue logo" src="./assets/logo.png">
<calculator />
</div>
</template>
<script>
import Calculator from './components/Calculator.vue'
export default {
name: 'App',
components: {
Calculator
}
}
</script>
簡単に計算機画面を作る
まずは数字など、入力するボタンや表示画面を簡単に作成します。
今回はこんな感じのシンプルな計算機を作成します。
単純にやると寂しい感じなのでCSSフレームワークのBlumaとBluma Swatchを使います。
簡単にCDNで今回はやってみます。
public
下にあるindex.html
のheadタグにコピペしてください。
<!DOCTYPE html>
<html lang="">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@0.9.3/css/bulma.min.css">
<link rel="stylesheet" href="https://unpkg.com/bulmaswatch/lumen/bulmaswatch.min.css">
<title>
<%= htmlWebpackPlugin.options.title %>
</title>
</head>
その後に単純にbuttonタグで適当に作成して、
こんな感じのそれっぽいのが出来上がりました。
計算機能を作成する
それでは次に本体である計算機能を作ります。
<template>
<div class="calc">
<h1 class="title">calculator</h1>
<div class="result box" style="text-align: right">
{{showNum}}
</div>
<div class="clear">
<button @click="clearCurrent" class="button is-danger is-medium">
C
</button>
<button @click="clearAll" class="button is-danger is-medium">AC</button>
</div>
<div class="buttons columns">
<div class="number-buttons colum is-one-third">
<button
v-for="num in buttons.num"
:key="num"
class="button is-dark"
@click="selectNumber(num)"
>
{{ num }}
</button>
</div>
<div class="column">
<button
v-for="operator in buttons.operator"
:key="operator"
class="button is-primary is-medium"
@click="selectOparator(operator)"
>
{{ operator }}
</button>
</div>
</div>
<div class="">
<button
@click="showResult"
class="button is-success is-medium equal-button"
>
=
</button>
</div>
</div>
</template>
<script src="https://cdn.jsdelivr.net/npm/decimal.js-light@2.5.0/decimal.min.js"></script>
<script>
export default {
data() {
return {
buttons: {
num: [
"7",
"8",
"9",
"4",
"5",
"6",
"1",
"2",
"3",
"0",
"00",
".",
"+/-"
],
operator: ["+", "-", "×", "÷"]
},
isInsertNumber: true, // 数字入力中かどうかを判断
isResult: false, // =を押した後かどうかを判断
currentOperator: "", // 現在選択中の演算子
currentNumber: "0", // 現在選択中の数字
numArray: [], // 押した数字の配列を格納する
opeArray: [] // 押した演算子の配列を格納する
};
},
computed: {
// 状態によって 合計値 or 入力値 を出し分ける
showNum() {
let result;
// 数字が入力中なら次の入力値を出力して
if (this.isInsertNumber) {
let text = String(this.currentNumber);
let newText = text.replace(/^0{1,}([^.])/, "$1");
result = newText;
} else {
// そうでないならtotalメソッドの結果を返す
result = this.total();
}
return result;
}
},
methods: {
// reduceメソッドを使い、全ての計算結果を出す
total() {
return this.numArray.reduce((prev, next, index) => {
return this.setTotalNum(prev, next, index - 1);
});
},
// 前後の数字を演算子ごとに計算して値を返す
// decimal.js-lightを使用して誤差によるエラーを回避
setTotalNum(prev, next, index) {
if (this.opeArray[index] === "+") {
let plus = new Decimal(prev).plus(next);
return plus
} else if (this.opeArray[index] === "-") {
let minus = new Decimal(prev).minus(next)
return minus
} else if (this.opeArray[index] === "÷") {
let division = new Decimal(prev).div(next)
return division
} else if (this.opeArray[index] === "×") {
let multiplication = new Decimal(prev).times(next)
return multiplication;
}
},
// 数字ボタンを押した時
selectNumber(num) {
// 正負を逆にする
if (num === "+/-") {
if (!this.isInsertNumber) return;
this.currentNumber = -1 * this.currentNumber;
return;
}
// = を押した直後、一度数字をリセットして次の計算をする
if (this.isResult) {
this.currentNumber = "0";
this.isResult = false;
}
// 数字以外が入力された場合は演算子の配列に今の演算子をpushして数字0にし、次の数字を入力できるようにする
if (!this.isInsertNumber) {
this.opeArray.push(this.currentOperator);
this.currentNumber = "0";
this.isInsertNumber = true;
}
// 小数点は1つまでしか入力できない
if (num === "." && this.currentNumber.indexOf(".") > -1) {
return;
}
this.currentNumber += num;
},
// 演算子ボタンを押した時
selectOparator(operator) {
if (this.isInsertNumber) {
this.numArray.push(Number(this.currentNumber));
this.isInsertNumber = false;
}
this.currentOperator = operator;
},
// =を押した時に動くメソッド
showResult() {
// 数字入力時 かつ current演算子有り かつ まだ=ボタンを押していないとき
// 入力された数字、演算子をリセットし、currentNumberにtotalメソッドで計算結果を返す
if (this.isInsertNumber && this.currentOperator && !this.isResult) {
this.numArray.push(Number(this.currentNumber));
this.currentOperator = "";
this.currentNumber = this.total();
this.numArray = [];
this.opeArray = [];
this.isResult = true;
}
},
// cを押した時
clearCurrent() {
this.currentNumber = 0;
this.isInsertNumber = true;
},
// acを押した時
clearAll() {
this.numArray = [];
this.opeArray = [];
this.currentNumber = 0;
this.currentOperator = "";
this.isInsertNumber = true;
}
}
};
</script>
**javascriptは小数点の計算で誤差が出るみたいなので、ライブラリを入れて防止します。 たまに計算を試していると、数字じゃなくて`3.5e-8`みたいな表示が出ることがあるんですが 改善方法わかる方いたら教えていただけたらありがたいです。**
CSSで見た目を整えていきます。
<style>
.calc {
width: 380px;
padding: 20px 0;
margin: 10px auto;
background-color: rgba(122, 196, 199, 0.808);
border-radius: 20px;
box-shadow: 8px 5px rgba(62, 150, 145, 0.6);
}
.title {
font-size: 20px;
color: rgb(235, 227, 128);
text-shadow: 1px 1px rgb(105, 95, 95);
}
.result {
width: 250px;
font-size: 30px;
font-weight: bold;
margin: 0 auto;
padding: 8px 10px;
border: 1px solid;
background-color: rgb(248, 165, 165);
color: rgb(231, 220, 220);
/* 数字が表示できない桁数になったら...で表示 */
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.clear {
width: 300px;
margin: 10px auto;
display: flex;
}
.clear button {
margin: 0 5px;
}
.buttons {
width: 300px;
margin: 0 auto 0;
}
.number-buttons button {
width: 30%;
margin: 0 0 5px;
font-size: 15px;
}
.op-buttons button {
width: 22%;
}
.equal-button {
margin: 0 auto 0;
width: 260px;
}
p {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
</style>
index.htmlにはCDNでdecimal.js-lightというjavascriptのライブラリを読み込んでいます。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@0.9.3/css/bulma.min.css">
<link rel="stylesheet" href="https://unpkg.com/bulmaswatch/lumen/bulmaswatch.min.css">
<script src="https://cdn.jsdelivr.net/npm/decimal.js-light@2.5.0/decimal.min.js"></script>
<title>
<%= htmlWebpackPlugin.options.title %>
</title>
</head>
**実行してみましょう。**
% yarn run serve
yarn run v1.22.10
warning ../../package.json: No license field
$ vue-cli-service serve
INFO Starting development server...
98% after emitting CopyPlugin
DONE Compiled successfully in 4472ms 9:56:59
App running at:
- Local: http://localhost:8080/
- Network: http://10.2.105.137:8080/
Note that the development build is not optimized.
To create a production build, run yarn build.
完成
せっかくなのでHerokuでデプロイしてみました
実際に動かしてみたい方はこちらからどうぞ
↓
Vue.jsでつくった電卓アプリ 「Calculator」
おわりに
いろいろと調べながらコードを読み込みながらどうにかできました。
細かい説明はしていないところと、まだまだ改善できるところはあるので随時気づいたら更新していきます。