Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
375 changes: 375 additions & 0 deletions editor.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,375 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Flag + Overlay Post Creator</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0" />

<style>
* {
box-sizing: border-box;
}
@font-face {
font-family: 'Monocraft';
src: url('./ttf/Monocraft-SemiBold.ttf') format('truetype');
}
body {
font-family: 'Monocraft', monospace, sans-serif;
text-align: center;
margin: 0;
padding: 20px;
background-color: #f0f0f0;
}
h2 {
margin-bottom: 10px;
}
.container {
display: flex;
flex-wrap: wrap;
justify-content: center;
gap: 20px;
max-width: 1200px;
margin: 0 auto;
}
.config-wrapper {
flex: 1 1 300px;
max-width: 400px;
text-align: left;
}
.config-wrapper input[type="text"],
.config-wrapper input[type="file"],
.config-wrapper button,
.config-wrapper label {
display: block;
width: 100%;
margin-top: 10px;
font-size: 16px;
}
.canvas-wrapper {
flex: 1 1 400px;
max-width: 700px;
}
canvas {
width: 100%;
height: auto;
border: 1px solid #ccc;
background-color: #000;
display: block;
}
button {
cursor: pointer;
}
.checkbox-wrapper {
margin-top: 10px;
font-size: 16px;
}
</style>
</head>

<body>
<h2>Flag + Overlay Post Creator</h2>

<div class="container">
<div class="config-wrapper">
<!-- Upload Flag -->
<label for="uploadFlag">Upload Flag Icon:</label>
<input type="file" id="uploadFlag" accept="image/*" />

<!-- Upload Main Image -->
<label for="uploadMain">Upload Main Image:</label>
<input type="file" id="uploadMain" accept="image/*" />

<!-- Text Inputs -->
<label for="titleText">Title (e.g. "Aman Central"):</label>
<input type="text" id="titleText" placeholder="Enter Title" />

<label for="subtitleText">Subtitle (e.g. "Alor Setar, Malaysia"):</label>
<input type="text" id="subtitleText" placeholder="Enter Subtitle" />

<label for="usernameText">Username (Minecraft name):</label>
<input type="text" id="usernameText" placeholder="Enter Username" />

<!-- New: Hide Overlay Checkbox -->
<div class="checkbox-wrapper">
<input type="checkbox" id="hideOverlay" />
<label for="hideOverlay">Hide Overlay</label>
</div>

<button id="downloadBtn">Download Final Image</button>
</div>

<div class="canvas-wrapper">
<canvas id="canvas"></canvas>
</div>
</div>

<script>
// -------------------------------------------------------------
// CONSTANTS
// -------------------------------------------------------------
const CANVAS_SIZE = 1080; // 1080×1080
const BOTTOM_BAR_HEIGHT = 200; // black overlay bar height
const FLAG_MAX_WIDTH = 100; // draw flag max width
const FLAG_MARGIN = 20; // margin from top-left for flag
const HEAD_SIZE = 64; // pixel size for player head
const TEXT_COLOR = '#FFFFFF'; // white
const BAR_COLOR = 'rgba(0, 0, 0, 0.85)'; // nearly-opaque black

// -------------------------------------------------------------
// GET DOM ELEMENTS
// -------------------------------------------------------------
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
const uploadFlag = document.getElementById('uploadFlag');
const uploadMain = document.getElementById('uploadMain');
const titleInput = document.getElementById('titleText');
const subtitleInput = document.getElementById('subtitleText');
const userInput = document.getElementById('usernameText');
const hideOverlayCB = document.getElementById('hideOverlay');
const downloadBtn = document.getElementById('downloadBtn');

// -------------------------------------------------------------
// STATE VARIABLES
// -------------------------------------------------------------
canvas.width = CANVAS_SIZE;
canvas.height = CANVAS_SIZE;

let flagImg = new Image();
let mainImg = new Image();
let headImg = new Image();
let dragging = false;
let offsetX = 0;
let offsetY = 0;
let imgX = 0, imgY = 0; // top-left corner of main image
let imgScale = 1;
let scaledW = 0, scaledH = 0; // scaled dimensions of main image
let headLoaded = false;

// -------------------------------------------------------------
// DRAW FUNCTION
// -------------------------------------------------------------
function draw() {
// 1) Clear entire canvas
ctx.clearRect(0, 0, CANVAS_SIZE, CANVAS_SIZE);

// 2) Draw main image (if loaded) with drag/crop logic
if (mainImg.complete && mainImg.naturalWidth) {
if (scaledW > 0 && scaledH > 0) {
// Constrain imgX,imgY so image never leaves the 1080×1080 square
const minX = CANVAS_SIZE - scaledW;
const minY = CANVAS_SIZE - scaledH;
imgX = Math.min(Math.max(imgX, minX), 0);
imgY = Math.min(Math.max(imgY, minY), 0);
ctx.drawImage(mainImg, imgX, imgY, scaledW, scaledH);
}
}
// 3) If “Hide Overlay” is checked, skip the bottom bar
if (hideOverlayCB.checked) {
return;
}

// 4) Draw flag icon in top-left (if loaded)
if (flagImg.complete && flagImg.naturalWidth) {
// maintain aspect ratio, max width = FLAG_MAX_WIDTH
const aspect = flagImg.naturalHeight / flagImg.naturalWidth;
const fw = FLAG_MAX_WIDTH;
const fh = FLAG_MAX_WIDTH * aspect;
ctx.drawImage(flagImg, FLAG_MARGIN, FLAG_MARGIN, fw, fh);
}


// 5) Draw bottom black overlay bar
ctx.fillStyle = BAR_COLOR;
ctx.fillRect(0, CANVAS_SIZE - BOTTOM_BAR_HEIGHT, CANVAS_SIZE, BOTTOM_BAR_HEIGHT);

// 6) Draw Title / Subtitle / Username+Head (all white, Monocraft)
ctx.fillStyle = TEXT_COLOR;
ctx.textAlign = 'center';
ctx.textBaseline = 'top';

// Title (BIG)
const title = titleInput.value.trim();
if (title) {
ctx.font = "bold 48px 'Monocraft', monospace";
ctx.fillText(
title,
CANVAS_SIZE / 2,
CANVAS_SIZE - BOTTOM_BAR_HEIGHT + 20
);
}

// Subtitle (smaller)
const subtitle = subtitleInput.value.trim();
if (subtitle) {
ctx.font = "32px 'Monocraft', monospace";
ctx.fillText(
subtitle,
CANVAS_SIZE / 2,
CANVAS_SIZE - BOTTOM_BAR_HEIGHT + 80
);
}

// Username + Head Row
const username = userInput.value.trim().toUpperCase();
if (username) {
// If head loaded, draw head + text; otherwise just text centered
if (headLoaded) {
const textMetrics = ctx.measureText(username);
const totalRowWidth = HEAD_SIZE + 10 + textMetrics.width;
const startX = (CANVAS_SIZE - totalRowWidth) / 2;

ctx.drawImage(
headImg,
startX,
CANVAS_SIZE - BOTTOM_BAR_HEIGHT + 130,
HEAD_SIZE,
HEAD_SIZE
);

ctx.font = "24px 'Monocraft', monospace";
ctx.textAlign = 'left';
ctx.textBaseline = 'middle';
ctx.fillText(
username,
startX + HEAD_SIZE + 10,
CANVAS_SIZE - BOTTOM_BAR_HEIGHT + 130 + HEAD_SIZE / 2
);
} else {
ctx.font = "24px 'Monocraft', monospace";
ctx.fillText(
username,
CANVAS_SIZE / 2,
CANVAS_SIZE - BOTTOM_BAR_HEIGHT + 150
);
}
}
}

// -------------------------------------------------------------
// EVENT: Upload Flag Image
// -------------------------------------------------------------
uploadFlag.addEventListener('change', e => {
const file = e.target.files[0];
if (!file) return;
const reader = new FileReader();
reader.onload = function(evt) {
flagImg = new Image();
flagImg.onload = draw;
flagImg.src = evt.target.result;
};
reader.readAsDataURL(file);
});

// -------------------------------------------------------------
// EVENT: Upload Main Image (with initial scale logic)
// -------------------------------------------------------------
uploadMain.addEventListener('change', e => {
const file = e.target.files[0];
if (!file) return;
const reader = new FileReader();
reader.onload = function(evt) {
mainImg = new Image();
mainImg.onload = function() {
// Fit main image so it covers at least 1080×1080
const scaleX = CANVAS_SIZE / mainImg.naturalWidth;
const scaleY = CANVAS_SIZE / mainImg.naturalHeight;
imgScale = Math.max(scaleX, scaleY);
scaledW = mainImg.naturalWidth * imgScale;
scaledH = mainImg.naturalHeight * imgScale;
// Center it
imgX = (CANVAS_SIZE - scaledW) / 2;
imgY = (CANVAS_SIZE - scaledH) / 2;
draw();
};
mainImg.src = evt.target.result;
};
reader.readAsDataURL(file);
});

// -------------------------------------------------------------
// EVENT: Drag & Crop Logic for Main Image
// -------------------------------------------------------------
canvas.addEventListener('mousedown', e => {
if (!(mainImg.complete && mainImg.naturalWidth)) return;
dragging = true;
const rect = canvas.getBoundingClientRect();
offsetX = (e.clientX - rect.left) - imgX;
offsetY = (e.clientY - rect.top) - imgY;
});
canvas.addEventListener('mousemove', e => {
if (!dragging) return;
const rect = canvas.getBoundingClientRect();
imgX = (e.clientX - rect.left) - offsetX;
imgY = (e.clientY - rect.top) - offsetY;
draw();
});
canvas.addEventListener('mouseup', () => dragging = false);
canvas.addEventListener('mouseout', () => dragging = false);

// -------------------------------------------------------------
// EVENT: Username → Load Head from PlayerDB
// -------------------------------------------------------------
userInput.addEventListener('input', () => {
const name = userInput.value.trim().toUpperCase();
if (!name) {
headLoaded = false;
draw();
return;
}

// Try local first
const localPath = `./heads/${name}.png`;
headImg = new Image();
headImg.onload = () => {
headLoaded = true;
draw();
};
headImg.onerror = () => {
// If local not found → fetch from PlayerDB
fetch(`https://playerdb.co/api/player/minecraft/${name}`)
.then(res => {
if (!res.ok) throw new Error('Not found');
return res.json();
})
.then(json => {
headImg.src = json.data.player.avatar;
headImg.onload = () => {
headLoaded = true;
draw();
};
})
.catch(_ => {
headLoaded = false;
draw();
});
};
headImg.src = localPath;
});

// -------------------------------------------------------------
// EVENT: Inputs / HideOverlay change → Redraw
// -------------------------------------------------------------
titleInput.addEventListener('input', draw);
subtitleInput.addEventListener('input', draw);
hideOverlayCB.addEventListener('change', draw);

// -------------------------------------------------------------
// DOWNLOAD FINAL IMAGE as `post.png`
// -------------------------------------------------------------
downloadBtn.addEventListener('click', () => {
const link = document.createElement('a');
link.download = 'post.png';
link.href = canvas.toDataURL('image/png');
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
});

// -------------------------------------------------------------
// INITIAL DRAW (blank canvas)
// -------------------------------------------------------------
draw();
</script>
</body>
</html>

File renamed without changes
File renamed without changes
Binary file added heads/NOSTOWO.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
File renamed without changes
Loading