overte-www/tablet/card-face.html

123 lines
3.2 KiB
HTML
Raw Normal View History

2026-03-20 08:02:39 +00:00
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<style>
* { margin: 0; padding: 0; }
body { background: transparent; overflow: hidden; }
canvas { display: block; }
</style>
</head>
<body>
<canvas id="c"></canvas>
<script>
var canvas = document.getElementById('c');
var ctx = canvas.getContext('2d');
var TEX_SIZE = 512;
canvas.width = TEX_SIZE;
canvas.height = TEX_SIZE;
// UV islands span Y: 0.15 to 0.85 (centered, 70% of texture height)
var CARD_TOP = Math.round(TEX_SIZE * 0.15); // 77px
var CARD_BOTTOM = Math.round(TEX_SIZE * 0.85); // 435px
var CARD_H = CARD_BOTTOM - CARD_TOP; // 358px
var CARD_W = TEX_SIZE / 2; // 256px each
var SUITS = { '♠': '#1a1a2e', '♣': '#1a1a2e', '♥': '#cc2222', '♦': '#cc2222' };
function drawCard(xOffset, rank, suit) {
var col = SUITS[suit] || '#1a1a2e';
var w = CARD_W;
var h = CARD_H;
var r = 10;
var p = 12;
ctx.save();
ctx.translate(xOffset, CARD_TOP);
// Card background
ctx.beginPath();
ctx.moveTo(r, 0);
ctx.lineTo(w - r, 0);
ctx.quadraticCurveTo(w, 0, w, r);
ctx.lineTo(w, h - r);
ctx.quadraticCurveTo(w, h, w - r, h);
ctx.lineTo(r, h);
ctx.quadraticCurveTo(0, h, 0, h - r);
ctx.lineTo(0, r);
ctx.quadraticCurveTo(0, 0, r, 0);
ctx.closePath();
ctx.fillStyle = '#ffffff';
ctx.fill();
ctx.strokeStyle = '#d0d0d0';
ctx.lineWidth = 1;
ctx.stroke();
ctx.fillStyle = col;
// Top-left rank
ctx.font = 'bold 36px Georgia, serif';
ctx.textBaseline = 'top';
ctx.textAlign = 'left';
ctx.fillText(rank, p, p);
// Suit below rank top-left
ctx.font = '26px Georgia, serif';
ctx.fillText(suit, p, p + 40);
// Bottom-right flipped
ctx.save();
ctx.translate(w, h);
ctx.rotate(Math.PI);
ctx.font = 'bold 36px Georgia, serif';
ctx.textBaseline = 'top';
ctx.textAlign = 'left';
ctx.fillText(rank, p, p);
ctx.font = '26px Georgia, serif';
ctx.fillText(suit, p, p + 40);
ctx.restore();
// Center suit
ctx.font = '110px Georgia, serif';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillText(suit, w / 2, h / 2);
ctx.restore();
}
function render(card1, card2) {
// Clear with transparent background
ctx.clearRect(0, 0, TEX_SIZE, TEX_SIZE);
drawCard(0, card1.rank, card1.suit);
drawCard(CARD_W, card2.rank, card2.suit);
var dataURI = canvas.toDataURL('image/png');
if (typeof EventBridge !== 'undefined') {
EventBridge.emitWebEvent(JSON.stringify({
type: 'cardTexture',
dataURI: dataURI,
}));
}
}
if (typeof EventBridge !== 'undefined') {
EventBridge.scriptEventReceived.connect(function(data) {
try {
var msg = JSON.parse(data);
if (msg.type === 'dealCards') {
render(msg.card1, msg.card2);
} else if (msg.type === 'ready') {
EventBridge.emitWebEvent(JSON.stringify({ type: 'rendererReady' }));
}
} catch(e) {}
});
// Signal ready on load
EventBridge.emitWebEvent(JSON.stringify({ type: 'rendererReady' }));
}
</script>
</body>
</html>