Kaleidoskop Zeichenanwendung p5.js
In dieser Übung lernst du, wie man eine Zeichenanwendung für Kaleidoskop ähnliche Muster in p5.js programmiert.
Editor öffnen
Um das Tutorial durchzuführen, öffnen wir den p5.js Editor über den folgenden Link: p5.js Web Editor (p5js.org).
function setup() {
createCanvas(400, 400);
}
function draw() {
background(220);
}
In der Funktion setup()
wird deine Zeichenfläche, ein sogenannter Canvas erzeugt. Diese Funktion wird nur einmal am Anfang ausgeführt.
Die Funktion draw()
wird wiederholt ausgeführt. Hier generiert p5.js automatisch einen grauen Hintergrund.
Für unsere Anwendung möchten wir eine größere Zeichenfläche und einen dunkelgrauen Hintergrund. Dafür werden die Werte folgendermaßen geändert.
function setup() {
createCanvas(450, 450);
}
function draw() {
background(30);
}
Erstellen einer Zeichenanwendung
Damit Linien zukünftig auf die Anwendung gezeichnet werden können, wird der Hintergrund nicht mehr in der draw()
, sondern in der setup()
Funktion initialisiert. Da wir im weiteren Verlauf mit den HSB Farbwerten arbeiten wollen, konfigurieren wir den Farbmodus mit colorMode(HSB, 360, 150, 100, 1)
.
Die Werte stehen für folgendes: HSB (Farbton, Sättigung, Helligkeit, Transparenz).
Um mit der Maus Linien zeichnen zu können, erstellen wir eine Linie line(mouseX, mouseY, pmouseX, pmouseY);
. Es zeichnet eine Linie von der vorherigen Mausposition (pmouseX, pmouseY)
zur aktuellen Mausposition (mouseX, mouseY)
.
Um die Linie sichtbar zu machen, weisen wir ihr eine Strichstärke und eine Farbe zu. Die Farbe ist hierbei im HSB Modus. Durch die Angabe der horizontalen Mausposition mouseX
beim Farbton, wird sich der Farbton je nach X-Position verändern.
function setup() {
createCanvas(450, 450);
background(30);
colorMode(HSB, 360, 150, 100, 1);
}
function draw() {
strokeWeight(15);
stroke(mouseX, 150, 100, 0.5);
line(mouseX, mouseY, pmouseX, pmouseY);
}
Folgendes sollte nun möglich sein:
Anpassungen zu einem Kaleidoskop
Um den Startpunkt des Koordinatensystems in die Mitte zu versetzen, verwenden wir translate(width/2, height/2)
in der draw()
Funktion.
Für den Kaleidoskop Effekt benötigen wir einen Winkel, an dem die Linien gespiegelt werden. Dafür wird zu aller erst eine Variable angle
erstellt. Diese befindet sich außerhalb der Funktionen, damit sie global gültig ist.
Durch den Wert 12 werden die Linien in einem 12 Grad Winkel gespiegelt. Im späteren Verlauf wird diese Variable veränderbar sein, sodass die Linien unterschiedlich oft gespiegelt werden können.
let angle = 12;
Um die Spiegelungen umsetzten zu können, erstellen wir eine for-Schleife in der draw()
Funktion. Innerhalb der for-Schleife wird das Koordinatensystem um den Winkel angle
rotiert, dann wird eine Linie gezeichnet, deren Farbe und Dicke festgelegt sind. Danach wird das Koordinatensystem gespiegelt und eine weitere Linie wird gezeichnet. Dieser Prozess wird angle
-mal wiederholt, wodurch eine Reihe von Linien entsteht, die sich um den Mittelpunkt der Leinwand drehen und dem Mauszeiger folgen.
for (let i = 0; i < angle; i++) {
rotate(angle);
strokeWeight(15);
stroke(mouseX, 150, 100, 0.5);
line(mouseX, mouseY, pmouseX, pmouseY);
push();
scale(1, -1);
line(mouseX, mouseY, pmouseX, pmouseY);
pop();
}
for (let i = 0; i < angle; i++) {
Diese Zeile beginnt eine for-Schleife, die von i = 0
bis i < angle
läuft. i
wird bei jedem Durchlauf um eins erhöht.
Mit rotate(angle);
wird das Koordinatensystem um den durch i
bestimmten Winkel rotiert.
push();
speichert den aktuellen Zustand des Transformationsstapels. Durch scale(1, -1);
wird die Zeichenrichtung gespiegelt, sodass die Linien nach unten gezogen werden. Zum Schluss stellt pop();
den vorherigen Zustand des Transformationsstapels wieder her.
Der Zwischenstand
let angle = 12;
function setup() {
createCanvas(450, 450);
background(30);
colorMode(HSB, 360, 150, 100, 1);
}
function draw() {
translate(width / 2, height / 2);
for (let i = 0; i < angle; i++) {
rotate(angle);
strokeWeight(15);
stroke(mouseX, 150, 100, 0.5);
line(mouseX, mouseY, pmouseX, pmouseY);
push();
scale(1, -1);
line(mouseX, mouseY, pmouseX, pmouseY);
pop();
}
}
Mit dem bestehenden Code lässt sich z.B. folgendes Muster erstellen:
Extra Einstellungen zur individuellen Anpassung des Kaleidoskops
Im nächsten Schritt werden nun 3 Slider hinzugefügt, um Sättigung, Strichstärke und den Winkel selbstständig anpassen zu können, sowie ein Button, welcher es ermöglicht eine zufällige Hintergrundfarbe zu generieren.
Dafür definieren wir zuerst ganz oben in unserem Code unsere Slider und Buttons, wo wir vorher schon den Winkel definiert haben.
let angle = 12;
let slider, slider2, slider3;
let colorButton;
let slider, slider2, slider3
sind Variablen für drei verschiedene Slider-Elemente, die in der Benutzeroberfläche erscheinen und jeweils die Parameter Sättigung, Strichstärke und Winkel steuern.
let colorButton
ist die Variable für den Button, der die Hintergrundfarbe ändert, wenn er geklickt wird.
Die Slider und ihre Beschriftungen (Sättigung, Strichstärke, Winkel) werden in eigenen Containern erstellt. Jeder Container verwendet das Flexbox-Layout display: flex
und orientiert sich vertikal mitflex-direction: column
.
Nun können wir damit beginnen, die Container für die einzelnen Slider und Buttons zu erstellen.
Erstellen des Containers für die Einstellungen
// Erstelle einen Container für die Einstellungen
let settingsContainer = createDiv('');
settingsContainer.style('display', 'grid');
settingsContainer.style('grid-template-rows', 'auto auto');
// Zwei Reihen: eine für Überschrift, eine für Slider
settingsContainer.style('grid-template-columns', '215px 215px');
settingsContainer.style('grid-gap', '10px');
settingsContainer.style('padding', '10px');
settingsContainer.style('margin-bottom', '10px');
settingsContainer.style("font-family", "Arial");
settingsContainer.style("font-size", "10pt");
Mit let settingsContainer = createDiv('')
wird ein neues <div>
HTML-Element erstellt, das als Container für die anderen Elemente dienen soll. Der leere String ''
bedeutet, dass das <div>
ohne Inhalt erstellt wird.
Mit der Zeile settingsContainer.style('display', 'grid')
wird die display
CSS-Eigenschaft des Containers auf grid
gesetzt. Das Grid-Layout ermöglicht es, Elemente in einem zweidimensionalen Raster anzuordnen.
Die Zeile settingsContainer.style('grid-template-rows', 'auto auto')
definiert die Reihenstruktur des Grids. auto auto
bedeutet, dass zwei Reihen erstellt werden und jede Reihe automatisch die Höhe des Inhalts ihrer Zellen annimmt.
settingsContainer.style('grid-template-columns', '215px 215px')
Hier werden die Spalten des Grids definiert. Durch die Angaben 215px 215px
werden zwei gleichgroße Spalten erstellt, die zusammen ungefähr die Breite des Canvas ergeben.
settingsContainer.style('grid-gap', '10px')
setzt den Abstand zwischen den Reihen und Spalten auf 10 Pixel. und settingsContainer.style('padding', '10px')
fügt einen Innenabstand von 10 Pixeln auf allen Seiten innerhalb des Containers hinzu.
Durch settingsContainer.style('margin-bottom', '10px')
wird ein Abstand von 10 Pixeln unterhalb des Containers hinzugefügt.
Mit den letzten zwei Styles wird die Schriftart und Schriftgröße festgelegt.
Sättigung (satContainer)
let satContainer = createDiv('').parent(settingsContainer).style('display', 'flex').style('flex-direction', 'column');
createP('Sättigung').parent(satContainer).style('color', 'black');
slider = createSlider(100, 255, 170).parent(satContainer);
createDiv('')
erstellt ein <div>
Element ohne Inhalt.
Durch .parent(settingsContainer)
wirddas neu erstellte <div>
als Kind des settingsContainer
hinzugefügt.
.style('display', 'flex')
setzt das Display-Property des <div>
auf Flexbox und .style('flex-direction', 'column')
richtet die Kind-Elemente vertikal an.
createP('Sättigung')
erstellt ein <p>
Element mit dem Text "Sättigung" als Beschriftung für den Slider und durch
.parent(satContainer)
wird das <p>
Element als Kind des satContainer
hinzugefügt.
.style('color', 'black')
setzt die Textfarbe der Beschriftung auf Schwarz. Den Slider erstellt man mit createSlider(min, max, default).
In diesem kann man einen bestimmten Wertebereich mit einem Standardwert definieren. createSlider(100, 255, 170)
erstellt einen Slider mit einem Minimalwert von 100, einem Maximalwert von 255 und einem Startwert von 170.
Durch .parent(satContainer)
wird der Slider wird als Kind des satContainer
hinzugefügt.
Strichstärke (weightContainer)
let weightContainer = createDiv('').parent(settingsContainer).style('display', 'flex').style('flex-direction', 'column');
createP('Strichstärke').parent(weightContainer).style('color', 'black');
slider2 = createSlider(4, 20, 12).parent(weightContainer);
Dieser Abschnitt tut im Wesentlichen das Gleiche wie bei der Sättigung, erstellt aber Elemente für die Anpassung der Strichstärke. Der Slider slider2
hat hier einen Bereich von 4 bis 20 mit einem Startwert von 12.
Winkel (angleContainer)
let angleContainer = createDiv('').parent(settingsContainer).style('display', 'flex').style('flex-direction', 'column');
createP('Winkel').parent(angleContainer).style('color', 'black');
slider3 = createSlider(5, 23, 12).parent(angleContainer);
Dieser Abschnitt wiederholt den Prozess ein drittes Mal, diesmal aber für den Winkel. Der Slider slider3
erlaubt Werte zwischen 5 und 23 und beginnt mit dem Wert 12.
In jedem der drei Fälle wird eine vertikale Gruppierung flex-direction: column
von zwei Elementen erstellt. Ein Paragraph-Element <p>
, das als Label dient, und ein Slider-Element, das die Steuerung bietet. Diese Gruppen sind wiederum im settingsContainer
angeordnet, der als Grid konfiguriert ist.
Diese Struktur erlaubt es, die drei verschiedenen Aspekte des Kaleidoskops interaktiv zu steuern: die Sättigung der Farben, die Strichstärke von Linien und den Winkel, in dem das Kaleidoskop gezeichnet wird.
Erstellen des Buttons
let buttonContainer = createDiv('').parent(settingsContainer).style('display', 'flex').style('flex-direction', 'column');
Die Funktion createDiv('')
erstellt ein neues <div>
-Element. Das Element.parent(settingsContainer)
wird zum Kind des settingsContainer
. Durch.style('display', 'flex')
wird das <div>
-Element auf "flex" gesetzt, um das Flexbox-Modell anzuwenden..style('flex-direction', 'column')
bestimmt die Hauptachse der Flexbox, "column" legt fest, dass die Kind-Elemente des Containers vertikal angeordnet werden.
colorButton = createButton('Hintergrundfarbe generieren');
createButton('Hintergrundfarbe generieren')
erstellt ein Button-Element mit dem Text "Hintergrundfarbe generieren". Dies wird der sichtbare Text auf dem Button sein.
colorButton.style('background-color', 'gray');
colorButton.style('color', 'white');
colorButton.style('font-family', 'Arial');
colorButton.style('padding', '10px 20px');
colorButton.style("margin-top", "20px");
colorButton.style('border', 'none');
colorButton.style('border-radius', '5px');
colorButton.style('cursor', 'pointer');
.style('background-color', 'gray')
setzt die Hintergrundfarbe des Buttons auf Grau. Durch .style('color', 'white')
wird der Text auf dem Button weiß gefärbt.
.style('font-family', 'Arial')
legt die Schriftart Arial für den Text auf dem Button fest.
.style('padding', '10px 20px')
fügt einen Innenabstand zum Button hinzu und colorButton.style('margin-top', '20px')
fügt einen Rand oben von 20px hinzu.
.style('border', 'none')
entfernt jeglichen Rahmen um den Button herum und mit
.style('border-radius', '5px')
werden die Ecken des Buttons um 5px abgerundet.
.style('cursor', 'pointer')
ändert den Mauszeiger zu einem Zeiger, wenn er über dem Button ist, was verdeutlicht, dass der Button klickbar ist.
colorButton.mousePressed(changeBackgroundColor).parent(buttonContainer);
colorButton.mousePressed(changeBackgroundColor)
weist dem Button colorButton
eine Funktion zu, die ausgeführt wird, wenn der Button geklickt wird. In diesem Fall ist die Funktion changeBackgroundColor()
ausgeführt, wenn der Button geklickt wird. Diese Funktion ändert die Hintergrundfarbe der Zeichenfläche.
.parent(buttonContainer)
setzt das neu erstellte Button-Element als ein Kind-Element von buttonContainer
.
Zufällige Farbe generieren
function changeBackgroundColor() {
let randomHue = random(360);
background(randomHue, 150, 100);
}
Die Funktion function changeBackgroundColor()
generiert eine zufällige Farbtonkomponente (Hue) und ändert die Hintergrundfarbe entsprechend, wenn der Button gedrückt wird. Hier wird eine Variable randomHue
deklariert und ihr ein zufälliger Wert zwischen 0 und 360 zugewiesen. Die Funktion random()
generiert eine zufällige Zahl zwischen 0 und 360.
background(randomHue, 150, 100);
setzt die Hintergrundfarbe des Canvas auf eine neue Farbe. Der erste Parameter randomHue
ist der zufällige Wert für den Farbton (Hue) im HSB-Farbraum, der zwischen 0 und 360 liegt. Die nächsten beiden Parameter sind festgelegt auf 150
für die Sättigung und 100
für die Helligkeit. Diese Werte bleiben konstant, was bedeutet, dass die Hintergrundfarbe immer eine bestimmte Sättigung und Helligkeit haben wird, während der Farbton zufällig variiert.
Vollständiger Code des Buttons
let buttonContainer = createDiv('').parent(settingsContainer).style('display', 'flex').style('flex-direction', 'column');
colorButton = createButton('Hintergrundfarbe generieren');
colorButton.style('background-color', 'gray');
colorButton.style('color', 'white');
colorButton.style('font-family', 'Arial');
colorButton.style('padding', '10px 20px');
colorButton.style('border', 'none');
colorButton.style('border-radius', '5px');
colorButton.style('cursor', 'pointer');
colorButton.mousePressed(changeBackgroundColor).parent(buttonContainer);
}
// Funktion zum Ändern der Hintergrundfarbe
function changeBackgroundColor() {
let randomHue = random(360);
background(randomHue, 150, 100);
}
Sliderwerte in die for-Schleife einfügen
function draw() {
translate(width / 2, height / 2);
let sat = slider.value();
let weight = slider2.value();
angle = slider3.value();
for (let i = 0; i < angle; i++) {
rotate(angle);
strokeWeight(weight);
stroke(mouseX, sat, sat, 0.5);
line(mouseX, mouseY, pmouseX, pmouseY);
push();
scale(1, -1);
line(mouseX, mouseY, pmouseX, pmouseY);
pop();
}
}
Jetzt ersetzen wir die Standartwerte, die im ersten Teil des Tutorials festgelegt wurden durch die Werte der Slider die zuvor erstellt wurden.
Über slider.value()
wird der Wert für die Sättigung ausgelesen welcher dann in der Stroke-Funktion eingesetzt wird. Das selbe macht man mit slider2.value()
und slider3.value()
um die Strichstärke und den Winkel in die entsprechenden Funktionen einzusetzen.
Vollständiger Code des Kaleidoskops
let angle;
let slider, slider2, slider3;
let colorButton;
function setup() {
createCanvas(450, 450);
background(30);
colorMode(HSB, 360, 150, 100, 1);
// Erstelle einen Container für die Einstellungen
let settingsContainer = createDiv("");
settingsContainer.style("display", "grid");
settingsContainer.style("grid-template-rows", "auto auto"); // Zwei Reihen: eine für Überschrift, eine für Slider
settingsContainer.style("grid-template-columns", "215px 215px"); // Spaltenaufteilung
settingsContainer.style("grid-gap", "10px"); // Abstand zwischen den Spalten
settingsContainer.style("padding", "10px"); // Padding für das gesamte Container
settingsContainer.style("margin-bottom", "10px");
settingsContainer.style("font-family", "Arial");
settingsContainer.style("font-size", "10pt");
// Erstelle Container für jeden Slider und Überschrift
let satContainer = createDiv("")
.parent(settingsContainer)
.style("display", "flex")
.style("flex-direction", "column");
createP("Sättigung").parent(satContainer).style("color", "black");
slider = createSlider(100, 255, 170).parent(satContainer);
let weightContainer = createDiv("")
.parent(settingsContainer)
.style("display", "flex")
.style("flex-direction", "column");
createP("Strichstärke").parent(weightContainer).style("color", "black");
slider2 = createSlider(4, 20, 12).parent(weightContainer);
let angleContainer = createDiv("")
.parent(settingsContainer)
.style("display", "flex")
.style("flex-direction", "column");
createP("Winkel").parent(angleContainer).style("color", "black");
slider3 = createSlider(5, 23, 12).parent(angleContainer);
let buttonContainer = createDiv("")
.parent(settingsContainer)
.style("display", "flex")
.style("flex-direction", "column");
colorButton = createButton("Hintergrundfarbe generieren");
colorButton.style("background-color", "gray");
colorButton.style("color", "white");
colorButton.style("font-family", "Arial");
colorButton.style("padding", "10px 20px");
colorButton.style("margin-top", "20px");
colorButton.style("border", "none");
colorButton.style("border-radius", "5px");
colorButton.style("cursor", "pointer");
colorButton.mousePressed(changeBackgroundColor).parent(buttonContainer);
}
function draw() {
translate(width / 2, height / 2);
let sat = slider.value();
let weight = slider2.value();
angle = slider3.value();
for (let i = 0; i < angle; i++) {
rotate(angle);
strokeWeight(weight);
stroke(mouseX, sat, sat, 0.5);
line(mouseX, mouseY, pmouseX, pmouseY);
push();
scale(1, -1);
line(mouseX, mouseY, pmouseX, pmouseY);
pop();
}
}
// Funktion zum Ändern der Hintergrundfarbe
function changeBackgroundColor() {
let randomHue = random(360);
background(randomHue, 150, 100);
}
Viel Spaß beim Nachmachen!
Hier kannst du den Code direkt im Editor öffnen.
Als Inspiration für dieses Tutorial diente das Video Kaleidoscope - p5.js Tutorial (youtube.com) .