Hast du eine Frage?


How-to: Textfeld Tool


Willkommen zum How-to: Textfeld Tool.
Hier lernst du, wie du ein Werkzeug programmierst mit dem du
per Gestensteuerung Textfelder aufziehen und platzieren kannst.

Falls du etwas nicht verstehst, nutze den Chatbot auf der rechten Seite.
Kopiere den Teil vom Code, den du erklärt haben möchtest und stelle deine Frage dazu.



Was brauchen wir für das Werkzeug:
→ Ordnerstruktur & Dateien (siehe How-to: Basic Setup)
→ Browser (funktioniert am besten mit Chrome)
→ Localhost (siehe How-to: Localhost)
→ Text-Editor (zb. SublimeText)

HTML index.html

Die index.html beinhaltet u.A.:
→ <script> zum Einbinden der benötigten Scripte
→ <h1> Überschrift (optional)
→ <p> Anleitungstext (optional)
→ <button> zum Hinzuf ügen von Textfeldern
→ <input> zum Einstellen der Schriftgröße
→ <div> als Container f ür <video> und <canvas>

1. Öffne die index.html

Du kannst den Code kopieren und in deine Datei einfügen:

<!DOCTYPE html>
<html>
<head>
<title>Textfelder</title>
<link rel="stylesheet" type="text/css" href="css/style.css">
<script src="https://cdn.jsdelivr.net/npm/@mediapipe/camera_utils@0.1/camera_utils.js" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/@mediapipe/drawing_utils@0.1/drawing_utils.js" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/@mediapipe/hands@0.1/hands.js" crossorigin="anonymous"></script>
</head>
<body>

<h1>Textfeld Tool</h1>
<p>Drücke auf "Neues Textfeld hinzufügen" und ziehe mit Zeigefinger und Daumen die Textbox auf. <br>
Halte die Textbox konstant in Größe und Position und sie wird in der Form auf der Leinwand fixiert. <br>
Funktioniert am besten wenn du gut ausgeleuchtet bist.
</p>

<button id="startButton">Neues Textfeld hinzufügen</button>
<input class="slider" type="range" min="1" max="100" value="50" oninput="handleSliderChange(this.value)">
<div class="container">
<video class="input_video" autoplay playsinline></video>
<canvas class="output_canvas" width="1480px" height="820px"></canvas>
</div>

<script src="js/script.js"></script>

</body>
</html>


2. Speicher die index.html

CSS style.css

1. Öffne die style.css

Die style.css beinhaltet:
→ *{} Zücksetzen auf den Default Style
→ h1{}, p{}, button{}, .slider{} basic Styling (optinal)
→ .container{} legt Größe fest und spiegelt Video und Gesten (wichtig)
→ .input_video{} definieren der Größe des Videos (wichtig)
→ .text-area{} Einstellungen für die Textboxen (wichtig)

Du kannst den Code kopieren und in deine style.css Datei einfügen:

* {
padding: 0;
margin: 0;
box-sizing: border-box;
}

body {
width: 100vw;
height: 100vw;
}

h1{
margin: 20px;
}

p {
margin-left: 20px;
}

button {
margin: 20px;
}

h1, p, button {
z-index: 9999999 !important;
}

.slider {
position: relative;
margin-top: 10px;
}

.container {
position: absolute;
transform: scaleX(-1);
width: 100vw;
height: 100vh;
top: 0;
z-index: -9999999 !important;
}

.input_video {
position: relative;
top: 0;
left: 0;
width: 100vw;
background-color: white;
height: 100vh;
object-fit: cover;
}

.text-area {
position: absolute;
font-size: 26px;
padding: 5px;
text-align: left;
white-space: pre-wrap;
overflow-wrap: break-word;
resize: none;
z-index: 9999999999!important;
text-decoration: none !important;
}


2. Speicher die style.css


JS script.js

Die script.js beinhaltet:
→ createTextArea: Wird definiert, um ein neues Textbereichs-Element zu erstellen und dessen Eigenschaften festzulegen
→ updateTextAreaPosition: Wird definiert, um die Position des Textfelds basierend auf den Positionen des Zeige- und Daumenfingers zu aktualisieren.
→ onResults: Wird definiert und als Rückruffunktion für die Handgesten-Ergebnisse aufgerufen
→ updateTextAreaSize: Aktualisiert die Größe des Textfelds basierend auf dem Abstand zwischen den Fingern
→ resetTextArea: Setzt das Textfeld zurück
→ handleSliderChange: Verarbeitet Schieberegleränderungen für die Schriftgröße des Textfeld
→ startButton.addEventListener: Button startet und stoppt das Tracking der Handgesten

1. Öffne die script.js

Du kannst den Code kopieren und in deine script.js Datei einfügen:

const videoElement = document.getElementsByClassName('input_video')[0];
const canvasElement = document.getElementsByClassName('output_canvas')[0];
const canvasCtx = canvasElement.getContext('2d');

let indexFingerPosition = null;
let thumbPosition = null;
let textAreaSize = 0;

let textArea = createTextArea();
document.body.appendChild(textArea);

let isPositionFixed = false;
let isSizeStable = false;
let stableSizeCount = 0;
const stableSizeThreshold = 25;
const sizeTolerance = 10;

let startTime = Date.now();
let stableSize = textAreaSize;

function createTextArea() {
const newTextArea = document.createElement('textarea');
newTextArea.classList.add('text-area');
newTextArea.style.width = `${textAreaSize}px`;
newTextArea.style.height = `${textAreaSize}px`;
newTextArea.style.display = 'none';
return newTextArea;
}

function updateTextAreaPosition() {
if (indexFingerPosition && thumbPosition) {
const avgX = (indexFingerPosition.x + thumbPosition.x) / 2;
const avgY = (indexFingerPosition.y + thumbPosition.y) / 2;

if (!isPositionFixed) {
const textAreaWidth = textArea.offsetWidth;
const textAreaHeight = textArea.offsetHeight;
textArea.style.left = `${avgX - (textAreaWidth / 2)}px`;
textArea.style.top = `${avgY - (textAreaHeight / 2)}px`;
}
}
}

function onResults(results) {
canvasCtx.clearRect(0, 0, canvasElement.width, canvasElement.height);

if (results.multiHandLandmarks && results.multiHandedness) {
for (let index = 0; index < results.multiHandLandmarks.length; index++) {
const classification = results.multiHandedness[index];
const isRightHand = classification.label === 'Right';
const landmarks = results.multiHandLandmarks[index];

const indexFingerLandmark = landmarks[8];
const thumbLandmark = landmarks[4];

if (indexFingerLandmark && thumbLandmark) {
const indexFingerX = canvasElement.width - (indexFingerLandmark.x * canvasElement.width);
const indexFingerY = indexFingerLandmark.y * canvasElement.height;
const thumbX = canvasElement.width - (thumbLandmark.x * canvasElement.width);
const thumbY = thumbLandmark.y * canvasElement.height;

indexFingerPosition = { x: indexFingerX, y: indexFingerY };
thumbPosition = { x: thumbX, y: thumbY };

const dx = indexFingerX - thumbX;
const dy = indexFingerY - thumbY;
const distance = Math.sqrt(dx * dx + dy * dy);

updateTextAreaSize(distance);
}
}
}

updateTextAreaPosition();
}

const hands = new Hands({
locateFile: (file) => {
return `https://cdn.jsdelivr.net/npm/@mediapipe/hands@0.1/${file}`;
}
});
hands.onResults(onResults);

const camera = new Camera(videoElement, {
onFrame: async () => {
await hands.send({ image: videoElement });
}
});

function updateTextAreaSize(distance) {
const minDistance = 50;
const maxDistance = 300;
const minSize = 50;
const maxSize = 200;

const size = (distance - minDistance) / (maxDistance - minDistance) * (maxSize - minSize) + minSize;

if (!isSizeStable) {
if (Math.abs(size - stableSize) <= sizeTolerance) {
stableSizeCount++;
if (stableSizeCount >= stableSizeThreshold) {
isSizeStable = true;
isPositionFixed = true;
textArea.style.position = 'fixed';
}
} else {
stableSize = size;
stableSizeCount = 0;
}
}

textAreaSize = stableSize;
textArea.style.width = `${textAreaSize}px`;
textArea.style.height = `${textAreaSize}px`;
}

function resetTextArea() {
indexFingerPosition = null;
thumbPosition = null;
textAreaSize = 0;
isPositionFixed = false;
isSizeStable = false;
stableSizeCount = 0;
textArea = createTextArea();
document.body.appendChild(textArea);
}

function handleSliderChange(value) {
const newSize = value;
textArea.style.fontSize = `${newSize}px`;
}

const startButton = document.getElementById('startButton');
let isTrackingStarted = true;

startButton.addEventListener('click', () => {
if (!isTrackingStarted) {
isTrackingStarted = true;
startButton.disabled = true;
textArea.style.display = 'block';
slider.style.display = 'block';
} else {
resetTextArea();
textArea.style.display = 'block';
slider.style.display = 'block';
startButton.textContent = 'Neues Textfeld platzieren';
}
});

startTime = Date.now();

window.addEventListener('load', () => {
camera.start();
});


2. Speicher die script.js


Du kannst jetzt den Ordner deines Werkzeuges mit einem Localhost öffnen und in deinem Browser das Tool testen :)