Archived
326 lines
13 KiB
JavaScript
326 lines
13 KiB
JavaScript
let testData = null, answers = [], currentQ = 0, myResult = null;
|
||
async function init() {
|
||
try {
|
||
const r = await fetch('/api/math_personality');
|
||
const j = await r.json();
|
||
if (j.code !== 0) { showToast('测试数据加载失败'); return; }
|
||
testData = j.data;
|
||
renderIntro();
|
||
} catch (e) {
|
||
document.getElementById('introScreen').innerHTML =
|
||
'<div style="text-align:center;padding:60px 20px"><p style="color:#94A3B8;margin-bottom:12px">加载失败,请检查网络</p><button class="btn-start" onclick="init()">重试</button></div>';
|
||
}
|
||
}
|
||
function renderIntro() {
|
||
const axesEl = document.getElementById('introAxes');
|
||
if (axesEl && testData.axes) {
|
||
axesEl.innerHTML = testData.axes.map(a =>
|
||
'<div class="intro-axis-item">' +
|
||
'<span class="axis-emoji">' + (a.emoji_left || '') + '</span>' +
|
||
'<span class="axis-label">' + a.name + '</span>' +
|
||
'<span class="axis-emoji">' + (a.emoji_right || '') + '</span>' +
|
||
'</div>'
|
||
).join('');
|
||
}
|
||
document.querySelector('.intro-meta').textContent =
|
||
'⏱ 约2分钟 · 4选项 · 16种人格';
|
||
}
|
||
function startQuiz() {
|
||
answers = new Array(testData.questions.length).fill(-1);
|
||
currentQ = 0;
|
||
switchScreen('quizScreen');
|
||
renderQuestion();
|
||
}
|
||
function renderQuestion() {
|
||
const q = testData.questions[currentQ];
|
||
document.getElementById('quizQuestion').textContent = q.question;
|
||
const optEl = document.getElementById('quizOptions');
|
||
const sel = answers[currentQ];
|
||
optEl.innerHTML = q.options.map((o, i) =>
|
||
'<label class="quiz-option' + (sel === i ? ' selected' : '') + '" onclick="selectOption(' + i + ')">' +
|
||
'<input type="radio" name="q' + currentQ + '" value="' + i + '"' + (sel === i ? ' checked' : '') + '>' +
|
||
'<span class="opt-text">' + o.text + '</span></label>'
|
||
).join('');
|
||
const pct = Math.round((currentQ + 1) / testData.questions.length * 100);
|
||
document.getElementById('progressFill').style.width = pct + '%';
|
||
const isLast=currentQ===testData.questions.length-1;
|
||
document.getElementById('progressFill').style.setProperty('--is-last',isLast?'1':'0');
|
||
document.getElementById('progressText').textContent = (currentQ + 1) + '/' + testData.questions.length;
|
||
const btn = document.getElementById('btnNext');
|
||
btn.disabled = answers[currentQ] === -1;
|
||
btn.textContent = currentQ === testData.questions.length - 1 ? '查看结果' : '下一题';
|
||
}
|
||
function selectOption(idx) {
|
||
answers[currentQ] = idx;
|
||
document.getElementById('btnNext').disabled = false;
|
||
document.querySelectorAll('.quiz-option').forEach((el, i) => {
|
||
el.classList.toggle('selected', i === idx);
|
||
});
|
||
}
|
||
async function nextQuestion() {
|
||
if (answers[currentQ] === -1) return;
|
||
if (currentQ < testData.questions.length - 1) {
|
||
currentQ++;
|
||
renderQuestion();
|
||
return;
|
||
}
|
||
const btn = document.getElementById('btnNext');
|
||
btn.disabled = true;
|
||
btn.textContent = '计算中…';
|
||
try {
|
||
const r = await fetch('/api/math_personality/calculate', {
|
||
method: 'POST',
|
||
headers: { 'Content-Type': 'application/json' },
|
||
body: JSON.stringify({ answers })
|
||
});
|
||
const j = await r.json();
|
||
if (j.code !== 0) { showToast('计算结果失败'); return; }
|
||
myResult = j.data;
|
||
const mbtiCode=j.data.binary_code;
|
||
let gotNewCard=false;
|
||
if(mbtiCode){
|
||
let cardState2=JSON.parse(localStorage.getItem('hulu_card_state')||'{}');
|
||
if(!cardState2.owned)cardState2.owned=[];
|
||
if(!cardState2.drawHistory)cardState2.drawHistory=[];
|
||
if(cardState2.owned.indexOf(mbtiCode)===-1){
|
||
cardState2.owned.push(mbtiCode);
|
||
cardState2.drawHistory.push({code:mbtiCode,date:new Date().toISOString().slice(0,10),method:'mathbti'});
|
||
localStorage.setItem('hulu_card_state',JSON.stringify(cardState2));
|
||
gotNewCard=true;
|
||
}
|
||
}
|
||
renderResult();
|
||
if(gotNewCard){
|
||
const res=j.data.result;
|
||
const notice=document.getElementById('cardNotice');
|
||
if(notice){notice.style.display='block';document.getElementById('cardNoticeName').textContent=res?res.name:'—'}
|
||
showToast('🎴 获得数学人物小卡!前往葫芦数学查看卡册');
|
||
}
|
||
switchScreen('resultScreen');
|
||
} catch (e) {
|
||
showToast('提交失败,请重试');
|
||
btn.disabled = false;
|
||
btn.textContent = currentQ === testData.questions.length - 1 ? '查看结果' : '下一题';
|
||
}
|
||
}
|
||
function renderResult() {
|
||
const res = myResult.result;
|
||
if (!res) return;
|
||
const portraitEl = document.getElementById('resultPortrait');
|
||
if (res.portrait) {
|
||
portraitEl.innerHTML = '<div class="result-portrait" style="background:' + (res.icon_bg || '#F0F0F0') + '"><img src="' + res.portrait + '" alt="" style="width:100%;height:100%;object-fit:cover;border-radius:50%" onerror="this.style.display=\'none\';this.nextElementSibling.style.display=\'flex\'"><span class="result-icon-fallback" style="display:none;font-size:48px">' + (res.icon || 'Σ') + '</span></div>';
|
||
} else {
|
||
portraitEl.innerHTML = '<div class="result-portrait" style="background:' + (res.icon_bg || '#F0F0F0') + '"><span class="result-icon-fallback" style="font-size:48px">' + (res.icon || 'Σ') + '</span></div>';
|
||
}
|
||
document.getElementById('resultName').innerHTML = res.name + ' <span class="type-code" style="font-size:13px;color:#94A3B8;font-weight:400">' + myResult.binary_code + '</span>';
|
||
document.getElementById('resultTagline').textContent = res.tagline || '';
|
||
const clanData = testData.clans[res.clan];
|
||
document.getElementById('resultMathematician').innerHTML =
|
||
(clanData ? '<span class="clan-badge" style="background:' + (clanData.color || '#888') + '15;color:' + (clanData.color || '#888') + ';border:1px solid ' + (clanData.color || '#888') + '30">' + clanData.emoji + ' ' + clanData.name + ' · ' + res.mathematician + '</span>' : '') +
|
||
'<span class="mathematician-name">代表数学家:' + (res.mathematician || '—') + '</span>';
|
||
const kwEl = document.getElementById('resultKeywords');
|
||
if (res.keywords) {
|
||
kwEl.innerHTML = res.keywords.map(k => '<span class="kw-tag">' + k + '</span>').join('');
|
||
kwEl.style.display = 'flex';
|
||
} else {
|
||
kwEl.style.display = 'none';
|
||
}
|
||
document.getElementById('resultDesc').textContent = res.description || '';
|
||
document.getElementById('resultShort').textContent = res.short_desc || '';
|
||
document.getElementById('bioTitle').textContent = '关于 ' + (res.mathematician || '这位数学家');
|
||
document.getElementById('bioText').textContent = res.bio || '';
|
||
document.getElementById('bioFact').textContent = res.bio_fact || '';
|
||
const axesEl = document.getElementById('resultAxes');
|
||
if (testData.axes) {
|
||
axesEl.innerHTML = testData.axes.map(a => {
|
||
const score = myResult.axis_scores[a.id] || 0;
|
||
const maxScore = testData.scoring.max_per_axis || 9;
|
||
const pct = Math.round(score / maxScore * 100);
|
||
return '<div class="axis-bar-row">' +
|
||
'<span class="axis-bar-label">' + (a.emoji_left || '') + ' ' + a.left + '</span>' +
|
||
'<div class="axis-bar-track"><div class="axis-bar-fill" style="width:' + pct + '%"></div></div>' +
|
||
'<span class="axis-bar-label">' + a.right + ' ' + (a.emoji_right || '') + '</span>' +
|
||
'</div>';
|
||
}).join('');
|
||
axesEl.style.display = 'block';
|
||
}
|
||
const petData = JSON.parse(localStorage.getItem('mb_pet') || '{}');
|
||
const adoptBtn = document.querySelector('.btn-adopt');
|
||
if (petData.type) {
|
||
adoptBtn.textContent = '✅ 已领养 · 我的数学精灵';
|
||
adoptBtn.classList.add('adopted');
|
||
}
|
||
if (clanData) {
|
||
document.getElementById('resultScreen').style.setProperty('--clan-color', clanData.color);
|
||
}
|
||
}
|
||
function toggleBio() {
|
||
const body = document.getElementById('bioBody');
|
||
const arrow = document.getElementById('bioArrow');
|
||
const expanded = body.style.maxHeight && body.style.maxHeight !== '0px';
|
||
if (expanded) {
|
||
body.style.maxHeight = '0px';
|
||
arrow.textContent = '▾';
|
||
} else {
|
||
body.style.maxHeight = body.scrollHeight + 'px';
|
||
arrow.textContent = '▴';
|
||
}
|
||
}
|
||
function adoptPet() {
|
||
if (!myResult) return;
|
||
const res = myResult.result;
|
||
const pet = {
|
||
type: res.name || '—',
|
||
icon: res.icon || 'Σ',
|
||
color: res.color || '#5B6AF0',
|
||
mathematician: res.mathematician || '',
|
||
clan: res.clan || '',
|
||
binary_code: myResult.binary_code
|
||
};
|
||
localStorage.setItem('mb_pet', JSON.stringify(pet));
|
||
const btn = document.querySelector('.btn-adopt');
|
||
btn.textContent = '✅ 已领养 · 我的数学精灵';
|
||
btn.classList.add('adopted');
|
||
showToast('🧬 数学精灵已领养!每天来葫芦数学 App 喂它成长');
|
||
}
|
||
function copyShare() {
|
||
if (!myResult) return;
|
||
const res = myResult.result;
|
||
const clanData = testData.clans[res.clan];
|
||
const text = '我的数学人格是「' + res.name + '」' + (res.icon || '') + '\n' +
|
||
(clanData ? clanData.emoji + ' ' + clanData.name + ' · ' + res.mathematician : '') + '\n' +
|
||
(res.short_desc || '') + '\n\n' +
|
||
'👉 你也来测测:' + window.location.href + '\n\n' +
|
||
'#数学人格测试 #MathBTI';
|
||
navigator.clipboard.writeText(text).then(() => showToast('已复制分享文案!'));
|
||
}
|
||
function shareResult() {
|
||
if (!myResult) return;
|
||
const res = myResult.result;
|
||
const text = '我的数学人格是「' + res.name + '」' + (res.icon || '') + '!' + (res.short_desc || '');
|
||
if (navigator.share) {
|
||
navigator.share({ title: 'MathBTI · 数学人格测试', text: text, url: window.location.href }).catch(() => {});
|
||
} else {
|
||
copyShare();
|
||
}
|
||
}
|
||
function generateShareCard() {
|
||
if (!myResult) return;
|
||
const res = myResult.result;
|
||
const clanData = testData.clans[res.clan];
|
||
const canvas = document.getElementById('shareCardCanvas');
|
||
const ctx = canvas.getContext('2d');
|
||
canvas.width = 600;
|
||
canvas.height = 800;
|
||
const gradient = ctx.createLinearGradient(0, 0, 600, 800);
|
||
gradient.addColorStop(0, (res.color || '#5B6AF0') + '22');
|
||
gradient.addColorStop(1, (res.color || '#5B6AF0') + '08');
|
||
ctx.fillStyle = '#FAFBFC';
|
||
ctx.fillRect(0, 0, 600, 800);
|
||
ctx.fillStyle = gradient;
|
||
ctx.fillRect(0, 0, 600, 800);
|
||
ctx.fillStyle = res.color || '#5B6AF0';
|
||
ctx.fillRect(0, 0, 600, 6);
|
||
ctx.fillStyle = res.icon_bg || '#F0F0F0';
|
||
ctx.beginPath();
|
||
ctx.arc(300, 180, 70, 0, Math.PI * 2);
|
||
ctx.fill();
|
||
ctx.fillStyle = '#334155';
|
||
ctx.font = '60px sans-serif';
|
||
ctx.textAlign = 'center';
|
||
ctx.textBaseline = 'middle';
|
||
ctx.fillText(res.icon || 'Σ', 300, 180);
|
||
ctx.fillStyle = '#1E293B';
|
||
ctx.font = 'bold 36px "PingFang SC", sans-serif';
|
||
ctx.fillText(res.name, 300, 280);
|
||
ctx.fillStyle = '#64748B';
|
||
ctx.font = '18px "PingFang SC", sans-serif';
|
||
if (clanData) {
|
||
ctx.fillText(clanData.emoji + ' ' + clanData.name + ' · ' + res.mathematician, 300, 315);
|
||
}
|
||
ctx.fillStyle = res.color || '#5B6AF0';
|
||
ctx.font = 'italic 15px "PingFang SC", sans-serif';
|
||
const tagline = res.tagline || '';
|
||
wrapText(ctx, tagline, 300, 350, 480, 22);
|
||
ctx.fillStyle = '#E2E8F0';
|
||
ctx.fillRect(60, 380, 480, 1);
|
||
ctx.fillStyle = '#334155';
|
||
ctx.font = '15px "PingFang SC", sans-serif';
|
||
const desc = res.short_desc || '';
|
||
wrapText(ctx, desc, 300, 410, 480, 24);
|
||
let ay = 450;
|
||
if (testData.axes && myResult.axis_scores) {
|
||
ctx.fillStyle = '#64748B';
|
||
ctx.font = '14px "PingFang SC", sans-serif';
|
||
testData.axes.forEach(a => {
|
||
const s = myResult.axis_scores[a.id] || 0;
|
||
const maxS = testData.scoring.max_per_axis || 9;
|
||
const pct = Math.round(s / maxS * 100);
|
||
ctx.fillText(a.emoji_left + ' ' + a.left + ' — ' + pct + '% — ' + a.right + ' ' + a.emoji_right, 300, ay);
|
||
ay += 28;
|
||
});
|
||
}
|
||
ctx.fillStyle = '#94A3B8';
|
||
ctx.font = '13px "PingFang SC", sans-serif';
|
||
ctx.fillText('葫芦数学 · MathBTI', 300, 730);
|
||
ctx.save();ctx.globalAlpha=.06;ctx.font='80px sans-serif';ctx.fillStyle='#A8907E';
|
||
ctx.fillText('🏮',480,680);
|
||
ctx.restore();
|
||
ctx.fillText('你也来测测你的数学人格类型 →', 300, 755);
|
||
ctx.fillStyle = '#E2E8F0';
|
||
ctx.fillRect(255, 640, 90, 90);
|
||
ctx.fillStyle = '#94A3B8';
|
||
ctx.font = '11px sans-serif';
|
||
ctx.fillText('扫码测', 300, 685);
|
||
document.getElementById('shareCardPreview').style.display = 'block';
|
||
document.getElementById('shareCardPreview').scrollIntoView({ behavior: 'smooth' });
|
||
}
|
||
function downloadShareCard() {
|
||
const canvas = document.getElementById('shareCardCanvas');
|
||
const link = document.createElement('a');
|
||
link.download = 'mathbti_' + (myResult ? myResult.binary_code : 'result') + '.png';
|
||
link.href = canvas.toDataURL('image/png');
|
||
link.click();
|
||
showToast('📸 图片已保存!');
|
||
}
|
||
function wrapText(ctx, text, x, y, maxWidth, lineHeight) {
|
||
const lines = [];
|
||
let current = '';
|
||
for (let i = 0; i < text.length; i++) {
|
||
const test = current + text[i];
|
||
if (ctx.measureText(test).width > maxWidth && current.length > 0) {
|
||
lines.push(current);
|
||
current = text[i];
|
||
} else {
|
||
current = test;
|
||
}
|
||
}
|
||
if (current) lines.push(current);
|
||
lines.forEach((line, idx) => {
|
||
ctx.fillText(line, x, y + idx * lineHeight);
|
||
});
|
||
}
|
||
function retryQuiz() {
|
||
answers = [];
|
||
currentQ = 0;
|
||
myResult = null;
|
||
document.getElementById('bioBody').style.maxHeight = '0px';
|
||
document.getElementById('bioArrow').textContent = '▾';
|
||
document.getElementById('shareCardPreview').style.display = 'none';
|
||
switchScreen('introScreen');
|
||
}
|
||
function switchScreen(id) {
|
||
document.querySelectorAll('.screen').forEach(s => s.classList.remove('active'));
|
||
document.getElementById(id).classList.add('active');
|
||
window.scrollTo({ top: 0, behavior: 'smooth' });
|
||
}
|
||
let toastTimer = null;
|
||
function showToast(msg) {
|
||
const el = document.getElementById('toast');
|
||
el.textContent = msg;
|
||
el.classList.add('show');
|
||
clearTimeout(toastTimer);
|
||
toastTimer = setTimeout(() => el.classList.remove('show'), 2200);
|
||
}
|
||
document.addEventListener('DOMContentLoaded', init);
|