| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205 |
- /**
- * DesignCanvas — 变体并排网格布局
- *
- * 用于展示2+个静态设计variations让用户对比选择。
- * 每个variation有label,可hover放大。
- *
- * 用法:
- * <DesignCanvas
- * title="Hero区设计探索"
- * subtitle="3个方向对比"
- * columns={3}
- * >
- * <Variation label="Minimal" description="极简克制版">
- * <div>...你的设计1...</div>
- * </Variation>
- * <Variation label="Editorial" description="杂志编辑风">
- * <div>...你的设计2...</div>
- * </Variation>
- * <Variation label="Brutalist" description="粗粝原始">
- * <div>...你的设计3...</div>
- * </Variation>
- * </DesignCanvas>
- *
- * 配合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 (
- <div style={canvasStyles.container}>
- {(title || subtitle) && (
- <div style={canvasStyles.header}>
- {title && <h1 style={canvasStyles.title}>{title}</h1>}
- {subtitle && <p style={canvasStyles.subtitle}>{subtitle}</p>}
- </div>
- )}
- <div style={gridStyle}>
- {React.Children.map(children, (child, idx) =>
- React.isValidElement(child)
- ? React.cloneElement(child, {
- _index: idx,
- _expanded: expanded === idx,
- _onToggle: () => setExpanded(expanded === idx ? null : idx),
- })
- : child
- )}
- </div>
- {expanded !== null && (
- <div
- onClick={() => 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',
- }}
- >
- <div
- onClick={e => e.stopPropagation()}
- style={{
- background: '#fff',
- borderRadius: 8,
- overflow: 'hidden',
- maxWidth: '90vw',
- maxHeight: '90vh',
- position: 'relative',
- }}
- >
- {React.Children.toArray(children)[expanded]}
- </div>
- </div>
- )}
- </div>
- );
- }
- function Variation({ label, description, number, children, _index, _expanded, _onToggle, aspectRatio = '4 / 3' }) {
- const displayNumber = number || String(_index + 1).padStart(2, '0');
- return (
- <div style={canvasStyles.cell}>
- <div style={canvasStyles.cellHeader}>
- <span style={{ ...canvasStyles.label, color: '#999', fontFamily: 'ui-monospace, monospace', fontSize: 12 }}>
- {displayNumber}
- </span>
- <span style={canvasStyles.label}>{label}</span>
- {description && <span style={canvasStyles.description}>— {description}</span>}
- </div>
- <div
- onClick={_onToggle}
- style={{
- ...canvasStyles.frame,
- aspectRatio,
- }}
- onMouseEnter={e => {
- e.currentTarget.style.boxShadow = '0 8px 24px rgba(0,0,0,0.08)';
- }}
- onMouseLeave={e => {
- e.currentTarget.style.boxShadow = 'none';
- }}
- >
- <div style={canvasStyles.frameInner}>
- {children}
- </div>
- </div>
- </div>
- );
- }
- if (typeof window !== 'undefined') {
- Object.assign(window, { DesignCanvas, Variation });
- }
|