Selamat siang teman!
Modul ES6 yang menggunakan sintaks "impor / ekspor" adalah alat yang cukup kuat dan pesaing yang layak untuk komponen kerangka kerja populer.
Izinkan saya menunjukkan ini dengan menggambar berbagai bentuk di atas kanvas.
Terinspirasi oleh bagian Panduan JavaScript dari MDN ini.
Inilah fungsionalitas yang akan diimplementasikan dalam aplikasi kecil kami:
- pembuatan otomatis kanvas dengan dimensi tertentu dan renderingnya di laman
- kemampuan menggambar kotak, lingkaran, dan segitiga dengan ukuran dan warna tertentu di kanvas
- membagi kode menjadi modul yang berisi bagian logis dari aplikasi
Dalam proses pembuatan aplikasi, kami akan memberikan perhatian khusus pada default dan bernama export / import, serta impor statis dan dinamis. Sebagian besar aplikasi akan ditulis menggunakan sintaks kelas.
Sangat diharapkan bahwa Anda memiliki setidaknya pemahaman dasar tentang bekerja dengan kelas dan kanvas.
Kode proyek ada di sini .
Demo aplikasi dapat dilihat di sini .
Mari kita mulai dengan dukungan.
Secara keseluruhan lumayan bagus. Rata-rata, sekitar 93%.
Struktur proyek akan menjadi sebagai berikut (Anda dapat membuat semua file sekaligus atau membuatnya sesuai kebutuhan):
modules
helpers
convert.js
shapes
circle.js
square.js
triangle.js
canvas.js
index.html
main.js
main.css
Markupnya terlihat seperti ini:
<div>
<section>
<h3>Circle</h3>
<label>
X:
<input type="number" value="75" data-prop="x" />
</label>
<label>
Y:
<input type="number" value="75" data-prop="y" />
</label>
<label>
Radius:
<input type="number" value="50" data-prop="radius" />
</label>
<label>
Color:
<input type="color" value="#ff0000" data-prop="color" />
</label>
<button data-btn="circle" class="draw_btn">Draw</button>
</section>
<section>
<h3>Square</h3>
<label>
X:
<input type="number" value="275" data-prop="x" />
</label>
<label>
Y:
<input type="number" value="175" data-prop="y" />
</label>
<label>
Length:
<input type="number" value="100" data-prop="length" />
</label>
<label>
Color:
<input type="color" value="#00ff00" data-prop="color" />
</label>
<button data-btn="square" class="draw_btn">Draw</button>
</section>
<section>
<h3>Triangle</h3>
<label>
X:
<input type="number" value="150" data-prop="x" />
</label>
<label>
Y:
<input type="number" value="100" data-prop="y" />
</label>
<label>
Length:
<input type="number" value="125" data-prop="length" />
</label>
<label>
Color:
<input type="color" value="#0000ff" data-prop="color" />
</label>
<button data-btn="triangle" class="draw_btn">Draw</button>
</section>
</div>
<button>Clear Canvas</button>
<script src="main.js" type="module"></script>
Apa yang harus Anda perhatikan di sini?
Untuk setiap bentuk, bagian terpisah dibuat dengan bidang untuk memasukkan data yang diperlukan dan tombol untuk memulai proses menggambar bentuk di kanvas. Pada contoh bagian lingkaran, data tersebut adalah: koordinat awal, radius dan warna. Kami menyetel bidang masukan ke nilai awal untuk mengaktifkan pengujian cepat kesehatan aplikasi. Atribut "data-prop" dirancang untuk mendapatkan nilai bidang untuk input dalam skrip. Atribut "data-btn" digunakan untuk menentukan tombol mana yang ditekan. Tombol terakhir digunakan untuk membersihkan kanvas.
Perhatikan bagaimana skrip terhubung. Atribut "type" dengan nilai "module" diperlukan. Atribut "defer" tidak diperlukan dalam kasus ini, karena pemuatan modul ditangguhkan (yaitu setelah halaman dimuat penuh) secara default. Perhatikan juga bahwa kami hanya menyertakan file "main.js" di halaman. File lain digunakan di dalam "main.js" sebagai modul.
Salah satu fitur utama modul adalah bahwa setiap modul memiliki ruang lingkup (konteks) sendiri, termasuk "main.js". Di satu sisi, ini bagus karena menghindari polusi pada namespace global dan, oleh karena itu, mencegah konflik antara variabel dan fungsi dengan nama yang sama. Di sisi lain, jika modul berbeda perlu mengakses elemen DOM yang sama, misalnya, Anda harus membuat skrip terpisah dengan variabel global dan menghubungkannya ke halaman sebelum modul utama, atau secara eksplisit membuat variabel global (window.variable = value), atau membuat variabel yang sama dalam setiap modul, atau bertukar variabel antar modul (yang sebenarnya akan kami lakukan).
Ada juga pendekatan keempat: mengakses elemen DOM dengan ID secara langsung. Tahukah Anda tentang kemungkinan ini? Misalnya, jika kita memiliki elemen dengan pengenal "main" di markup kita, kita dapat merujuknya sebagai main (main.innerHTML = "<p> Beberapa Konten Luar Biasa <p />") tanpa terlebih dahulu menentukan (mencari) elemen dengan menggunakan "document.getElementById ()" atau metode serupa. Namun, pendekatan ini tidak standar dan tidak disarankan untuk digunakan, karena tidak diketahui apakah akan didukung di masa mendatang, meskipun saya pribadi merasa peluang ini sangat nyaman.
Fitur lain dari modul statis adalah bahwa mereka hanya dapat diimpor sekali. Impor ulang akan diabaikan.
Terakhir, fitur ketiga dari modul adalah kode modul tidak dapat diubah setelah diimpor. Dengan kata lain, variabel dan fungsi yang dideklarasikan dalam modul hanya dapat diubah dalam modul ini, jika diimpor, hal ini tidak dapat dilakukan. Ini agak mengingatkan pada pola desain Modul, diimplementasikan menggunakan objek yang berisi variabel dan fungsi privat, atau menggunakan kelas dengan bidang dan metode privat.
Bergerak. Mari tambahkan beberapa gaya minimal:
body {
max-width: 768px;
margin: 0 auto;
color: #222;
text-align: center;
}
canvas {
display: block;
margin: 1rem auto;
border: 1px dashed #222;
border-radius: 4px;
}
div {
display: grid;
grid-template-columns: repeat(3, 1fr);
}
section {
padding: 1rem;
}
label {
display: block;
}
input {
margin: 0.25rem 0;
}
input:not([type="color"]) {
width: 50px;
}
button {
margin: 0.25rem auto;
cursor: pointer;
}
ul {
list-style: none;
}
li {
margin: 0.5rem auto;
width: 320px;
border-bottom: 1px dotted #222;
}
p {
margin: 0.25rem 0;
}
Tidak ada yang istimewa di sini. Anda bisa menambahkan keindahan sesuai keinginan Anda.
Mari beralih ke modul.
File "canvas.js" berisi kode kelas untuk membuat dan merender kanvas, serta daftar untuk pesan yang ditampilkan saat membuat bentuk tertentu (pesan ini mewakili informasi tentang area dan keliling (secara konvensional) bentuk):
// export default
// , IIFE ( , )
// , .. class ClassName..., export default ClassName
export default class Canvas {
// : ,
constructor(parent, width, height) {
this.parent = parent;
this.width = width;
this.height = height;
//
this.ctx = null;
//
this.listEl = null;
// ,
this.clearCanvas = this.clearCanvas.bind(this);
}
//
createCanvas() {
//
// ,
//
if (this.ctx !== null) {
console.log("Canvas already created!");
return;
} else {
// "canvas"
const canvasEl = document.createElement("canvas");
//
//
canvasEl.setAttribute("width", this.width);
canvasEl.setAttribute("height", this.height);
//
this.parent.append(canvasEl);
//
this.ctx = canvasEl.getContext("2d");
}
//
return this;
}
//
//
createReportList() {
if (this.listEl !== null) {
console.log("Report list already created!");
return;
} else {
const listEl = document.createElement("ul");
this.parent.append(listEl);
this.listEl = listEl;
}
return this;
}
//
clearCanvas() {
this.ctx.clearRect(0, 0, this.width, this.height);
this.listEl.innerHTML = "";
}
}
File "convert.js" berisi fungsi untuk mengubah derajat menjadi radian:
//
export const convert = (degrees) => (degrees * Math.PI) / 180;
Setiap file dalam direktori bentuk adalah modul untuk bentuk tertentu. Secara umum, kode modul-modul ini identik, kecuali metode menggambar, serta rumus untuk menghitung luas dan keliling suatu bangun datar. Pertimbangkan modul yang berisi kode untuk menggambar lingkaran (circle.js):
//
//
// - "Module"
import { convert } from "../helpers/convert.js";
//
//
export class Circle {
// ""
//
// , "" ctx listEl
constructor({ ctx, listEl, radius, x, y, color }) {
this.ctx = ctx;
this.listEl = listEl;
this.radius = radius;
this.x = x;
this.y = y;
this.color = color;
//
this.name = "Circle";
//
this.listItemEl = document.createElement("li");
}
//
draw() {
//
this.ctx.fillStyle = this.color;
//
this.ctx.beginPath();
// arc 6 :
// "x", "y", , ,
// ( "0, 2 * Math.PI")
// , :
this.ctx.arc(this.x, this.y, this.radius, convert(0), convert(360));
//
this.ctx.fill();
}
//
report() {
//
this.listItemEl.innerHTML = `<p>${this.name} area is ${Math.round(Math.PI * (this.radius * this.radius))}px squared.</p>`;
//
this.listItemEl.innerHTML += `<p>${this.name} circumference is ${Math.round(2 * Math.PI * this.radius)}px.</p>`;
this.listEl.append(this.listItemEl);
}
}
Terakhir, dalam file "main.js", impor default statis dari kelas modul "Canvas" dilakukan, instance kelas ini dibuat dan penekanan tombol ditangani, yang terdiri dari mengimpor modul kelas gambar yang sesuai secara dinamis dan memanggil metodenya:
//
//
import Canvas from "./modules/canvas.js";
// ,
// :
// ,
const { ctx, listEl, clearCanvas } = new Canvas(document.body, 400, 300).createCanvas().createReportList();
// ""
// ,
// "async"
document.addEventListener("click", async (e) => {
//
if (e.target.tagName !== "BUTTON") return;
//
if (e.target.className === "draw_btn") {
//
// ,
//
const { btn: btnName } = e.target.dataset;
//
// -
const shapeName = `${btnName[0].toUpperCase()}${btnName.slice(1)}`;
//
const shapeParams = {};
//
const inputsEl = e.target.parentElement.querySelectorAll("input");
//
inputsEl.forEach((input) => {
//
//
const { prop } = input.dataset;
//
// , ,
const value = !isNaN(input.value) ? input.valueAsNumber : input.value;
//
shapeParams[prop] = value;
});
//
shapeParams.ctx = ctx;
shapeParams.listEl = listEl;
console.log(shapeParams);
//
// "Module"
const ShapeModule = await import(`./modules/shapes/${btnName}.js`);
// -
//
// "Module" ( )
const shape = new ShapeModule[shapeName](shapeParams);
//
shape.draw();
//
shape.report();
} else {
//
// "Canvas"
clearCanvas();
}
});
Anda bisa bermain dengan kode di sini .
Seperti yang Anda lihat, modul ES6 memberikan kemungkinan yang cukup menarik terkait dengan pembagian kode ke dalam blok yang relatif mandiri yang berisi bagian logis dari aplikasi yang dapat dimuat secara langsung atau sesuai permintaan. Saat digunakan bersama-sama dengan literal template, ini adalah alternatif yang baik untuk komponen kerangka kerja populer. Maksud saya, pertama-tama, menampilkan halaman di sisi klien. Selain itu, pendekatan ini memungkinkan Anda untuk merender ulang hanya elemen DOM yang telah mengalami perubahan, yang pada gilirannya memungkinkan Anda melakukannya tanpa DOM virtual. Tetapi lebih dari itu di salah satu artikel berikut.
Saya harap Anda menemukan sesuatu yang menarik untuk diri Anda sendiri. Terima kasih atas perhatian Anda.