/**
* DesignCanvas — 变体并排网格布局
*
* 用于展示2+个静态设计variations让用户对比选择。
* 每个variation有label,可hover放大。
*
* 用法:
*
*
* ...你的设计1...
*
*
* ...你的设计2...
*
*
* ...你的设计3...
*
*
*
* 配合React+Babel使用。放在合适的script里,然后window.DesignCanvas/window.Variation可用。
*/
const canvasStyles = {
container: {
minHeight: '100vh',
background: '#F5F5F0',
padding: '40px 60px',
fontFamily: '-apple-system, "SF Pro Text", "PingFang SC", sans-serif',
},
header: {
marginBottom: 48,
maxWidth: 900,
},
title: {
fontSize: 36,
fontWeight: 600,
marginBottom: 12,
color: '#1A1A1A',
letterSpacing: '-0.02em',
},
subtitle: {
fontSize: 16,
color: '#666',
lineHeight: 1.5,
},
grid: {
display: 'grid',
gap: 32,
},
cell: {
display: 'flex',
flexDirection: 'column',
gap: 12,
},
cellHeader: {
display: 'flex',
alignItems: 'baseline',
gap: 12,
paddingBottom: 8,
borderBottom: '1px solid #E0E0DA',
},
label: {
fontSize: 14,
fontWeight: 600,
color: '#1A1A1A',
letterSpacing: '-0.01em',
},
description: {
fontSize: 13,
color: '#888',
},
frame: {
background: '#fff',
borderRadius: 4,
border: '1px solid #E0E0DA',
overflow: 'hidden',
position: 'relative',
transition: 'transform 0.2s ease, box-shadow 0.2s ease',
cursor: 'pointer',
},
frameInner: {
position: 'relative',
width: '100%',
},
badge: {
position: 'absolute',
top: 12,
left: 12,
background: 'rgba(0, 0, 0, 0.7)',
color: '#fff',
padding: '3px 8px',
borderRadius: 4,
fontSize: 11,
fontWeight: 500,
letterSpacing: '0.5px',
textTransform: 'uppercase',
zIndex: 10,
pointerEvents: 'none',
},
};
function DesignCanvas({ title, subtitle, columns = 3, children }) {
const [expanded, setExpanded] = React.useState(null);
const gridStyle = {
...canvasStyles.grid,
gridTemplateColumns: `repeat(${columns}, 1fr)`,
};
return (
{(title || subtitle) && (
{title &&
{title}
}
{subtitle &&
{subtitle}
}
)}
{React.Children.map(children, (child, idx) =>
React.isValidElement(child)
? React.cloneElement(child, {
_index: idx,
_expanded: expanded === idx,
_onToggle: () => setExpanded(expanded === idx ? null : idx),
})
: child
)}
{expanded !== null && (
setExpanded(null)}
style={{
position: 'fixed',
inset: 0,
background: 'rgba(0, 0, 0, 0.75)',
zIndex: 1000,
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
padding: 40,
cursor: 'zoom-out',
}}
>
e.stopPropagation()}
style={{
background: '#fff',
borderRadius: 8,
overflow: 'hidden',
maxWidth: '90vw',
maxHeight: '90vh',
position: 'relative',
}}
>
{React.Children.toArray(children)[expanded]}
)}
);
}
function Variation({ label, description, number, children, _index, _expanded, _onToggle, aspectRatio = '4 / 3' }) {
const displayNumber = number || String(_index + 1).padStart(2, '0');
return (
{displayNumber}
{label}
{description && — {description}}
{
e.currentTarget.style.boxShadow = '0 8px 24px rgba(0,0,0,0.08)';
}}
onMouseLeave={e => {
e.currentTarget.style.boxShadow = 'none';
}}
>
{children}
);
}
if (typeof window !== 'undefined') {
Object.assign(window, { DesignCanvas, Variation });
}