Tạo app ảo thuật với Javascript

Hi anh em, hôm nay mình sẽ hướng dẫn mọi người làm một app ảo thuật nho nhỏ với những con số nhé. Đem cái này đi tán gái thì thì xịn luôn nha.

Cách chơi

  1. Đầu tiên bạn sẽ nhập một số 4 chữ số
  2. Mình sẽ ghi ngẫu nhiên một số vào tờ giấy và để sang một bên
  3. Tiếp theo bạn nhập ngẫu nhiên một số 4 chữ số
  4. Mình sẽ nhập ngẫu nhiên một số 4 chữ số phía dưới bạn
  5. Lặp lại bước 3, bạn nhập một số
  6. Lặp lại bước 4, mình nhập một số
  7. Bây giờ ta có tổng 5 số của mình và bạn vừa nhập lúc nãy. Và con số này lại trùng với con số mà mình đã ghi vào tờ giấy ở bước thứ 2 dù cho mình và bạn đều nhập ngẫu nhiên.

Các bạn có thể xem video dưới đây để hiểu rõ luật chơi nhé.

Giải thích quy luật

Bạn thấy vi diệu phải không? Thật ra thì không có gì là ngẫu nhiên cả, mọi thứ đã được tính toán và chỉ có bạn là ngẫu nhiên thôi.

Nếu bạn lấy số của bạn + số của mình phía dưới sẽ luôn bằng 9999. Nếu gọi x là số của bạn và y là số của mình thì ta có.

bash
x1 + x2 + y3 + x4 + y5 = sum
Tương đương
x1 + (x2 + y3) + (x4 + y5) = sum
Tương đương
x1 + 9999 + 9999 = sum
Tương đương
sum = x1 + 19998

Vậy mình chỉ cần lấy số đầu tiên của bạn cộng thêm cho 19998 là sẽ có được kết quả tính tổng.

Hướng dẫn code

Các bạn có thể tham khảo mã muồn tại link Github này

Mình đã chú thích trong code rồi nha.

index.html

html
<!DOCTYPE html>
<html lang="en">
<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" />
<title>Magic Number</title>
<link rel="stylesheet" href="style.css" />
</head>
<body>
<div class="container">
<h1 class="title-1">Magic Number</h1>
<div class="main">
<div class="main-control">
<div class="reset-area">
<button class="btn-reset">Reset</button>
</div>
<div class="result"></div>
<form>
<div class="form-group">
<input
type="text"
inputmode="numeric"
pattern="[0-9]*"
placeholder="..."
minlength="4"
maxlength="4"
required
/>
<button type="submit">OK</button>
</div>
</form>
</div>
<div class="main-info">
<h2 class="title-2">Dãy số</h2>
<div class="magic-numbers"></div>
</div>
</div>
<p class="description">Cách chơi:</p>
<p class="description">
Đầu tiên bạn hãy nhập một số có 4 chữ số bất kỳ. Mình sẽ viết một số dự đoán vào một khu vực khác (bạn có thể
click vào để xem trước).
</p>
<p class="description">
Tiếp theo bạn hãy nhập một số gồm 4 chữ số, mình sẽ nhập ngẫu nhiên 4 chữ số phía dưới số của bạn.
</p>
<p class="description">
Làm lại lần nữa, bạn nhập tiếp tục số 4 chữ số, mình sẽ nhập ngẫu nhiên 4 chữ số phía dưới số của bạn.
</p>
<p class="description">
Boom, chúng ta có tổng các dãy số của bạn và mình bằng với con số mà mình đã viết từ trước mặc dầu các con số
của bạn và mình đều ngẫu nhiên!
</p>
</div>
<script src="app.js"></script>
</body>
</html>

app.js

js
// Thứ tự nhập
let turn = 1
// Mảng chứa các số đã nhập
let numbers = []
// Function tạo element
// element này sẽ được chèn vào trong <div class="magic-numbers"></div>
const createMagicNumber = (className, role, value) => {
const node = document.createElement('div')
node.className = className
node.innerHTML = `
<span>${role}</span>
<span>${value}</span>
`
return node
}
const magicNumbersNode = document.querySelector('.magic-numbers')
const resultNode = document.querySelector('.result')
const formNode = document.querySelector('form')
// Lắng nghe sự kiện submit số
formNode.addEventListener('submit', event => {
event.preventDefault()
const value = document.querySelector('form input').value
// Tạo element của bạn
const yourNode = createMagicNumber('number number-you', 'Bạn', value)
// Reset ô input sau khi submit
document.querySelector('form input').value = ''
// Nếu là lần đầu tiên thì chỉ cần chèn 1 node là node của bạn
if (turn === 1) {
// Tính nhanh kết quả
// công thức phía dưới tương đương Number(value) + 19998
result = Number('2' + value) - 2
resultNode.innerHTML = `
<div class="result-overlay"></div>
<span>Dự đoán: ${result}</span>
`
resultNode.classList.add('active')
magicNumbersNode.appendChild(yourNode)
numbers.push(Number(value))
turn++
} else {
// Nếu là lần thứ 2 trở đi thì chèn 2 node vào
// là node của bạn và node của mình
const myNode = createMagicNumber('number number-me', 'Mình', 9999 - Number(value))
magicNumbersNode.appendChild(yourNode)
magicNumbersNode.appendChild(myNode)
numbers.push(Number(value), 9999 - Number(value))
turn += 2
}
// Nếu là lượt cuối thì sẽ chèn thêm element tính tổng
if (turn === 6) {
const sumValue = numbers.reduce((current, result) => result + current)
const sumNode = createMagicNumber('number number-sum', 'Tổng', sumValue)
magicNumbersNode.appendChild(sumNode)
// Ẩn cái ô input đi, không cho nhập nữa
formNode.style.display = 'none'
}
})
// Lắng nghe sự kiện nhấn button reset
document.querySelector('.btn-reset').addEventListener('click', () => {
magicNumbersNode.innerHTML = ''
resultNode.innerHTML = ''
resultNode.classList.remove('active')
turn = 1
numbers = []
formNode.style = null
})
// Lắng nghe sự kiện nhấn vào khu vực result.
// Chúng ta sẽ thực hiện làm mờ hoặc xóa làm mờ.
resultNode.addEventListener('click', () => {
document.querySelector('.result-overlay').classList.toggle('hide')
})

Cho thêm một chút css cho đẹp nhé

style.css

css
@import url('https://fonts.googleapis.com/css2?family=Roboto&display=swap');
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
html {
font-size: 10px;
min-height: 100%;
}
body {
background-color: #0093e9;
background-image: linear-gradient(160deg, #0093e9 0%, #80d0c7 100%);
color: #fff;
font-family: 'Roboto', sans-serif;
font-size: 1.9rem;
padding: 1.5rem;
}
.title-1 {
text-align: center;
font-size: 3rem;
margin-bottom: 1rem;
}
.title-2 {
text-align: center;
font-size: 2.5rem;
margin-bottom: 1rem;
}
.main {
display: grid;
grid-template-columns: 1fr 1fr;
grid-gap: 1rem;
margin-bottom: 2rem;
}
.container {
max-width: 400px;
margin: 0 auto;
}
.magic-numbers {
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
}
.number {
background: rgba(255, 255, 255, 0.25);
box-shadow: 0 8px 10px 0 rgba(31, 38, 135, 0.2);
backdrop-filter: blur(10px);
-webkit-backdrop-filter: blur(10px);
border-radius: 10px;
border: 1px solid rgba(255, 255, 255, 0.18);
margin-bottom: 1rem;
}
.number.number-me {
background: rgba(255, 255, 89, 0.25);
}
.number.number-sum {
background: rgba(250, 77, 77, 0.25);
}
.number span {
padding: 1rem;
display: inline-block;
}
.number span:first-child {
width: 70px;
}
form {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
.reset-area {
text-align: center;
margin-bottom: 1rem;
}
.reset-area .btn-reset {
padding: 1rem 1.5rem;
font-size: 1.9rem;
background-color: #fe5f55;
transition: 0.2s background-color;
cursor: pointer;
color: #fff;
border: 0;
border-radius: 3px;
}
.reset-area .btn-reset:hover {
background-color: #fe2f22;
}
.result {
display: none;
position: relative;
text-align: center;
padding: 1.5rem;
background: rgba(255, 255, 255, 0.05);
box-shadow: 0 20px 30px rgba(0, 0, 0, 0.1);
border-radius: 10px;
border: 1px solid rgba(255, 255, 255, 0.18);
margin: 2rem auto;
cursor: pointer;
}
.result.active {
display: block;
}
.result-overlay {
content: '';
position: absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
background: rgba(255, 255, 255, 0.05);
backdrop-filter: blur(3px);
-webkit-backdrop-filter: blur(3px);
}
.result-overlay.hide {
display: none;
}
.form-group {
position: relative;
border: 1px solid rgba(255, 255, 255, 0.18);
display: flex;
align-items: center;
height: 5rem;
background: #fff;
border-radius: 3px;
}
.form-group input {
border: 0;
height: 100%;
padding: 1rem;
font-size: 2rem;
width: 100px;
outline: none;
}
.form-group button {
font-size: 1.6rem;
padding: 1rem;
background: #fb5533;
color: #fff;
border: 0;
cursor: pointer;
border-radius: 3px;
margin-right: 2px;
}
.description {
margin-bottom: 1rem;
font-size: 1.4rem;
}

Test app

Mọi người có thể vào link này https://magic-number.vercel.app/ để test hoặc test trực tiếp trên blog của mình.

Một app ảo thuật đơn giản nhưng cũng khá thú vị, giúp chúng ta rèn luyện các thao tác DOM trên Javascript. Anh em cũng có thể đem cái này đi gây ấn tượng với crush cũng được ^^!, có kết quả nhớ feedback mình biết với nhé.