346 lines
14 KiB
HTML
346 lines
14 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="zh-CN">
|
|
<head>
|
|
<!-- Google tag (gtag.js) -->
|
|
<script async src="https://www.googletagmanager.com/gtag/js?id=G-LVVXH3TL04"></script>
|
|
<script>
|
|
window.dataLayer = window.dataLayer || [];
|
|
function gtag(){dataLayer.push(arguments);}
|
|
gtag('js', new Date());
|
|
gtag('config', 'G-LVVXH3TL04');
|
|
</script>
|
|
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>数字心理学 · 人生说明书</title>
|
|
<style>
|
|
:root {
|
|
--bg: #faf7f2;
|
|
--text: #3d3226;
|
|
--muted: #8b7355;
|
|
--border: #d5c9b5;
|
|
--gold: #b8943e;
|
|
--gold-light: #f7f0e0;
|
|
--brown: #4a3020;
|
|
--cream: #fdfaf3;
|
|
--rose: #c97b84;
|
|
--ink: #2c2018;
|
|
}
|
|
|
|
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
body {
|
|
font-family: 'PingFang SC', 'Noto Serif SC', 'STSong', 'Microsoft YaHei', serif;
|
|
background: #e8e0d5;
|
|
color: var(--text);
|
|
display: flex;
|
|
justify-content: center;
|
|
align-items: flex-start;
|
|
min-height: 100vh;
|
|
padding: 12px;
|
|
}
|
|
|
|
.poster {
|
|
width: 100%;
|
|
max-width: 420px;
|
|
max-height: 95vh;
|
|
background: var(--cream);
|
|
overflow-y: auto;
|
|
box-shadow: 0 4px 32px rgba(0,0,0,0.1);
|
|
border-radius: 4px;
|
|
background-image:
|
|
repeating-linear-gradient(0deg, transparent, transparent 3px, rgba(139,115,85,0.015) 3px, rgba(139,115,85,0.015) 6px);
|
|
scrollbar-width: none;
|
|
}
|
|
.poster::-webkit-scrollbar { display: none; }
|
|
|
|
.inner { padding: 24px 20px 36px; }
|
|
|
|
/* ─── Header ─── */
|
|
.header { text-align: center; margin-bottom: 20px; }
|
|
.header h1 { font-size: 1.6em; font-weight: 400; letter-spacing: 0.16em; color: var(--brown); margin-bottom: 4px; }
|
|
.header .tags { font-size: 0.62em; color: var(--muted); letter-spacing: 0.06em; display: flex; justify-content: center; gap: 10px; }
|
|
.header .tags span {
|
|
border: 1px solid var(--border); padding: 2px 10px; border-radius: 10px;
|
|
background: rgba(255,255,255,0.5);
|
|
}
|
|
|
|
.divider { border: none; border-top: 1px solid var(--border); margin: 16px 0; opacity: 0.5; }
|
|
.divider-dot { border: none; border-top: 1px dotted var(--border); margin: 12px 0; }
|
|
|
|
/* Section title */
|
|
.sec-title {
|
|
text-align: center; font-size: 0.8em; font-weight: 600;
|
|
letter-spacing: 0.1em; color: var(--brown); margin: 16px 0 10px;
|
|
}
|
|
|
|
/* Triangle wrapper */
|
|
.tri-wrap { display: flex; justify-content: center; margin: 8px 0 12px; }
|
|
.tri-wrap svg { display: block; max-width: 100%; }
|
|
|
|
.tri-bottom { text-align: center; font-size: 0.72em; color: var(--muted); letter-spacing: 0.06em; margin: -4px 0 8px; }
|
|
|
|
/* Missing */
|
|
.missing { text-align: center; font-size: 0.7em; line-height: 1.7; }
|
|
.missing .chip {
|
|
display: inline-block; min-width: 20px; height: 20px; line-height: 20px;
|
|
border-radius: 50%; background: var(--gold-light); color: var(--gold);
|
|
font-weight: 700; font-size: 0.9em; margin: 0 2px;
|
|
}
|
|
.missing-short { text-align: center; font-size: 0.74em; color: var(--rose); margin-top: 2px; }
|
|
|
|
/* Code lines */
|
|
.codes { font-size: 0.7em; line-height: 1.8; padding: 0 4px; }
|
|
.codes .r { display: flex; justify-content: space-between; border-bottom: 1px dotted rgba(139,115,85,0.12); padding: 2px 4px; }
|
|
.codes .k { color: var(--muted); font-weight: 500; }
|
|
.codes .v { color: var(--text); }
|
|
|
|
/* Career */
|
|
.career { text-align: center; font-size: 0.7em; margin-top: 8px; }
|
|
.career .ct { font-weight: 600; color: var(--brown); }
|
|
.career .ci { display: inline-block; border: 1px solid var(--border); border-radius: 10px; padding: 2px 10px; margin: 2px; background: rgba(255,255,255,0.5); }
|
|
|
|
/* Joint codes */
|
|
.joint { margin-top: 4px; }
|
|
.joint-age { font-size: 0.66em; color: var(--gold); font-weight: 600; text-align: center; margin: 10px 0 4px; border-bottom: 1px solid var(--gold-light); padding-bottom: 2px; }
|
|
.joint-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 3px 10px; font-size: 0.64em; padding: 0 4px; }
|
|
.joint-grid .jc { font-weight: 700; color: var(--brown); }
|
|
.joint-grid .jm { color: var(--muted); }
|
|
|
|
/* Flow label */
|
|
.flow-label { text-align: center; }
|
|
.flow-label .big { font-size: 0.95em; font-weight: 600; color: var(--brown); letter-spacing: 0.08em; }
|
|
.flow-label .small { font-size: 0.63em; color: var(--muted); letter-spacing: 0.06em; }
|
|
|
|
.footer { text-align: center; font-size: 0.8em; font-weight: 300; letter-spacing: 0.14em; color: var(--muted); margin-top: 20px; padding-top: 14px; border-top: 1px solid var(--border); }
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="poster">
|
|
<div class="inner">
|
|
|
|
<div class="header">
|
|
<h1>人生说明书</h1>
|
|
<div class="tags">
|
|
<span>性格特质|天赋优势</span>
|
|
<span>事业方向|流年运势</span>
|
|
</div>
|
|
</div>
|
|
|
|
<hr class="divider">
|
|
|
|
<!-- ═════ TRIANGLE 1: 出生盘 ═════ -->
|
|
<div class="sec-title">✦ 出生盘 ✦</div>
|
|
<div class="tri-wrap" id="t1"></div>
|
|
<div class="tri-bottom">17 02 1986</div>
|
|
|
|
<div class="missing">
|
|
<div style="font-weight:600;color:var(--brown);margin-bottom:2px;">— 数字缺失 —</div>
|
|
里缺:<span class="chip">3</span><span class="chip">4</span><span class="chip">9</span>
|
|
外缺:<span class="chip">1</span><span class="chip">5</span><span class="chip">6</span><span class="chip">7</span>
|
|
<br>里外缺:无
|
|
</div>
|
|
<div class="missing-short">缺失:行动力、规划能力、认同感</div>
|
|
|
|
<div class="codes">
|
|
<div class="r"><span class="k">主性格</span><span class="v">7 追求真理、敏锐钻研</span></div>
|
|
<div class="r"><span class="k">内心码</span><span class="v">5 固执</span></div>
|
|
<div class="r"><span class="k">外心码</span><span class="v">6 现实主义</span></div>
|
|
<div class="r"><span class="k">潜意识码</span><span class="v">2 外刚内柔</span></div>
|
|
<div class="r"><span class="k">坐镇码</span><span class="v">167 白手起家</span></div>
|
|
</div>
|
|
|
|
<div class="career">
|
|
<div class="ct">适合职业</div>
|
|
<span class="ci">分析科学</span><span class="ci">哲学</span><span class="ci">侦探</span><span class="ci">IT</span>
|
|
<br><span class="ci">学术研究</span><span class="ci">教授</span><span class="ci">推理类</span>
|
|
</div>
|
|
|
|
<hr class="divider-dot">
|
|
|
|
<!-- ═════ TRIANGLE 2: 流年盘 ═════ -->
|
|
<div class="sec-title">✦ 流年盘 ✦</div>
|
|
<div class="tri-wrap" id="t2"></div>
|
|
<div class="flow-label">
|
|
<div class="big">流年:开始年</div>
|
|
<div class="small">新的开始、新的机会</div>
|
|
</div>
|
|
|
|
<hr class="divider">
|
|
|
|
<!-- ═════ 联合码 ═════ -->
|
|
<div class="sec-title">12 组联合码</div>
|
|
|
|
<div class="joint">
|
|
<div class="joint-age">21—40岁 · 工作和朋友</div>
|
|
<div class="joint-grid">
|
|
<div><span class="jc">821</span> <span class="jm">大单销售高手</span></div>
|
|
<div><span class="jc">819</span> <span class="jm">亲力亲为</span></div>
|
|
<div><span class="jc">213</span> <span class="jm">讲师号</span></div>
|
|
<div><span class="jc">933</span> <span class="jm">手艺码</span></div>
|
|
</div>
|
|
|
|
<div class="joint-age">41—60岁 · 儿女和下属</div>
|
|
<div class="joint-grid">
|
|
<div><span class="jc">167</span> <span class="jm">白手起家</span></div>
|
|
<div><span class="jc">178</span> <span class="jm">成就他人</span></div>
|
|
<div><span class="jc">674</span> <span class="jm">天生傲骨</span></div>
|
|
<div><span class="jc">483</span> <span class="jm">专业码</span></div>
|
|
</div>
|
|
|
|
<div class="joint-age">61岁至晚年 · 晚年和家庭</div>
|
|
<div class="joint-grid">
|
|
<div><span class="jc">156</span> <span class="jm">五方求财</span></div>
|
|
<div><span class="jc">167</span> <span class="jm">白手起家</span></div>
|
|
<div><span class="jc">562</span> <span class="jm">学会花钱</span></div>
|
|
<div><span class="jc">729</span> <span class="jm">异性缘</span></div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="footer">内在探索<br>心灵成长</div>
|
|
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
/* ══════════════════════════════════════
|
|
PYRAMID TRIANGLE RENDERER v2
|
|
Clean geometric pyramid with internal grid lines
|
|
══════════════════════════════════════ */
|
|
|
|
const CW = 40; // cell width
|
|
const CH = 36; // cell height
|
|
const RG = 18; // row gap (vertical)
|
|
const PAD = 24;
|
|
|
|
function triad(cx, cellsInRow) {
|
|
const rowW = cellsInRow * CW;
|
|
return cx - rowW / 2;
|
|
}
|
|
|
|
function renderPyramid(elId, layers, edgeL, edgeR, centerNum) {
|
|
/*
|
|
layers = [{nums: [3], highlight: true}, {nums: [4,8]}, {nums: [1,6]}, {nums: [8,2,1,5]}]
|
|
edgeL/edgeR = [num, num, num] or null
|
|
*/
|
|
const nLayers = layers.length;
|
|
const bottomCells = layers[layers.length - 1].nums.length;
|
|
const baseW = bottomCells * CW; // width of bottom row cells
|
|
const marginW = 60; // space for edge numbers
|
|
|
|
const svgW = baseW + marginW * 2;
|
|
const svgH = PAD * 2 + CH * nLayers + RG * (nLayers - 1) + 20;
|
|
|
|
let svg = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ${svgW} ${svgH}" width="${svgW}" height="${svgH}">`;
|
|
|
|
// Triangle outline (large continuous isosceles)
|
|
const topX = svgW / 2;
|
|
const topY = PAD;
|
|
const botLY = PAD + CH * nLayers + RG * (nLayers - 1);
|
|
const tipR = 18; // how far the triangle tip extends above/below the cell
|
|
|
|
// Triangle vertices
|
|
const tx = topX, ty = topY - tipR;
|
|
const lx = marginW - 8, ly = botLY + tipR;
|
|
const rx = svgW - marginW + 8, ry = botLY + tipR;
|
|
|
|
// Draw triangle background fill
|
|
svg += `<polygon points="${tx},${ty} ${lx},${ly} ${rx},${ry}" fill="rgba(200,164,92,0.04)" stroke="var(--ink)" stroke-width="2.5" stroke-linejoin="round"/>`;
|
|
|
|
// Internal horizontal grid lines
|
|
for (let i = 1; i < nLayers; i++) {
|
|
const cellsAbove = layers[i - 1].nums.length;
|
|
const cellsHere = layers[i].nums.length;
|
|
const y = PAD + CH + (i - 1) * (CH + RG) + RG / 2;
|
|
|
|
const topRowW = cellsAbove * CW;
|
|
const botRowW = cellsHere * CW;
|
|
const leftX = topX - topRowW / 2 + (topRowW - botRowW) / 2;
|
|
const rightX = leftX + botRowW;
|
|
|
|
svg += `<line x1="${leftX}" y1="${y}" x2="${rightX}" y2="${y}" stroke="var(--ink)" stroke-width="1" opacity="0.5"/>`;
|
|
}
|
|
|
|
// Vertical dividers within each row
|
|
for (let i = 0; i < nLayers; i++) {
|
|
const cells = layers[i].nums.length;
|
|
const y = PAD + i * (CH + RG);
|
|
const rowW = cells * CW;
|
|
const leftX = topX - rowW / 2;
|
|
for (let j = 1; j < cells; j++) {
|
|
const vx = leftX + j * CW;
|
|
svg += `<line x1="${vx}" y1="${y}" x2="${vx}" y2="${y + CH}" stroke="var(--ink)" stroke-width="1" opacity="0.5"/>`;
|
|
}
|
|
}
|
|
|
|
// Cells
|
|
for (let i = 0; i < nLayers; i++) {
|
|
const cells = layers[i].nums.length;
|
|
const y = PAD + i * (CH + RG);
|
|
const rowW = cells * CW;
|
|
const leftX = topX - rowW / 2;
|
|
for (let j = 0; j < cells; j++) {
|
|
const val = layers[i].nums[j];
|
|
const x = leftX + j * CW;
|
|
const isTop = (i === 0);
|
|
|
|
// Cell background
|
|
if (isTop) {
|
|
svg += `<rect x="${x}" y="${y}" width="${CW}" height="${CH}" fill="var(--ink)" rx="2"/>`;
|
|
svg += `<text x="${x + CW/2}" y="${y + CH/2}" dy="0.35em" text-anchor="middle" fill="#fff" font-size="20" font-weight="700" font-family="inherit">${val}</text>`;
|
|
} else {
|
|
svg += `<rect x="${x}" y="${y}" width="${CW}" height="${CH}" fill="#fff" stroke="var(--ink)" stroke-width="1" rx="2"/>`;
|
|
svg += `<text x="${x + CW/2}" y="${y + CH/2}" dy="0.35em" text-anchor="middle" fill="var(--ink)" font-size="16" font-weight="600" font-family="inherit">${val}</text>`;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Center annotation (between layer 1 and 2) — birth chart only
|
|
if (centerNum != null) {
|
|
const cy = PAD + CH + RG / 2;
|
|
const cw2 = 36, ch2 = 28;
|
|
const cx = topX - cw2 / 2;
|
|
const cyPos = cy - ch2 / 2;
|
|
svg += `<polygon points="${cx + cw2/2},${cyPos - 4} ${cx + cw2 + 4},${cyPos + ch2/2} ${cx + cw2/2},${cyPos + ch2 + 4} ${cx - 4},${cyPos + ch2/2}" fill="var(--ink)" stroke="var(--ink)" stroke-width="1"/>`;
|
|
svg += `<text x="${cx + cw2/2}" y="${cyPos + ch2/2}" dy="0.35em" text-anchor="middle" fill="#fff" font-size="15" font-weight="700" font-family="inherit">${centerNum}</text>`;
|
|
}
|
|
|
|
// Edge numbers (small circles on left/right slopes)
|
|
if (edgeL && edgeL.length > 0) {
|
|
const ex = marginW / 2;
|
|
const topY2 = PAD + CH * 0.5;
|
|
const botY2 = PAD + CH * (nLayers - 1) + RG * (nLayers - 2) + CH * 0.5;
|
|
for (let i = 0; i < edgeL.length; i++) {
|
|
const ey = topY2 + (botY2 - topY2) * i / (edgeL.length - 1);
|
|
svg += `<circle cx="${ex}" cy="${ey}" r="13" fill="#fff" stroke="var(--gold)" stroke-width="1.5"/>`;
|
|
svg += `<text x="${ex}" y="${ey}" dy="0.35em" text-anchor="middle" fill="var(--gold)" font-size="13" font-weight="700" font-family="inherit">${edgeL[i]}</text>`;
|
|
}
|
|
}
|
|
if (edgeR && edgeR.length > 0) {
|
|
const ex = svgW - marginW / 2;
|
|
const topY2 = PAD + CH * 0.5;
|
|
const botY2 = PAD + CH * (nLayers - 1) + RG * (nLayers - 2) + CH * 0.5;
|
|
for (let i = 0; i < edgeR.length; i++) {
|
|
const ey = topY2 + (botY2 - topY2) * i / (edgeR.length - 1);
|
|
svg += `<circle cx="${ex}" cy="${ey}" r="13" fill="#fff" stroke="var(--gold)" stroke-width="1.5"/>`;
|
|
svg += `<text x="${ex}" y="${ey}" dy="0.35em" text-anchor="middle" fill="var(--gold)" font-size="13" font-weight="700" font-family="inherit">${edgeR[i]}</text>`;
|
|
}
|
|
}
|
|
|
|
svg += `</svg>`;
|
|
document.getElementById(elId).innerHTML = svg;
|
|
}
|
|
|
|
// ═══════ RENDER ═══════
|
|
renderPyramid('t1',
|
|
[{nums:[3], highlight:true}, {nums:[4,8]}, {nums:[1,6]}, {nums:[8,2,1,5]}],
|
|
[3,9,3], [9,7,2], 7
|
|
);
|
|
|
|
renderPyramid('t2',
|
|
[{nums:[1], highlight:true}, {nums:[1,9]}, {nums:[8,2]}, {nums:[2,7]}],
|
|
null, null, null
|
|
);
|
|
</script>
|
|
</body>
|
|
</html>
|