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
134 changes: 134 additions & 0 deletions index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>PinGenius Creator</title>
<link rel="stylesheet" href="style.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/html2canvas/1.4.1/html2canvas.min.js"></script>
</head>
<body>
<div class="app-container">
<div class="controls-container">
<h1>PinGenius Creator</h1>
<div class="input-group">
<label for="topic-input">Topic (Internal Tagging/Organization)</label>
<input type="text" id="topic-input" maxlength="100" placeholder="e.g., 'Dog Training'">
</div>
<div class="input-group">
<label for="headline-input">Headline</label>
<input type="text" id="headline-input" maxlength="70" placeholder="e.g., '5 Easy Tricks to Teach Your Puppy'">
</div>
<div class="input-group">
<label for="main-text-input">Main Pin Text</label>
<textarea id="main-text-input" maxlength="180" rows="4" placeholder="e.g., 'Master these simple commands...'"></textarea>
</div>
<div class="input-group">
<label for="pro-tip-input">Pro-Tip/Fact</label>
<div class="pro-tip-group">
<select id="pro-tip-label">
<option value="Pro-Tip">Pro-Tip</option>
<option value="Did You Know?">Did You Know?</option>
</select>
<input type="text" id="pro-tip-input" maxlength="120" placeholder="e.g., 'Consistency is key!'">
</div>
</div>
<div class="input-group">
<label for="cta-input">Call to Action (CTA)</label>
<input type="text" id="cta-input" maxlength="90" placeholder="e.g., 'Click to Read More!'">
</div>

<hr>
<div class="customization-section">
<h2>Background</h2>
<div class="input-group">
<label for="bg-color-picker">Background Color</label>
<input type="color" id="bg-color-picker" value="#dddddd">
</div>
<div class="input-group">
<label for="bg-image-picker">Background Image</label>
<input type="file" id="bg-image-picker" accept="image/*">
</div>
</div>

<hr>
<div class="customization-section">
<h2>Text Styles</h2>

<div class="text-style-group">
<h3>Headline</h3>
<div class="style-controls">
<div class="input-group-small">
<label>Color</label>
<input type="color" id="headline-color-picker" value="#ffffff">
</div>
<div class="input-group-large">
<label>Font Size (<span id="headline-size-value">35</span>px)</label>
<input type="range" id="headline-size-slider" min="20" max="80" value="35">
</div>
</div>
</div>

<div class="text-style-group">
<h3>Main Text</h3>
<div class="style-controls">
<div class="input-group-small">
<label>Color</label>
<input type="color" id="main-text-color-picker" value="#ffffff">
</div>
<div class="input-group-large">
<label>Font Size (<span id="main-text-size-value">18</span>px)</label>
<input type="range" id="main-text-size-slider" min="12" max="40" value="18">
</div>
</div>
</div>

<div class="text-style-group">
<h3>Pro-Tip/Fact</h3>
<div class="style-controls">
<div class="input-group-small">
<label>Color</label>
<input type="color" id="pro-tip-color-picker" value="#000000">
</div>
<div class="input-group-large">
<label>Font Size (<span id="pro-tip-size-value">16</span>px)</label>
<input type="range" id="pro-tip-size-slider" min="10" max="30" value="16">
</div>
</div>
</div>

<div class="text-style-group">
<h3>Call to Action</h3>
<div class="style-controls">
<div class="input-group-small">
<label>Color</label>
<input type="color" id="cta-color-picker" value="#ffffff">
</div>
<div class="input-group-large">
<label>Font Size (<span id="cta-size-value">22</span>px)</label>
<input type="range" id="cta-size-slider" min="14" max="50" value="22">
</div>
</div>
</div>
</div>

<hr>
<div class="export-section">
<button id="export-btn" class="btn-primary">Export as PNG</button>
</div>
</div>
<div class="preview-container">
<div id="pin-preview">
<div id="headline-preview" class="text-preview">Your Headline</div>
<div id="main-text-preview" class="text-preview">Your main pin text goes here. It can be a short sentence or two.</div>
<div id="pro-tip-preview" class="text-preview">
<span id="pro-tip-label-preview">Pro-Tip:</span>
<span id="pro-tip-text-preview">This is an amazing pro-tip!</span>
</div>
<div id="cta-preview" class="text-preview">Call to Action</div>
</div>
</div>
</div>
<script src="script.js"></script>
</body>
</html>
198 changes: 198 additions & 0 deletions script.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,198 @@
document.addEventListener('DOMContentLoaded', () => {
console.log('PinGenius Creator script loaded!');

// Get references to the DOM elements
const controlsContainer = document.querySelector('.controls-container');
const pinPreview = document.getElementById('pin-preview');

// --- Get references to DOM elements ---

// Input fields
const headlineInput = document.getElementById('headline-input');
const mainTextInput = document.getElementById('main-text-input');
const proTipLabel = document.getElementById('pro-tip-label');
const proTipInput = document.getElementById('pro-tip-input');
const ctaInput = document.getElementById('cta-input');

// Preview elements
const headlinePreview = document.getElementById('headline-preview');
const mainTextPreview = document.getElementById('main-text-preview');
const proTipLabelPreview = document.getElementById('pro-tip-label-preview');
const proTipTextPreview = document.getElementById('pro-tip-text-preview');
const ctaPreview = document.getElementById('cta-preview');

// Customization fields
const bgColorPicker = document.getElementById('bg-color-picker');
const bgImagePicker = document.getElementById('bg-image-picker');
const exportBtn = document.getElementById('export-btn');


// --- Text Style Elements ---
const textStyleControls = {
headline: {
color: document.getElementById('headline-color-picker'),
size: document.getElementById('headline-size-slider'),
sizeValue: document.getElementById('headline-size-value'),
preview: headlinePreview
},
mainText: {
color: document.getElementById('main-text-color-picker'),
size: document.getElementById('main-text-size-slider'),
sizeValue: document.getElementById('main-text-size-value'),
preview: mainTextPreview
},
proTip: {
color: document.getElementById('pro-tip-color-picker'),
size: document.getElementById('pro-tip-size-slider'),
sizeValue: document.getElementById('pro-tip-size-value'),
preview: proTipPreview
},
cta: {
color: document.getElementById('cta-color-picker'),
size: document.getElementById('cta-size-slider'),
sizeValue: document.getElementById('cta-size-value'),
preview: ctaPreview
}
};

// --- Functions ---

// Function to update the live preview
function updatePreview() {
// Update headline
headlinePreview.textContent = headlineInput.value;
// Update main text
mainTextPreview.textContent = mainTextInput.value;
// Update pro-tip/fact
proTipLabelPreview.textContent = proTipLabel.value + ': ';
proTipTextPreview.textContent = proTipInput.value;
// Update CTA
ctaPreview.textContent = ctaInput.value;
}

// --- Draggable Functionality ---
function makeDraggable(element) {
let pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0;

element.onmousedown = dragMouseDown;

function dragMouseDown(e) {
e = e || window.event;
e.preventDefault();
// Get the mouse cursor position at startup:
pos3 = e.clientX;
pos4 = e.clientY;
document.onmouseup = closeDragElement;
// Call a function whenever the cursor moves:
document.onmousemove = elementDrag;
}

function elementDrag(e) {
e = e || window.event;
e.preventDefault();
// Calculate the new cursor position:
pos1 = pos3 - e.clientX;
pos2 = pos4 - e.clientY;
pos3 = e.clientX;
pos4 = e.clientY;
// Set the element's new position, but constrain it to the parent
const parentRect = pinPreview.getBoundingClientRect();
const elemRect = element.getBoundingClientRect();

let newTop = element.offsetTop - pos2;
let newLeft = element.offsetLeft - pos1;

// Constrain top
if (newTop < 0) newTop = 0;
if (newTop + elemRect.height > parentRect.height) newTop = parentRect.height - elemRect.height;

// Constrain left
if (newLeft < 0) newLeft = 0;
if (newLeft + elemRect.width > parentRect.width) newLeft = parentRect.width - elemRect.width;

element.style.top = newTop + "px";
element.style.left = newLeft + "px";
}

function closeDragElement() {
// Stop moving when mouse button is released:
document.onmouseup = null;
document.onmousemove = null;
}
}


// --- Event Listeners ---

// Add event listeners to all input fields
controlsContainer.addEventListener('input', updatePreview);
controlsContainer.addEventListener('change', updatePreview); // For the select dropdown

// Initial call to set the preview from placeholder values
updatePreview();

// Background color listener
bgColorPicker.addEventListener('input', () => {
pinPreview.style.backgroundColor = bgColorPicker.value;
pinPreview.style.backgroundImage = 'none'; // Remove image when a color is selected
});

// Background image listener
bgImagePicker.addEventListener('change', () => {
const file = bgImagePicker.files[0];
if (file) {
const reader = new FileReader();
reader.onload = (e) => {
pinPreview.style.backgroundImage = `url('${e.target.result}')`;
};
reader.readAsDataURL(file);
}
});

// --- Text Style Listeners ---
for (const key in textStyleControls) {
const controls = textStyleControls[key];

// Color listener
controls.color.addEventListener('input', () => {
controls.preview.style.color = controls.color.value;
});

// Size listener
controls.size.addEventListener('input', () => {
const size = controls.size.value;
controls.preview.style.fontSize = `${size}px`;
controls.sizeValue.textContent = size;
});

// Set initial values on load
controls.preview.style.color = controls.color.value;
controls.preview.style.fontSize = `${controls.size.value}px`;
}

// --- Apply Draggable Functionality ---
for (const key in textStyleControls) {
makeDraggable(textStyleControls[key].preview);
}

// --- Export Listener ---
exportBtn.addEventListener('click', () => {
// Temporarily remove box-shadow for cleaner export
const originalShadow = pinPreview.style.boxShadow;
pinPreview.style.boxShadow = 'none';

html2canvas(pinPreview, {
useCORS: true,
allowTaint: true,
backgroundColor: null // Use the actual background color/image
}).then(canvas => {
// Restore box-shadow
pinPreview.style.boxShadow = originalShadow;

const link = document.createElement('a');
link.download = 'pingenius-pin.png';
link.href = canvas.toDataURL('image/png');
link.click();
});
});
});
Loading