Image-based half-sheet handouts: vibrant gradient backgrounds with dynamic QR overlay

Replaces text-heavy card design with eye-catching image backgrounds (pocket/two/phone/graveyard/stop-waiting angles, 1700x1100px, half-sheet 2-per-page format). QR code + link overlaid dynamically via Canvas in bottom-right with semi-dark fade for readability. Print layout: two copies per letter page with cut line between. Visual impact first, clean overlay on top.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
martbost
2026-08-27 07:47:52 -05:00
parent 3761fab3c6
commit b019becebe
7 changed files with 145 additions and 47 deletions
+139 -12
View File
@@ -92,35 +92,162 @@
buildHalf(id);
}
function halfSheetHtml(f, url, shortUrl, haveId) {
return '<div class="hs-top"><div class="hs-ring"><span>RM</span></div><div class="hs-brand">THE RM CIRCLE</div><div class="hs-eye">' + f.eye + '</div></div>' +
'<div class="hs-main"><div class="hs-left"><h2>' + f.h + '</h2><p class="pitch">' + f.pitch + '</p>' +
'<div class="hs-chips">' + f.chips.map(function (c, i) { return '<span class="hs-chip' + (i === 0 ? ' gold' : '') + '">' + c + '</span>'; }).join('') + '</div></div>' +
'<div class="hs-qrbox"><div class="q">' + (haveId ? qrSvg(url) : '') + '</div><div class="scan">SCAN ME 📷</div><div class="hs-link">' + shortUrl + '</div></div></div>' +
'<div class="hs-foot"><p class="hs-disc">Independent team resource. Participation takes real effort and involves cryptocurrency risk. No income is guaranteed. Learn more at rmcircle.team.</p></div>';
}
function buildHalf(id) {
var host = document.getElementById('halfsheets');
if (!host) return;
host.innerHTML = '';
var haveId = /^\d{1,15}$/.test(id);
var idLabel = haveId ? id : '____';
HALF.forEach(function (f) {
var url = 'https://rmcircle.team/join/' + (haveId ? id : '') + (f.angle ? '?v=' + f.angle : '');
var shortUrl = 'rmcircle.team/join/' + idLabel;
var imgFile = '/handout-' + (f.angle || 'stop-waiting') + '.png';
var page = el('div', 'hs-page');
var card = el('div', 'hs ' + f.theme, halfSheetHtml(f, url, shortUrl, haveId));
var pr = el('button', 'hs-print btn btn-teal btn-sm', '🖨 Print (2 per page)');
var card = el('div', 'hs hs-img-bg');
card.style.backgroundImage = 'url(' + imgFile + ')';
card.style.backgroundSize = 'cover';
card.style.backgroundPosition = 'center';
card.style.position = 'relative';
var canvas = el('canvas');
canvas.width = 1700;
canvas.height = 1100;
canvas.className = 'hs-canvas';
canvas.style.position = 'absolute';
canvas.style.top = '0';
canvas.style.left = '0';
canvas.style.zIndex = '1';
card.appendChild(canvas);
var pr = el('button', 'hs-print btn btn-teal btn-sm', 'Print (2 per page)');
pr.style.position = 'relative';
pr.style.zIndex = '2';
card.appendChild(pr);
// Render QR on canvas
(function(canvas, url, shortUrl) {
var renderQr = function() {
var ctx = canvas.getContext('2d');
if (!ctx) return;
ctx.fillStyle = 'rgba(30, 30, 40, 0.6)';
ctx.fillRect(1020, 550, 680, 550);
var qrSize = 240;
var qrX = 1700 - qrSize - 40;
var qrY = 1100 - qrSize - 100;
ctx.fillStyle = '#ffffff';
ctx.fillRect(qrX - 10, qrY - 10, qrSize + 20, qrSize + 20);
ctx.strokeStyle = '#000';
ctx.lineWidth = 3;
ctx.strokeRect(qrX - 10, qrY - 10, qrSize + 20, qrSize + 20);
if (url.indexOf('join/') !== -1) {
var qrSvgStr = qrSvg(url);
if (qrSvgStr) {
var tempDiv = document.createElement('div');
tempDiv.innerHTML = qrSvgStr;
var svg = tempDiv.querySelector('svg');
if (svg) {
var svgData = new XMLSerializer().serializeToString(svg);
var img = new Image();
img.onload = function() {
ctx.drawImage(img, qrX, qrY, qrSize, qrSize);
drawLink();
};
img.onerror = drawLink;
img.src = 'data:image/svg+xml;base64,' + btoa(svgData);
} else { drawLink(); }
} else { drawLink(); }
} else { drawLink(); }
function drawLink() {
ctx.font = 'bold 20px Arial';
ctx.fillStyle = '#ffffff';
ctx.textAlign = 'center';
ctx.shadowColor = 'rgba(0,0,0,0.8)';
ctx.shadowBlur = 4;
ctx.shadowOffsetX = 2;
ctx.shadowOffsetY = 2;
ctx.fillText(shortUrl, 1700 - 120, 1100 - 30);
}
};
setTimeout(renderQr, 100);
})(canvas, url, shortUrl);
page.appendChild(card);
page.appendChild(el('div', 'hs-cut', '✂ &nbsp;cut here&nbsp; ✂'));
page.appendChild(el('div', 'hs hs-copy ' + f.theme, halfSheetHtml(f, url, shortUrl, haveId)));
page.appendChild(el('div', 'hs-cut', 'cut here'));
var card2 = el('div', 'hs hs-img-bg hs-copy');
card2.style.backgroundImage = 'url(' + imgFile + ')';
card2.style.backgroundSize = 'cover';
card2.style.backgroundPosition = 'center';
card2.style.position = 'relative';
var canvas2 = el('canvas');
canvas2.width = 1700;
canvas2.height = 1100;
canvas2.style.position = 'absolute';
canvas2.style.top = '0';
canvas2.style.left = '0';
card2.appendChild(canvas2);
(function(canvas, url, shortUrl) {
var renderQr = function() {
var ctx = canvas.getContext('2d');
if (!ctx) return;
ctx.fillStyle = 'rgba(30, 30, 40, 0.6)';
ctx.fillRect(1020, 550, 680, 550);
var qrSize = 240;
var qrX = 1700 - qrSize - 40;
var qrY = 1100 - qrSize - 100;
ctx.fillStyle = '#ffffff';
ctx.fillRect(qrX - 10, qrY - 10, qrSize + 20, qrSize + 20);
ctx.strokeStyle = '#000';
ctx.lineWidth = 3;
ctx.strokeRect(qrX - 10, qrY - 10, qrSize + 20, qrSize + 20);
if (url.indexOf('join/') !== -1) {
var qrSvgStr = qrSvg(url);
if (qrSvgStr) {
var tempDiv = document.createElement('div');
tempDiv.innerHTML = qrSvgStr;
var svg = tempDiv.querySelector('svg');
if (svg) {
var svgData = new XMLSerializer().serializeToString(svg);
var img = new Image();
img.onload = function() {
ctx.drawImage(img, qrX, qrY, qrSize, qrSize);
drawLink();
};
img.onerror = drawLink;
img.src = 'data:image/svg+xml;base64,' + btoa(svgData);
} else { drawLink(); }
} else { drawLink(); }
} else { drawLink(); }
function drawLink() {
ctx.font = 'bold 20px Arial';
ctx.fillStyle = '#ffffff';
ctx.textAlign = 'center';
ctx.shadowColor = 'rgba(0,0,0,0.8)';
ctx.shadowBlur = 4;
ctx.shadowOffsetX = 2;
ctx.shadowOffsetY = 2;
ctx.fillText(shortUrl, 1700 - 120, 1100 - 30);
}
};
setTimeout(renderQr, 100);
})(canvas2, url, shortUrl);
page.appendChild(card2);
pr.addEventListener('click', function () {
document.body.classList.add('print-one'); page.classList.add('printing');
window.print();
setTimeout(function () { document.body.classList.remove('print-one'); page.classList.remove('printing'); }, 400);
});
host.appendChild(page);
});
}