Menulis simulator pengetikan sentuh menggunakan JavaScript murni: Bagian 2

Halo Untuk mengantisipasi dimulainya kursus "Pengembang JavaScript Fullstack", salah satu penulis kami memutuskan untuk berbagi pengalamannya dalam membuat simulator untuk pengetikan sentuh. Tetapi kami, pada gilirannya, ingin menunjukkan materi ini kepada Anda dan hari ini membagikan bagian terakhirnya.




Bagian pertama bisa dibaca di sini.

Halo semuanya! Kami terus menulis simulator pengetikan asli menggunakan JavaScript asli. Pada bagian terakhir, kami membuat logika utama aplikasi, di mana, dengan menekan tombol Enter, level pertama dari dua puluh karakter dimuat, yang ketika mempelajari metode pencetakan sentuh adalah salah satu yang pertama kali dipelajari (J, K, F, D). Versi kerja dari versi pertama dapat dilihat di sini . Namun, kami masih memiliki beberapa tugas untuk meningkatkan aplikasi.



Mari kita merumuskan TOR untuk memahami apa yang ingin kita capai:

“Di awal permainan, setelah pengguna menekan Enter yang mengundang, yang bergerak secara animasi dari kiri ke kanan, level pertama hingga ketiga dimuat secara berurutan. Dengan menekan tombol yang benar, sinyal permainan positif dimainkan, jika terjadi kesalahan, sinyal permainan negatif dimainkan. Level pertama dan kedua memiliki simbol biasa, level ketiga adalah bonus, di mana simbol khusus untuk programmer (bukan komik). Di akhir level, kami memiliki jendela hasil yang ditulis dan diambil dari localStorage , jendela ini menampilkan jumlah kesalahan pemain dan waktu akhir sesi permainan. Dalam kasus kesalahan terlalu banyak, suara kerugian dimainkan, dalam kasus transisi yang sukses ke tingkat berikutnya - melodi utama. "

Oke, sekarang kita punya TK, mari kita mulai pemrograman. Jelas, basis kode kita akan meningkat secara signifikan, yang berarti bahwa untuk mengatasi banyak kode dan mengimpor pustaka yang diperlukan, kita perlu paket webpack . Tetapi mari kita mulai dengan tata letak dan melihat perubahan apa yang telah terjadi. Saya tidak akan memberikan isi head , karena satu-satunya hal yang telah berubah di sana adalah bahwa sekarang kita menarik javascript dari dist/code.js diproses oleh dist/code.js , tetapi yang lainnya tetap dist/code.js .

Di badan halaman, saya menambahkan jendela modal yang belum terlihat dan di mana kami akan menulis hasil melewati permainan dalam table :

 <body class="has-background-black-bis"> <div class="modal"> <div class="modal-background has-background-link"></div> <div class="modal-content has-background-white"> <h3 class="is-size-4">   </h3> <table class="table"> <thead> <tr> <th>  </th> <th>  </th> </tr> </thead> <tbody> <tr class="target_error"> <!--      --> </tr> </tbody> </table> <div> </div> <button class="modal-close is-large" aria-label="close"></button> </div> </div> 

Tentu saja, fakta bahwa kita akan memiliki jendela modal tepat di halaman menyebabkan rasa sakit fisik bagi pecinta SPA - kerangka kerja (setelah semua, mereka ingin menguraikan semua ini dengan rapi menjadi komponen). Tapi saya tidak ingin melukis generasi konten ini di JS. Mari kita lihat bagian selanjutnya dari html kami:

  <section class="hero is-primary is-large"> <div class="hero-head container"> <h1 class="label is-size-4 has-text-white promo">   3000</h1> <!--   --> <h3 class="label is-size-4 has-text-danger has-text-centered name-level"></h3> <div class="error-panel is-hidden"> <!--       --> <progress id="prog" class="progress is-danger" value="0" max="20"> </progress> </div> </div> <div class="hero-body has-background-black-bis main-board"> <div id="columns"> <h3 class="label is-size-2 has-text-white has-text-centered begin anim-elem">Press Enter to Start</h3> <div class="buttons columns is-half is-centered"> <!--        --> </div> </div> </div> </section> </body> 

Bahkan, hanya tiga hal yang telah berubah dari yang terakhir: sekarang ada nama untuk level yang akan kita isi dari js, panel kesalahan awalnya disembunyikan dan nama aplikasi itu sendiri telah berubah.

Baru saja akan memulai JS, saya hanya akan menambahkan sedikit CSS untuk memperbaiki sedikit dari apa yang tidak bisa saya lakukan dengan Bulma :

 body{ max-height:40vh !important; } .promo{ margin-top: 1rem; } 

Ok js. Saya menulis konfigurasi webpack paling sederhana. Dari apa yang ada di halaman pertama dokumentasi webpack , hanya berbeda dengan adanya pelacakan perubahan pada file. Untuk sebagian besar, saya membutuhkannya untuk menggunakan impor dalam file index.js utama, dan akhirnya memiliki file yang diperkecil:

 const path = require("path"); module.exports = { entry: './js/index.js', output: { filename: 'code.js', path: path.resolve(__dirname, 'dist'), }, watch: true, } 

Hebat, dan sekarang kita bisa beralih ke struktur js. Untuk animasi, saya memutuskan untuk menggunakan anime.js - walaupun saya mengerti betul bahwa jumlah animasi yang kita butuhkan dapat dilakukan dalam css dalam 10 baris. Mungkin di masa depan kami akan menambahkan lebih banyak animasi, jadi saya menyeret seluruh anime.es.js . Selain itu, saya menambahkan fungsi pembuatan file acak ke file terpisah - murni demi kenyamanan:

 export default function getRandomInt(max) { return Math.floor(Math.random() * Math.floor(max)); } 

Kemudian saya memutuskan untuk menempatkan fungsi showResult ke dalam file terpisah, yang terlibat dalam menggambar hasil setelah pengguna dapat melewati seluruh permainan. Ini adalah fungsi yang cukup universal yang mengambil argumen pertama ke input di mana informasi dari localStorage akan ditulis dan baru saja diterima, dan yang kedua - informasi yang perlu ditulis (dalam kasus kami, ini adalah jumlah kesalahan pemain). Selain itu, saya menerapkan kunci penyortiran dari localStorage , sehingga data dalam tabel diurutkan berdasarkan waktu mereka dibuat. Di bagian kedua fungsi, tabel menampilkan data upaya pemain sebelumnya. Namun, lebih sedikit kata, lebih banyak kode:

 export default function showResult(target_El, content){ localStorage.setItem(+new Date, content); (function drawOnLoad() { let temp_arr = []; for (let i = 0; i < localStorage.length; i++) { temp_arr.push(+localStorage.key(i)); } temp_arr.sort(); for(let i = 0; i< temp_arr.length; i++){ let item_time = new Date(temp_arr[i]); target_El.insertAdjacentHTML('afterend', `<th>${item_time.getDate()} / ${item_time.getMonth()} ${item_time.getHours()} : ${item_time.getMinutes()} </th> <th> ${localStorage.getItem(String(temp_arr[i]))}</th> `); } })(); } 

Hebat, dengan tambahan file-file tambahan, akhirnya kita bisa masuk ke file utama. Pertama kami membuat impor:

  import anime from "./anime.es"; import getRandomInt from "./random"; import showResult from "./showResult"; 

Perpustakaan diterima. Mari kita buat animasi dari prasasti yang kami undang sejak awal:

 anime({ targets: ".anim-elem", translateX: [-50, 50], easing: "linear", direction: "alternate", duration: 1000, loop: true }); 

Hebat! Akhirnya, aplikasi itu sendiri. Saya memutuskan untuk mengalihkan semua data ke json untuk menggambarkan server yang dapat dihasilkan game ini, misalnya, dalam bahasa Hungaria, Rusia, atau Cina yang disederhanakan - singkatnya, dengan serangkaian karakter apa pun (tetapi sebelum itu, tentu saja, tumbuh dan tumbuh). Setelah menerima data pada fetch , fungsi itu sendiri disebut asinkron, yang akan memulai permainan kami. Saya gist.github JSON ke gist.github (dapat dilihat di sini )

 function get_data() { fetch( // "    ,   json" ) //     json  -     , //  -       .then(res => res.json()) .then(data => { //    ,     read_data(data); }) .catch(err => { console.warn(" "); console.warn(err.name); //   }); } get_data(); //      , ...       

Seperti yang sudah dipahami pembaca, dalam fungsi read_data asinkron kami, semua fungsi lain dari gim akan ditemukan. Karenanya, sisa kode akan sudah ada di dalam fungsi ini:

 var number_of_level = 0; //        0 var error_sound = new Audio("sounds/error_sound.wav"); var fail_sound = new Audio("sounds/fail_sound.wav"); //    var press_sound = new Audio("sounds/press_sound.wav"); var succes_sound = new Audio("sounds/succes_sound.wav"); //       .         

Saya menemukan suara 8-bit di bank gratis di Internet. Sekarang mari kita lihat elemen yang saya dapatkan dari pohon DOM:
  let modal = document.querySelector(".modal"); //      var target_error = document.querySelector(".target_error"); //          let error_panel = document.querySelector(".error-panel"); //    let begin = document.querySelector(".begin"); //    ,     enter   .      let progress = document.getElementById("prog"); //     let buttons = document.querySelector(".buttons"); //         let name_level = document.querySelector(".name-level"); //       let modal_close = document.querySelector(".modal-close"); // ,                document.addEventListener("keydown", StartGame, { once: true // once           //     ,      }); 

Selanjutnya adalah fungsi start dari game. Beberapa perubahan juga menyalipnya - sekarang panel kesalahan kami menjadi terlihat hanya pada awal permainan, bunyi klik dimainkan, prasasti yang diundang dihapus, dan itu tidak bersembunyi (saya melakukannya untuk sedikit optimasi, karena dengan begitu kami akan memainkan animasi elemen yang tidak terlihat pada halaman) , dan kemudian fungsi permainan utama disebut:

  function StartGame(e) { if (e.key == "Enter") { error_panel.classList.remove("is-hidden"); //     press_sound.play(); begin.remove(); //    mainGame(); //    } } 

Ok, selanjutnya adalah gambar surat-surat. Semua perbedaan dari versi sebelumnya adalah bahwa kita sekarang mengambil data dari objek JSON:

  function drawBoard(info) { let str_arr = info.level_info[number_of_level].symbols; //    -     ,      webpack, .... name_level.innerHTML = info.level_info[number_of_level].name_level; let col_arr = info.symbol_colors; for (let i = 0; i < 20; i++) { //    20  let rand = getRandomInt(str_arr.length); buttons.insertAdjacentHTML( "afterbegin", `<button class='game-button button is-large ${col_arr[rand]}' id='${str_arr[rand]}'> ${str_arr[rand]}</button>` ); } } 

Selanjutnya, kami memanggil fungsi permainan utama dengan informasi dari JSON, yang fungsi asinkron kami masih ambil sebagai argumen:

  function mainGame() { drawBoard(information); document.addEventListener("keydown", press); 

Kemudian fungsi terakhir press dalam kode kita akan pergi, di mana kita menentukan kerugian dan menang. Jika pengguna mencetak terlalu banyak kesalahan, Anda harus memberi tahu dia bahwa dia kalah, dan memainkan melodi yang bertanggung jawab atas "kegagalan". Ujung level yang berhasil terjadi jika variabel count_right mendapatkan nilai yang sama dengan jumlah karakter yang kita hasilkan (20). Saya sadar bahwa transisi ke level berikutnya dapat dilakukan ketika panjang array elements_arr menjadi sama dengan 0, tetapi untuk saat ini kami memiliki solusi seperti itu. Jika pengguna telah berhasil menyelesaikan ketiga level, papan hasil ditampilkan dengan jumlah kesalahan:

  var errors_count = 0; var count_right = 0; function press(e) { let elements_arr = document.querySelectorAll(".game-button"); //      if (e.key == elements_arr[0].id) { //      querySelector,      elements_arr[0].remove(); count_right++; //    press_sound.play(); } else { errors_count++; //   error_sound.play(); progress.value = errors_count; //     if (errors_count > 20) { //         ,   fail_sound.play(); //    name_level.innerHTML = ' !'; setTimeout(() => { window.location.reload(); //       }, 2500); } } if (count_right == 20) { count_right = 0; number_of_level++; if (number_of_level == 3) { //     3  modal.classList.add("is-active"); //     showResult(target_error, errors_count); modal_close.onclick = async function() { modal.classList.remove("is-active"); window.location.reload(); //,     ,    }; } succes_sound.play(); mainGame(); } } } //      ,    

Ini adalah bagian utama dari artikel kami. Jika Anda tertarik dengan kode sumber aplikasi, Anda dapat menemukannya di sini . Jika Anda ingin bermain dan melihat aplikasi kami beraksi, selamat datang di sini . Silakan tulis semua bug dan pertanyaan baik di komentar atau segera di repositori issues di github.

Ada banyak lagi yang dapat ditambahkan ke aplikasi - ini adalah beberapa animasi lucu dari surat, musik di latar belakang (atau Anda dapat memastikan bahwa ketika Anda mencetak, suara yang berbeda dibuat untuk memaksa pengguna untuk mencetak dalam ritme tertentu). Anda dapat menggulir huruf dan mode kecepatan - sehingga Anda dapat mencetak tepat waktu dan mencatat akurasi yang dicapai. Saya juga ingin melihat hitungan mundur di awal level sehingga pemain dapat memposisikan tangannya dengan benar pada keyboard dan bersiap-siap. Singkatnya, ada banyak ide (juga terjemahan yang sangat jelas dari aplikasi ini untuk Bereaksi atau Vue), dan saya berharap bahwa orang-orang Habrovsk juga akan menyarankan sesuatu. Terima kasih atas perhatian Anda!

Yah, kami sedang menunggu semua orang di kursus !

Source: https://habr.com/ru/post/id481020/


All Articles