If you're seeing this message, it means we're having trouble loading external resources on our website.

Jeżeli jesteś za filtrem sieci web, prosimy, upewnij się, że domeny *.kastatic.org i *.kasandbox.org są odblokowane.

Główna zawartość

Typ obiektu przycisku

Jednym z najlepszych sposobów, aby kod był wielokrotnego użytku i wydajny jest programowanie zorientowane obiektowo, zwłaszcza dla elementów UI, takich jak przyciski. W programowaniu zorientowanym obiektowo, myślimy o naszym programie w kategoriach abstrakcyjnych obiektów o szczególnych zachowaniach. Potem tworzymy instancje tych obiektów z konkretnymi parametrami. Jeśli nie pamiętasz, jak zrobić to w JavaScripcie, przypomnij sobie tutaj.
Aby użyć PZO do tworzenia przycisków, trzeba zdefiniować klasę Button, a następnie dodać metody takie jak rysowanie i obsługa kliknięcia myszy. Powinniśmy być w stanie napisać kod taki jak ten:
var btn1 = new Button(...);
btn1.draw();

mouseClicked = function() {
  if (btn1.isMouseInside()) {
     println("Whoah, kliknąłeś we mnie!");
  }
}
Porównajmy kod z tym, który pisaliśmy w ostatnim rozdziale:
var btn1 = {...};
drawButton(btn1);

mouseClicked = function() {
if (isMouseInside(btn1)) {
println("Whoah, kliknąłeś we mnie!");
  }
}
Bardzo podobne, co nie? Ale jest duża różnica -- wszystkie funkcje są definiowane w klasie Button. faktycznie należą do przycisków. Jest mocniejsza więź pomiędzy właściwościami i zachowaniem, a to prowadzi do schludniejszego i bardziej przenośnego kodu.
Aby zdefiniować klasę Button musimy zacząć od konstruktora: specjalnej funkcji, która konfiguruje parametry i ustala wartości początkowe klasy.
Na początek, konstruktor który przyjmuje zmienne x, y, szerokość i wysokość:
var Button = function(x, y, width, height) {
    this.x = x;
    this.y = y;
    this.width = width;
    this.height = height;
};

var btn1 = new Button(100, 100, 150, 150);
To na pewno działa, ale mam inne podejście, które chciałbym polecić. Zamiast przyjmować poszczególne parametry, konstruktor może przyjmować obiekt konfiguracji.
var Button = function(config) {
this.x = config.x;
this.y = config.y;
this.width = config.width;
this.height = config.height;
this.label = config.label;
};
Zaletą obiektu konfiguracji jest to, że możemy dodawać więcej parametrów dla konstruktora do obsłużenia (jak label) i jest to nadal łatwe dla nas do zrozumienia, co robi każdy parametru, podczas budowy przycisku:
var btn1 = new Button({
    x: 100, y: 100,
    width: 150, height: 50,
    label: "Kliknij, proszę!"});
Ale możemy pójść o krok dalej od tego. Co jeśli większość przycisków będzie miała samą szerokość i wysokość? Nie powinnyśmy podawać szerokości i wysokości dla każdego przycisku, powinniśmy je jedynie określać w razie potrzeby. Możemy zaprogramować konstruktor, aby sprawdzał, czy własność jest obecnie zdefiniowana, a jeśli nie podał wartość domyślną. Tak:
var Button = function(config) {
this.x = config.x || 0;
this.y = config.y || 0;
this.width = config.width || 150;
this.height = config.height || 50;
this.label = config.label || "Click";
};
Teraz możemy po prostu podać jedynie część zmiennych, ponieważ konstruktor uzupełni resztę wartościami domyślnymi:
var btn1 = new Button({x: 100, y: 100, label: "Kliknij,  proszę!"});
Cała ta praca na konstruktor? Ale warto, przysięgam.
Teraz, kiedy mamy opanowany konstruktor, zajmijmy się zachowaniem: metoda draw. Będzie to ten sam kod co drawButton, ale będzie pobierać dane z this, ponieważ jest zdefiniowana w prototypie klasy:
Button.prototype.draw = function() {
fill(0, 234, 255);
rect(this.x, this.y, this.width, this.height, 5);
fill(0, 0, 0);
textSize(19);
textAlign(LEFT, TOP);
text(this.label, this.x+10, this.y+this.height/4);
};
Kiedy to zdefiniujemy, możemy wywołać to w ten sposób:
btn1.draw();
Tutaj jest program który korzysta z klasy Button do stworzenia 2 przycisków - zwróć uwagę jak łatwo jest je stworzyć i rysować wiele przycisków:
Pominęliśmy jednak najtrudniejsze, czyli: obsługę kliknięć. Może zacznijmy od zdefiniowania funkcji w klasie Button, która będzie zwracała prawdę, jeśli użytkownik kliknie wewnątrz określonego pola przycisku. Po raz kolejny, jest to funkcja jak poprzednio, ale bierze wszystkie właściwości z this zamiast przekazanego obiektu:
Button.prototype.isMouseInside = function() {
return mouseX > this.x &&
mouseX < (this.x + this.width) &&
mouseY > this.y &&
mouseY < (this.y + this.height);
};
Teraz możemy użyć jej wewnątrz funkcji mouseClicked:
mouseClicked = function() {
if (btn1.isMouseInside()) {
println("Dobry wybór!");
} else if (btn2.isMouseInside()) {
println("Yay, wybrałeś mnie!");
}
};
Wypróbuj poniżej, klikając każdy z przycisków:
Ale jest coś co mnie irytuje w sposobie w jakim obsługujemy kliknięcia. Sensem programowania obiektowego jest zebranie wszystkich zachowań związanych z obiektem wewnątrz klasy i używanie właściwości, aby dostosować zachowanie. Ale zostawiliśmy część zachowania wystającą poza klasę, metodę println wewnątrz mouseClicked:
mouseClicked = function() {
if (btn1.isMouseInside()) {
println("Dobry wybór!");
} else if (btn2.isMouseInside()) {
println("Yay, wybrałeś mnie!");
}
};
Te instrukcje drukowania powinny być jakoś lepiej powiązane z przyciskiem, tak jak reszta którą podaliśmy w konstruktorze. Teraz, patrząc na to w ten sposób, możemy przenieść wiadomość do config konstruktora i zdefiniować metodę handleMouseClick do drukowania:
var Button = function(config) {
...
    this.message = config.message || "Naciśnięty!";
};

Button.prototype.handleMouseClick = function() {
if (this.isMouseInside()) {
println(this.message);
}
};

var btn1 = new Button({
x: 100,
y: 100,
label: "Please click!",
message: "Dobry wybór!"
});

mouseClicked = function() {
btn1.handleMouseClick();
};
Jest znacznie ładniej, od kiedy wszystko dotyczące zachowania konkretnego przycisku jest opakowane w konstruktorze. Ale jest to również zbyt proste. Co jeśli chcemy zrobić coś poza tym drukowaniem wiadomości, jak narysować kilka kształtów lub zmienić scenę, coś, co zajęłoby kilka linii kodu? W takim przypadku możemy chcieć dostarczać konstruktorowi więcej niż tylko napis -- rzeczywiście chcielibyśmy dostarczyć sporo kodu. Jak możemy dostarczyć sporo kodu?
... Funkcją! W JavaScript (ale nie we wszystkich językach) możemy przekazać funkcje jako parametry do funkcji. Jest to przydatne w wielu sytuacjach, ale szczególnie przydatne podczas definiowania zachowania interfejsu użytkownika kontroli jak przyciski. Możemy powiedzieć przyciskowi, "Hej, tutaj masz funkcje, jest w niej sporo kodu, który chce wywołać, kiedy użytkownik kliknie przycisk." Odnosimy się do tych funkcji jako "wywołania zwrotnego" (ang. callback function), ponieważ nie zostaną wywołane od razu, ale w odpowiednim czasie.
Możemy zacząć przekazując jako parametr onClick funkcje:
var btn1 = new Button({
x: 100,
y: 100,
label: "Please click!",
onClick: function() {
text("Dobry wybór!", 100, 300);
}
});
Następnie musimy upewnić się, że nasz konstruktor ustawia właściwość onClick prawidłowo do tego co przekazujemy. Domyślnie, w przypadku nieprzekazania onClick,wystarczy, że zdefiniujemy "pustą" funkcję, która nie będzie wykonywać "żadnych" operacji. Jest tam tylko po to, abyśmy mogli ją wywołać i nie wystąpił błąd:
var Button = function(config) {
// ...
this.onClick = config.onClick || function() {};
};
Wreszcie musimy rzeczywiście wywołać funkcję wywołania zwrotnego, kiedy użytkownik kliknie przycisk. Jest to dość proste - można ją wywołać pisząc nazwę właściwości w której funkcja jest zapisana i dopisując za nie puste nawiasy:
Button.prototype.handleMouseClick = function() {
if (this.isMouseInside()) {
this.onClick();
}
};
I już skończyliśmy - mamy klasę Button, dzięki której, możemy łatwo tworzyć nowe przyciski, każdy przycisk posiadający inny wygląd i inaczej odpowiadać na kliknięcia. Poklikaj na poniższym przykładzie i zobaczyć co się dzieje, kiedy zmienisz parametry przycisku:
Teraz możesz użyć tego kodu jako szablon, możesz dostosować przyciski w inny sposób, jak różne kolory, lub ich odpowiedzi na inne zdarzenia, takie jak najechanie myszy. Wypróbuj to w swoich programach!

Chcesz dołączyć do dyskusji?

Rozumiesz angielski? Kliknij tutaj, aby zobaczyć więcej dyskusji na angielskiej wersji strony Khan Academy.