2025-11-29 16:58:58 +00:00
|
|
|
import { useCallback } from 'react';
|
2025-11-30 15:12:16 +00:00
|
|
|
import html2canvas from 'html2canvas';
|
2025-11-29 16:58:58 +00:00
|
|
|
|
|
|
|
|
export function useScreenshot() {
|
2025-11-30 15:12:16 +00:00
|
|
|
const takeScreenshot = useCallback(async (username: string = 'Explorer') => {
|
|
|
|
|
// 1. Find the container that includes both the Canvas and the HTML overlays (labels)
|
|
|
|
|
const element = document.getElementById('cosmo-scene-container');
|
|
|
|
|
if (!element) {
|
|
|
|
|
console.error('Scene container not found');
|
2025-11-29 16:58:58 +00:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
try {
|
2025-11-30 15:12:16 +00:00
|
|
|
// 2. Use html2canvas to capture the visual composite
|
|
|
|
|
const canvas = await html2canvas(element, {
|
|
|
|
|
backgroundColor: '#000000',
|
|
|
|
|
useCORS: true, // Allow loading cross-origin images (textures)
|
|
|
|
|
logging: false,
|
|
|
|
|
scale: window.devicePixelRatio, // Capture at high resolution
|
|
|
|
|
allowTaint: true, // Allow tainted canvas (might limit editing but okay for saving)
|
|
|
|
|
ignoreElements: (_el) => {
|
|
|
|
|
// Ignore elements that we don't want in the screenshot (if any end up inside)
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
});
|
2025-11-29 16:58:58 +00:00
|
|
|
|
2025-11-30 15:12:16 +00:00
|
|
|
const ctx = canvas.getContext('2d');
|
2025-11-29 16:58:58 +00:00
|
|
|
if (!ctx) return;
|
|
|
|
|
|
2025-11-30 15:12:16 +00:00
|
|
|
const width = canvas.width;
|
|
|
|
|
const height = canvas.height;
|
2025-11-29 16:58:58 +00:00
|
|
|
|
2025-11-30 15:12:16 +00:00
|
|
|
// 3. Add Overlay / Watermark
|
2025-11-29 16:58:58 +00:00
|
|
|
const now = new Date();
|
|
|
|
|
const dateStr = now.toLocaleDateString('zh-CN', { year: 'numeric', month: '2-digit', day: '2-digit' });
|
|
|
|
|
const timeStr = now.toLocaleTimeString('zh-CN', { hour: '2-digit', minute: '2-digit', second: '2-digit' });
|
|
|
|
|
|
2025-11-30 15:12:16 +00:00
|
|
|
// Background gradient for text legibility
|
|
|
|
|
const gradient = ctx.createLinearGradient(0, height - 150, 0, height);
|
2025-11-29 16:58:58 +00:00
|
|
|
gradient.addColorStop(0, 'transparent');
|
2025-11-30 15:12:16 +00:00
|
|
|
gradient.addColorStop(1, 'rgba(0,0,0,0.9)');
|
2025-11-29 16:58:58 +00:00
|
|
|
ctx.fillStyle = gradient;
|
2025-11-30 15:12:16 +00:00
|
|
|
ctx.fillRect(0, height - 200, width, 200);
|
2025-11-29 16:58:58 +00:00
|
|
|
|
2025-11-30 15:12:16 +00:00
|
|
|
// Text Settings
|
|
|
|
|
ctx.shadowColor = 'rgba(0,0,0,0.5)';
|
|
|
|
|
ctx.shadowBlur = 4;
|
|
|
|
|
|
|
|
|
|
// User Name & App Name (Bottom Right)
|
|
|
|
|
ctx.textAlign = 'right';
|
|
|
|
|
|
|
|
|
|
// Nickname@Cosmo
|
2025-11-29 16:58:58 +00:00
|
|
|
ctx.font = 'bold 32px sans-serif';
|
|
|
|
|
ctx.fillStyle = '#ffffff';
|
2025-11-30 15:12:16 +00:00
|
|
|
ctx.fillText(`${username}@Cosmo`, width - 40, height - 60);
|
2025-11-29 16:58:58 +00:00
|
|
|
|
2025-11-30 15:12:16 +00:00
|
|
|
// Subtitle
|
2025-11-29 16:58:58 +00:00
|
|
|
ctx.font = '16px sans-serif';
|
|
|
|
|
ctx.fillStyle = '#aaaaaa';
|
|
|
|
|
ctx.fillText('DEEP SPACE EXPLORER', width - 40, height - 35);
|
|
|
|
|
|
2025-11-30 15:12:16 +00:00
|
|
|
// Date/Time (Bottom Left)
|
2025-11-29 16:58:58 +00:00
|
|
|
ctx.textAlign = 'left';
|
|
|
|
|
ctx.font = 'bold 24px monospace';
|
|
|
|
|
ctx.fillStyle = '#44aaff';
|
|
|
|
|
ctx.fillText(dateStr, 40, height - 60);
|
|
|
|
|
|
|
|
|
|
ctx.font = '18px monospace';
|
|
|
|
|
ctx.fillStyle = '#cccccc';
|
|
|
|
|
ctx.fillText(timeStr, 40, height - 35);
|
|
|
|
|
|
2025-11-30 15:12:16 +00:00
|
|
|
// 4. Trigger Download
|
|
|
|
|
const dataUrl = canvas.toDataURL('image/png');
|
2025-11-29 16:58:58 +00:00
|
|
|
const link = document.createElement('a');
|
|
|
|
|
link.download = `Cosmo_Snapshot_${now.toISOString().slice(0,19).replace(/[:T]/g, '-')}.png`;
|
|
|
|
|
link.href = dataUrl;
|
|
|
|
|
link.click();
|
|
|
|
|
|
|
|
|
|
} catch (err) {
|
|
|
|
|
console.error('Screenshot failed:', err);
|
|
|
|
|
}
|
|
|
|
|
}, []);
|
|
|
|
|
|
|
|
|
|
return { takeScreenshot };
|
2025-11-30 15:12:16 +00:00
|
|
|
}
|