adding custom meme template instead of fixed library
This commit is contained in:
@@ -260,3 +260,26 @@ canvas#memeCanvas {
|
||||
gap: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
/* Custom Template Card & Drag Hover Styles */
|
||||
.custom-template-card .template-image-wrapper {
|
||||
transition: background-color 0.2s ease;
|
||||
}
|
||||
|
||||
.custom-template-card:hover .template-image-wrapper {
|
||||
background-color: rgba(255, 255, 255, 0.08) !important;
|
||||
}
|
||||
|
||||
.custom-template-card i {
|
||||
transition: transform 0.2s ease, color 0.2s ease;
|
||||
}
|
||||
|
||||
.custom-template-card:hover i {
|
||||
transform: scale(1.1);
|
||||
}
|
||||
|
||||
.canvas-wrapper.drag-hover {
|
||||
border-color: var(--accent, #9f0) !important;
|
||||
box-shadow: 0 0 25px rgba(159, 255, 0, 0.4) !important;
|
||||
}
|
||||
|
||||
|
||||
@@ -82,11 +82,23 @@
|
||||
|
||||
const defaultSize = 40;
|
||||
|
||||
// Initial layers
|
||||
textLayers = [
|
||||
{ id: Date.now(), text: '', x: canvas.width / 2, y: 40, fontSize: defaultSize },
|
||||
{ id: Date.now() + 1, text: '', x: canvas.width / 2, y: canvas.height - 100, fontSize: defaultSize }
|
||||
];
|
||||
// Initial layers - only set if we don't have any layers yet
|
||||
if (textLayers.length === 0) {
|
||||
textLayers = [
|
||||
{ id: Date.now(), text: '', x: canvas.width / 2, y: 40, fontSize: defaultSize },
|
||||
{ id: Date.now() + 1, text: '', x: canvas.width / 2, y: canvas.height - 100, fontSize: defaultSize }
|
||||
];
|
||||
} else {
|
||||
// Keep the text layers but adjust their coordinates to be in-bounds if they exceed new boundaries
|
||||
textLayers.forEach(layer => {
|
||||
if (layer.x > canvas.width) {
|
||||
layer.x = canvas.width / 2;
|
||||
}
|
||||
if (layer.y > canvas.height) {
|
||||
layer.y = canvas.height - 100;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
renderInputs();
|
||||
draw();
|
||||
@@ -460,6 +472,77 @@
|
||||
}
|
||||
});
|
||||
|
||||
// Custom Local Image Selector logic
|
||||
const fileInput = document.getElementById('customTemplateFile');
|
||||
const selectFileBtn = document.getElementById('selectCustomFileBtn');
|
||||
const canvasWrapper = document.querySelector('.canvas-wrapper');
|
||||
|
||||
const loadLocalImage = (file) => {
|
||||
if (file && file.type.startsWith('image/')) {
|
||||
const reader = new FileReader();
|
||||
reader.onload = (event) => {
|
||||
img.src = event.target.result;
|
||||
|
||||
// Update template metadata
|
||||
window.memeTemplate.name = file.name.replace(/\.[^/.]+$/, "");
|
||||
|
||||
// Update header title dynamically
|
||||
const headerTitle = document.querySelector('.meme-title');
|
||||
if (headerTitle) {
|
||||
headerTitle.innerHTML = `${window.f0ckI18n?.meme?.create_meme || 'Create Meme:'} ${window.memeTemplate.name}`;
|
||||
}
|
||||
|
||||
// Update tags input value if tags are present
|
||||
const tagsInput = document.getElementById('tags');
|
||||
if (tagsInput) {
|
||||
tagsInput.value = `meme, Custom, ${window.memeTemplate.name}`;
|
||||
}
|
||||
};
|
||||
reader.readAsDataURL(file);
|
||||
}
|
||||
};
|
||||
|
||||
if (selectFileBtn && fileInput) {
|
||||
selectFileBtn.addEventListener('click', () => {
|
||||
fileInput.click();
|
||||
});
|
||||
|
||||
fileInput.addEventListener('change', (e) => {
|
||||
const file = e.target.files[0];
|
||||
loadLocalImage(file);
|
||||
});
|
||||
}
|
||||
|
||||
// HTML5 Drag & Drop Support
|
||||
if (canvas && canvasWrapper) {
|
||||
const preventDefaults = (e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
};
|
||||
|
||||
['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => {
|
||||
canvasWrapper.addEventListener(eventName, preventDefaults, false);
|
||||
});
|
||||
|
||||
['dragenter', 'dragover'].forEach(eventName => {
|
||||
canvasWrapper.addEventListener(eventName, () => {
|
||||
canvasWrapper.classList.add('drag-hover');
|
||||
}, false);
|
||||
});
|
||||
|
||||
['dragleave', 'drop'].forEach(eventName => {
|
||||
canvasWrapper.addEventListener(eventName, () => {
|
||||
canvasWrapper.classList.remove('drag-hover');
|
||||
}, false);
|
||||
});
|
||||
|
||||
canvasWrapper.addEventListener('drop', (e) => {
|
||||
const dt = e.dataTransfer;
|
||||
const file = dt.files[0];
|
||||
loadLocalImage(file);
|
||||
}, false);
|
||||
}
|
||||
|
||||
// Initial draw
|
||||
setTimeout(draw, 300);
|
||||
}
|
||||
|
||||
@@ -528,7 +528,11 @@
|
||||
"text_layer": "Textebene",
|
||||
"enter_text": "Text eingeben...",
|
||||
"size_label": "Größe",
|
||||
"create_meme": "Meme erstellen:"
|
||||
"create_meme": "Meme erstellen:",
|
||||
"custom_template_title": "Eigene Vorlage",
|
||||
"custom_template_tag": "Lokale Datei",
|
||||
"select_image": "Eigenes Bild auswählen",
|
||||
"choose_file_btn": "Bild aussuchen"
|
||||
},
|
||||
"timeago": {
|
||||
"just_now": "gerade eben",
|
||||
|
||||
@@ -532,7 +532,11 @@
|
||||
"text_layer": "Text Layer",
|
||||
"enter_text": "Enter text...",
|
||||
"size_label": "Size",
|
||||
"create_meme": "Create Meme:"
|
||||
"create_meme": "Create Meme:",
|
||||
"custom_template_title": "Use Own Template",
|
||||
"custom_template_tag": "Local File",
|
||||
"select_image": "Select Custom Image",
|
||||
"choose_file_btn": "Choose Image"
|
||||
},
|
||||
"timeago": {
|
||||
"just_now": "just now",
|
||||
|
||||
@@ -528,7 +528,11 @@
|
||||
"text_layer": "Tekstlaag",
|
||||
"enter_text": "Voer tekst in...",
|
||||
"size_label": "Grootte",
|
||||
"create_meme": "Meme Maken:"
|
||||
"create_meme": "Meme Maken:",
|
||||
"custom_template_title": "Eigen sjabloon gebruiken",
|
||||
"custom_template_tag": "Lokaal bestand",
|
||||
"select_image": "Selecteer eigen afbeelding",
|
||||
"choose_file_btn": "Kies afbeelding"
|
||||
},
|
||||
"timeago": {
|
||||
"just_now": "zojuist",
|
||||
|
||||
@@ -531,7 +531,11 @@
|
||||
"text_layer": "Textebene",
|
||||
"enter_text": "Text eingeben...",
|
||||
"size_label": "Größe",
|
||||
"create_meme": "Memel erstellen:"
|
||||
"create_meme": "Memel erstellen:",
|
||||
"custom_template_title": "Eigene Vorlage nutzen",
|
||||
"custom_template_tag": "Lokale Datei",
|
||||
"select_image": "Eigenes Bild auswählen",
|
||||
"choose_file_btn": "Bild aussuchen"
|
||||
},
|
||||
"timeago": {
|
||||
"just_now": "gerade eben",
|
||||
|
||||
@@ -29,6 +29,30 @@ export default (router, tpl) => {
|
||||
});
|
||||
});
|
||||
|
||||
// Custom meme template page
|
||||
router.get(/^\/meme\/custom$/, lib.userauth, async (req, res) => {
|
||||
if (!cfg.websrv.meme_creator) {
|
||||
res.writeHead(404).end('Not Found');
|
||||
return;
|
||||
}
|
||||
res.reply({
|
||||
body: tpl.render('meme-creator', {
|
||||
template: {
|
||||
id: 'custom',
|
||||
name: 'Custom Template',
|
||||
url: 'data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="800" height="600" viewBox="0 0 800 600"><rect width="800" height="600" fill="%231a1a1b"/><text x="50%25" y="50%25" fill="%23888888" font-family="sans-serif" font-size="24" dominant-baseline="middle" text-anchor="middle">Click %22Choose Image%22 or Drag and Drop here</text></svg>',
|
||||
category: 'Custom',
|
||||
sub_category: ''
|
||||
},
|
||||
page_meta: {
|
||||
title: 'Create Meme - Custom Template',
|
||||
description: 'Create a meme using your own custom template',
|
||||
url: `https://${cfg.main.url.domain}/meme/custom`
|
||||
}
|
||||
}, req)
|
||||
});
|
||||
});
|
||||
|
||||
// Meme creator page
|
||||
router.get(/^\/meme\/(?<id>[a-z0-9-]+)$/, lib.userauth, async (req, res) => {
|
||||
if (!cfg.websrv.meme_creator) {
|
||||
|
||||
@@ -32,6 +32,14 @@
|
||||
</div>
|
||||
|
||||
<div class="meme-controls">
|
||||
<div id="customTemplateSelector" class="form-group" style="margin-bottom: 20px;">
|
||||
<label for="customTemplateFile">{{ t('meme.select_image') }}</label>
|
||||
<input type="file" id="customTemplateFile" accept="image/*" style="display: none;">
|
||||
<button id="selectCustomFileBtn" type="button" class="btn btn-secondary btn-block">
|
||||
<i class="fa fa-image"></i> {{ t('meme.choose_file_btn') }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div id="textLayersContainer">
|
||||
<!-- Dynamic inputs injected by JS -->
|
||||
</div>
|
||||
|
||||
@@ -14,6 +14,15 @@
|
||||
</div>
|
||||
|
||||
<div class="template-grid">
|
||||
<a href="/meme/custom" class="template-item custom-template-card" data-category="All">
|
||||
<div class="template-image-wrapper" style="background: var(--nav-bg, #2b2b2b); border-bottom: 1px solid rgba(255,255,255,0.05);">
|
||||
<i class="fa fa-upload" style="font-size: 3.5rem; color: var(--accent, #9f0);"></i>
|
||||
</div>
|
||||
<div class="template-info">
|
||||
<span class="template-name">{{ t('meme.custom_template_title') }}</span>
|
||||
<span class="template-category-tag">{{ t('meme.custom_template_tag') }}</span>
|
||||
</div>
|
||||
</a>
|
||||
@each(templates as template)
|
||||
<a href="/meme/{{ template.id }}" class="template-item" data-category="{!! template.category || 'General' !!}">
|
||||
<div class="template-image-wrapper">
|
||||
|
||||
Reference in New Issue
Block a user