685 lines
26 KiB
JavaScript
685 lines
26 KiB
JavaScript
/**
|
||
* 传承基因测试 - 主逻辑
|
||
* 包含:题目数据、类型数据、答题流程、计分逻辑、雷达图绘制、海报生成
|
||
*/
|
||
|
||
(function () {
|
||
'use strict';
|
||
|
||
// ========================================
|
||
// 题目数据(18题,6个维度,每维度3题)
|
||
// ========================================
|
||
const questions = [
|
||
// 维度1 - 自我觉察力(题1-3)
|
||
'当我感到烦躁时,我能说出自己真正在气什么,而不是只觉得"烦"',
|
||
'我经常反思自己的行为模式,而不是只看到别人的问题',
|
||
'面对冲突时,我能觉察到自己身体的变化(心跳加速、肌肉紧绷等)',
|
||
// 维度2 - 情绪驾驭力(题4-6)
|
||
'在和孩子吵架后,我能让自己冷静下来再去沟通,而不是冷战或翻旧账',
|
||
'当情绪上来时,我能让自己暂停几秒再回应,而不是脱口而出',
|
||
'我很少因为一时冲动说出让自己后悔的话',
|
||
// 维度3 - 关系修复力(题7-9)
|
||
'我发现自己和父母的相处方式,正在不知不觉中复制到我和孩子的关系上',
|
||
'当关系出现问题时,我愿意主动去修复,而不是等对方先改变',
|
||
'我能分辨哪些情绪是自己的,哪些是"替别人承担的"',
|
||
// 维度4 - 传承意识(题10-12)
|
||
'我想过自己的某些性格特点或反应模式,可能是从祖辈那里继承来的',
|
||
'我注意到家族里有些模式在一代代重复(比如某种情绪、某种关系模式)',
|
||
'我相信了解家族的历史和故事,对理解和教育孩子有帮助',
|
||
// 维度5 - 内在驱动力(题13-15)
|
||
'我清楚自己为什么要做现在做的事,而不是随大流或被外界推着走',
|
||
'我觉得自己的人生是自己主动选择的,而不是被安排好的',
|
||
'遇到困难时,我能找到继续前行的内在力量,而不是只靠外部压力',
|
||
// 维度6 - 成长信念(题16-18)
|
||
'我相信不管现在是什么状态,人都可以改变和成长',
|
||
'我愿意为了长远的目标,每天坚持做一点小事',
|
||
'我觉得"慢慢来"不是放弃,而是一种智慧'
|
||
];
|
||
|
||
// 维度名称(用于雷达图标签)
|
||
const dimensionNames = [
|
||
'自我觉察力', '情绪驾驭力', '关系修复力',
|
||
'传承意识', '内在驱动力', '成长信念'
|
||
];
|
||
|
||
// ========================================
|
||
// 8种类型数据
|
||
// ========================================
|
||
const types = {
|
||
awakener: {
|
||
name: '觉醒者',
|
||
icon: 'assets/icons/awakener.png',
|
||
desc: '已走在自我成长路上,觉察力和情绪力都很强',
|
||
quote: '「看见即觉察,觉察即涨心」',
|
||
analysis: '你拥有出色的自我觉察能力,能够清晰地看见自己的情绪和念头。在情绪管理方面,你已经掌握了"知止"的智慧,能在关键时刻停住并选择回应方式。你正在成为自己生活的"第一目击证人",内在诚信正在建立。继续深化修炼,从"看见"走向"止于至善",你将影响整个家族的传承模式。',
|
||
recommend: '你已经走在觉醒的路上,课程将帮你从"看见"走向"止于至善",深化修炼,让美好状态持续。'
|
||
},
|
||
icebreaker: {
|
||
name: '破冰人',
|
||
icon: 'assets/icons/icebreaker.png',
|
||
desc: '正在打破家族旧模式,传承意识和关系修复力突出',
|
||
quote: '「旧地图无法发现新大陆」',
|
||
analysis: '你对家族传承有着深刻的意识,能够看到代际之间传递的模式。你已经开始打破那些不再服务于你的共生剧本,在关系修复方面展现出勇气和行动力。你正在成为家族中"那个不一样的人"。系统学习冰山模型,将帮助你更深入地理解这些模式背后的根源。',
|
||
recommend: '你已开始打破旧模式,课程将带你系统理解冰山下的家族数据,找到真正的破冰点。'
|
||
},
|
||
pauser: {
|
||
name: '知止行者',
|
||
icon: 'assets/icons/pauser.png',
|
||
desc: '情绪驾驭力突出,能在关键时刻停住',
|
||
quote: '「在电光火石间看见自己」',
|
||
analysis: '你拥有强大的情绪驾驭能力,能够在情绪升起的瞬间停住,善用那0.0001秒的间隙。这是非常珍贵的能力——很多人终其一生都在被情绪带着走。不过,你还可以更进一步:从"知止"到"看见",觉察情绪背后的根源。当你能看见情绪从哪里来,你就真正掌握了选择的自由。',
|
||
recommend: '你已能在情绪中停住,课程将帮你看见情绪背后的根源,从"知止"走向"格物致知"。'
|
||
},
|
||
guardian: {
|
||
name: '传承守护者',
|
||
icon: 'assets/icons/guardian.png',
|
||
desc: '传承意识强、成长信念坚定,但行动力还不足',
|
||
quote: '「冰冻三尺非一日之寒,精诚所至金石为开」',
|
||
analysis: '你对家族传承有着深刻的理解和坚定的成长信念,你知道改变需要时间和耐心。你的内在已经有了清晰的认知,但从意识到行动之间还有一段距离。这不是你的问题——冰冻三尺非一日之寒。关键是从最小的行动开始,日拱一卒,让认知慢慢落地为每天的功课。',
|
||
recommend: '你对传承有深刻意识,课程将帮你把意识转化为每天的功课,从"知道"到"做到"。'
|
||
},
|
||
seeker: {
|
||
name: '寻道者',
|
||
icon: 'assets/icons/seeker.png',
|
||
desc: '成长信念强、内在驱动力高,但方向还不够清晰',
|
||
quote: '「通过外面生活现象,找到内在卡点」',
|
||
analysis: '你拥有强大的内在驱动力和坚定的成长信念,这是非常宝贵的品质。你已经准备好改变,也愿意为此投入时间和精力。但有时候你可能感到方向不够清晰——知道要改变,但不知道从哪里开始。这正是"格物致知"的用武之地:通过外面生活现象找到内在卡点,方向自然就会浮现。',
|
||
recommend: '你有强大的成长动力,课程将帮你找到内在卡点,明确方向,从"寻找"到"看见"。'
|
||
},
|
||
sleeper: {
|
||
name: '沉睡者',
|
||
icon: 'assets/icons/sleeper.png',
|
||
desc: '多维度偏低,但成长信念还在,是觉醒的起点',
|
||
quote: '「每个人本自具足,生来就足够好」',
|
||
analysis: '你可能觉得自己在生活的惯性中运转,很多方面都还没有找到方向。但请看到一件重要的事:你的成长信念还在——你相信人可以改变,这本身就是觉醒的种子。很多人连这颗种子都没有。觉醒的第一步,不是做更多,而是"看见"——看见自己的情绪、念头和模式。从"我看见我"开始,一切都会不同。',
|
||
recommend: '觉醒的第一步是看见。课程将从最基础的"我看见我"开始,帮你唤醒内在的力量。'
|
||
},
|
||
relational: {
|
||
name: '关系困者',
|
||
icon: 'assets/icons/relational.png',
|
||
desc: '关系是最大痛点,情绪驾驭和关系修复都较弱',
|
||
quote: '「找回足够好的自己,用它修复生活」',
|
||
analysis: '关系是你当前最大的挑战和突破口。你可能发现自己总是在重复某些关系模式——和父母的、和伴侣的、和孩子的。这些模式不是你的错,它们是家族大数据通过代际传承传递下来的"共生剧本"。好消息是,你已经意识到了问题所在,这就是改变的开始。从"我看见我的妈妈"开始,你将慢慢解开这些关系的结。',
|
||
recommend: '关系是你的突破口,课程将教你从"我看见我的妈妈"开始,修复关系,走出共生剧本。'
|
||
},
|
||
habitual: {
|
||
name: '惯性行者',
|
||
icon: 'assets/icons/habitual.png',
|
||
desc: '在惯性中运转,内在驱动力和自我觉察都较弱',
|
||
quote: '「慢慢来比较快」',
|
||
analysis: '你可能感觉自己像一台被程序化运转的机器——每天做着差不多的事,想着差不多的念头,每一念都来自过去。这不是你的问题,而是每个人都可能陷入的状态,课程中称之为"空心人"或"NPC"。但旧地图无法发现新大陆,看见即觉察,觉察即涨心。当你开始看见自己的惯性,改变就已经发生了。',
|
||
recommend: '看见即觉察,觉察即涨心。课程将帮你走出惯性,找回内在力量,从"NPC"变回自己。'
|
||
}
|
||
};
|
||
|
||
// ========================================
|
||
// 状态管理
|
||
// ========================================
|
||
let currentQuestion = 0; // 当前题目索引(0-17)
|
||
let answers = []; // 答题记录(长度18,每项1-5)
|
||
|
||
// localStorage 键名
|
||
const STORAGE_KEY = 'heritage_gene_test';
|
||
|
||
// ========================================
|
||
// DOM 元素引用
|
||
// ========================================
|
||
const $home = document.getElementById('home');
|
||
const $quiz = document.getElementById('quiz');
|
||
const $loading = document.getElementById('loading');
|
||
const $result = document.getElementById('result');
|
||
const $poster = document.getElementById('poster');
|
||
|
||
const $progressFill = document.getElementById('progress-fill');
|
||
const $progressText = document.getElementById('progress-text');
|
||
const $quizQuestion = document.getElementById('quiz-question');
|
||
const $questionNumber = document.getElementById('question-number');
|
||
const $questionInner = document.getElementById('question-wrapper').querySelector('.question-inner');
|
||
const $scaleButtons = document.getElementById('scale-buttons');
|
||
const $btnPrev = document.getElementById('btn-prev');
|
||
|
||
const $resultIcon = document.getElementById('result-icon');
|
||
const $resultName = document.getElementById('result-name');
|
||
const $resultDesc = document.getElementById('result-desc');
|
||
const $resultAnalysis = document.getElementById('result-analysis');
|
||
const $resultRecommend = document.getElementById('result-recommend');
|
||
const $radarCanvas = document.getElementById('radar-canvas');
|
||
|
||
const $posterIcon = document.getElementById('poster-icon');
|
||
const $posterName = document.getElementById('poster-name');
|
||
const $posterDesc = document.getElementById('poster-desc');
|
||
const $posterQuote = document.getElementById('poster-quote');
|
||
const $posterRadarCanvas = document.getElementById('poster-radar-canvas');
|
||
const $posterContent = document.getElementById('poster-content');
|
||
const $posterPreview = document.getElementById('poster-preview');
|
||
const $posterImage = document.getElementById('poster-image');
|
||
|
||
const $privacyModal = document.getElementById('privacy-modal');
|
||
const $courseModal = document.getElementById('course-modal');
|
||
const $courseModalImg = document.getElementById('course-modal-img');
|
||
const $courseCardImg = document.getElementById('course-card-img');
|
||
|
||
// ========================================
|
||
// 初始化
|
||
// ========================================
|
||
function init() {
|
||
// 尝试从 localStorage 恢复进度
|
||
loadProgress();
|
||
|
||
// 绑定事件
|
||
document.getElementById('btn-start').addEventListener('click', startQuiz);
|
||
document.getElementById('btn-prev').addEventListener('click', prevQuestion);
|
||
document.getElementById('btn-share').addEventListener('click', generatePoster);
|
||
document.getElementById('btn-retry').addEventListener('click', retryQuiz);
|
||
document.getElementById('btn-privacy').addEventListener('click', function () {
|
||
$privacyModal.style.display = 'flex';
|
||
});
|
||
document.getElementById('btn-close-privacy').addEventListener('click', function () {
|
||
$privacyModal.style.display = 'none';
|
||
});
|
||
document.getElementById('btn-poster-close').addEventListener('click', closePoster);
|
||
|
||
// 点击遮罩关闭弹窗
|
||
$privacyModal.addEventListener('click', function (e) {
|
||
if (e.target === $privacyModal) {
|
||
$privacyModal.style.display = 'none';
|
||
}
|
||
});
|
||
|
||
// 课程卡片:点击查看大图
|
||
$courseCardImg.addEventListener('click', function () {
|
||
$courseModalImg.src = $courseCardImg.src;
|
||
$courseModal.style.display = 'flex';
|
||
});
|
||
|
||
// 课程大图:点击遮罩关闭
|
||
$courseModal.addEventListener('click', function (e) {
|
||
if (e.target === $courseModal) {
|
||
$courseModal.style.display = 'none';
|
||
}
|
||
});
|
||
|
||
// 量表按钮事件委托
|
||
$scaleButtons.addEventListener('click', function (e) {
|
||
var btn = e.target.closest('.btn-scale');
|
||
if (!btn) return;
|
||
var value = parseInt(btn.getAttribute('data-value'));
|
||
selectAnswer(value);
|
||
});
|
||
}
|
||
|
||
// ========================================
|
||
// 页面切换
|
||
// ========================================
|
||
function showPage(pageId) {
|
||
var pages = document.querySelectorAll('.page');
|
||
pages.forEach(function (p) {
|
||
p.classList.remove('active');
|
||
});
|
||
document.getElementById(pageId).classList.add('active');
|
||
// 滚动到顶部
|
||
window.scrollTo(0, 0);
|
||
}
|
||
|
||
// ========================================
|
||
// 答题流程
|
||
// ========================================
|
||
|
||
/** 开始测试 */
|
||
function startQuiz() {
|
||
currentQuestion = 0;
|
||
answers = new Array(questions.length).fill(0);
|
||
saveProgress();
|
||
showPage('quiz');
|
||
renderQuestion();
|
||
}
|
||
|
||
/** 渲染当前题目(带滑动动画) */
|
||
function renderQuestion(direction) {
|
||
// direction: 'next' | 'prev' | undefined(首次)
|
||
var doRender = function() {
|
||
// 更新进度
|
||
var progress = ((currentQuestion) / questions.length) * 100;
|
||
$progressFill.style.width = progress + '%';
|
||
$progressText.textContent = (currentQuestion + 1) + ' / ' + questions.length;
|
||
|
||
// 更新题目
|
||
$questionNumber.textContent = 'Q' + (currentQuestion + 1);
|
||
$quizQuestion.textContent = questions[currentQuestion];
|
||
|
||
// 更新量表按钮状态
|
||
var btns = $scaleButtons.querySelectorAll('.btn-scale');
|
||
var currentAnswer = answers[currentQuestion];
|
||
btns.forEach(function (btn) {
|
||
var val = parseInt(btn.getAttribute('data-value'));
|
||
if (val === currentAnswer) {
|
||
btn.classList.add('selected');
|
||
} else {
|
||
btn.classList.remove('selected');
|
||
}
|
||
});
|
||
|
||
// 重置选项弹入动画
|
||
resetScaleAnimation();
|
||
|
||
// 上一题按钮
|
||
$btnPrev.style.visibility = currentQuestion > 0 ? 'visible' : 'hidden';
|
||
|
||
// 滑入
|
||
if (direction === 'next') {
|
||
$questionInner.classList.remove('slide-in-right', 'slide-out-left', 'slide-in-left', 'slide-out-right');
|
||
$questionInner.classList.add('slide-in-right');
|
||
requestAnimationFrame(function() {
|
||
requestAnimationFrame(function() {
|
||
$questionInner.classList.remove('slide-in-right');
|
||
});
|
||
});
|
||
} else if (direction === 'prev') {
|
||
$questionInner.classList.remove('slide-in-right', 'slide-out-left', 'slide-in-left', 'slide-out-right');
|
||
$questionInner.classList.add('slide-in-left');
|
||
requestAnimationFrame(function() {
|
||
requestAnimationFrame(function() {
|
||
$questionInner.classList.remove('slide-in-left');
|
||
});
|
||
});
|
||
}
|
||
};
|
||
|
||
if (!direction) {
|
||
doRender();
|
||
return;
|
||
}
|
||
|
||
// 先滑出当前题目
|
||
var outClass = direction === 'next' ? 'slide-out-left' : 'slide-out-right';
|
||
$questionInner.classList.remove('slide-in-right', 'slide-out-left', 'slide-in-left', 'slide-out-right');
|
||
$questionInner.classList.add(outClass);
|
||
|
||
setTimeout(function() {
|
||
doRender();
|
||
}, 250);
|
||
}
|
||
|
||
/** 重置量表按钮的弹入动画 */
|
||
function resetScaleAnimation() {
|
||
var btns = $scaleButtons.querySelectorAll('.btn-scale');
|
||
btns.forEach(function(btn) {
|
||
btn.style.animation = 'none';
|
||
btn.offsetHeight; // 触发重排
|
||
btn.style.animation = '';
|
||
});
|
||
}
|
||
|
||
/** 选择答案 */
|
||
function selectAnswer(value) {
|
||
answers[currentQuestion] = value;
|
||
saveProgress();
|
||
|
||
// 高亮选中按钮
|
||
var btns = $scaleButtons.querySelectorAll('.btn-scale');
|
||
btns.forEach(function (btn) {
|
||
var val = parseInt(btn.getAttribute('data-value'));
|
||
if (val === value) {
|
||
btn.classList.add('selected');
|
||
} else {
|
||
btn.classList.remove('selected');
|
||
}
|
||
});
|
||
|
||
// 更新进度条到当前题的完成状态
|
||
var progress = ((currentQuestion + 1) / questions.length) * 100;
|
||
$progressFill.style.width = progress + '%';
|
||
|
||
// 0.3秒后自动跳下一题
|
||
setTimeout(function () {
|
||
if (currentQuestion < questions.length - 1) {
|
||
currentQuestion++;
|
||
renderQuestion('next');
|
||
} else {
|
||
// 最后一题,进入计算页
|
||
showLoading();
|
||
}
|
||
}, 300);
|
||
}
|
||
|
||
/** 上一题 */
|
||
function prevQuestion() {
|
||
if (currentQuestion > 0) {
|
||
currentQuestion--;
|
||
renderQuestion('prev');
|
||
}
|
||
}
|
||
|
||
// ========================================
|
||
// 计算页
|
||
// ========================================
|
||
function showLoading() {
|
||
showPage('loading');
|
||
// 2.5秒后跳转结果页
|
||
setTimeout(function () {
|
||
calculateAndShowResult();
|
||
}, 2500);
|
||
}
|
||
|
||
// ========================================
|
||
// 计分与类型匹配
|
||
// ========================================
|
||
function calculateAndShowResult() {
|
||
// 计算各维度分数(每维度3题,分值3-15)
|
||
var dimensions = [];
|
||
for (var d = 0; d < 6; d++) {
|
||
var sum = 0;
|
||
for (var q = 0; q < 3; q++) {
|
||
sum += answers[d * 3 + q];
|
||
}
|
||
dimensions.push(sum);
|
||
}
|
||
|
||
// 维度:觉察、情绪、关系、传承、驱动、成长
|
||
var 觉察 = dimensions[0];
|
||
var 情绪 = dimensions[1];
|
||
var 关系 = dimensions[2];
|
||
var 传承 = dimensions[3];
|
||
var 驱动 = dimensions[4];
|
||
var 成长 = dimensions[5];
|
||
|
||
// 类型匹配(按优先级)
|
||
var typeKey;
|
||
if (觉察 >= 10 && 情绪 >= 10) {
|
||
typeKey = 'awakener';
|
||
} else if (传承 >= 10 && 关系 >= 10) {
|
||
typeKey = 'icebreaker';
|
||
} else if (情绪 >= 10 && 觉察 <= 9) {
|
||
typeKey = 'pauser';
|
||
} else if (传承 >= 10 && 成长 >= 10 && 驱动 <= 9) {
|
||
typeKey = 'guardian';
|
||
} else if (成长 >= 10 && 驱动 >= 10 && 觉察 <= 9) {
|
||
typeKey = 'seeker';
|
||
} else if (关系 <= 9 && 情绪 <= 9) {
|
||
typeKey = 'relational';
|
||
} else if (驱动 <= 9 && 觉察 <= 9) {
|
||
typeKey = 'habitual';
|
||
} else {
|
||
typeKey = 'sleeper';
|
||
}
|
||
|
||
var typeData = types[typeKey];
|
||
|
||
// 渲染结果页
|
||
$resultIcon.src = typeData.icon;
|
||
$resultIcon.alt = typeData.name;
|
||
$resultName.textContent = typeData.name;
|
||
$resultDesc.textContent = typeData.desc;
|
||
$resultAnalysis.textContent = typeData.analysis;
|
||
$resultRecommend.textContent = typeData.recommend;
|
||
|
||
// 绘制雷达图
|
||
drawRadarChart($radarCanvas, dimensions, 300);
|
||
|
||
// 保存结果到 localStorage
|
||
localStorage.setItem(STORAGE_KEY + '_result', JSON.stringify({
|
||
typeKey: typeKey,
|
||
dimensions: dimensions
|
||
}));
|
||
|
||
// 清除答题进度
|
||
localStorage.removeItem(STORAGE_KEY);
|
||
|
||
showPage('result');
|
||
}
|
||
|
||
// ========================================
|
||
// 雷达图绘制(Canvas API)
|
||
// ========================================
|
||
function drawRadarChart(canvas, dimensions, size) {
|
||
var ctx = canvas.getContext('2d');
|
||
var dpr = window.devicePixelRatio || 1;
|
||
|
||
// 高清屏适配
|
||
canvas.width = size * dpr;
|
||
canvas.height = size * dpr;
|
||
canvas.style.width = size + 'px';
|
||
canvas.style.height = size + 'px';
|
||
ctx.scale(dpr, dpr);
|
||
|
||
var centerX = size / 2;
|
||
var centerY = size / 2;
|
||
var maxRadius = size * 0.3;
|
||
var levels = 5; // 5层网格
|
||
var sides = 6; // 6个维度
|
||
var angleStep = (Math.PI * 2) / sides;
|
||
var startAngle = -Math.PI / 2; // 从顶部开始
|
||
|
||
ctx.clearRect(0, 0, size, size);
|
||
|
||
// 绘制背景网格(5层)
|
||
for (var level = 1; level <= levels; level++) {
|
||
var r = (maxRadius / levels) * level;
|
||
ctx.beginPath();
|
||
for (var i = 0; i <= sides; i++) {
|
||
var angle = startAngle + angleStep * i;
|
||
var x = centerX + r * Math.cos(angle);
|
||
var y = centerY + r * Math.sin(angle);
|
||
if (i === 0) {
|
||
ctx.moveTo(x, y);
|
||
} else {
|
||
ctx.lineTo(x, y);
|
||
}
|
||
}
|
||
ctx.closePath();
|
||
ctx.strokeStyle = 'rgba(255, 255, 255, 0.08)';
|
||
ctx.lineWidth = 1;
|
||
ctx.stroke();
|
||
}
|
||
|
||
// 绘制轴线
|
||
for (var i = 0; i < sides; i++) {
|
||
var angle = startAngle + angleStep * i;
|
||
ctx.beginPath();
|
||
ctx.moveTo(centerX, centerY);
|
||
ctx.lineTo(
|
||
centerX + maxRadius * Math.cos(angle),
|
||
centerY + maxRadius * Math.sin(angle)
|
||
);
|
||
ctx.strokeStyle = 'rgba(255, 255, 255, 0.1)';
|
||
ctx.lineWidth = 1;
|
||
ctx.stroke();
|
||
}
|
||
|
||
// 绘制数据区域
|
||
ctx.beginPath();
|
||
for (var i = 0; i < sides; i++) {
|
||
// 维度分值范围3-15,映射到0-1
|
||
var value = (dimensions[i] - 3) / 12;
|
||
value = Math.max(0, Math.min(1, value));
|
||
var r = maxRadius * value;
|
||
var angle = startAngle + angleStep * i;
|
||
var x = centerX + r * Math.cos(angle);
|
||
var y = centerY + r * Math.sin(angle);
|
||
if (i === 0) {
|
||
ctx.moveTo(x, y);
|
||
} else {
|
||
ctx.lineTo(x, y);
|
||
}
|
||
}
|
||
ctx.closePath();
|
||
|
||
// 渐变填充
|
||
var gradient = ctx.createRadialGradient(centerX, centerY, 0, centerX, centerY, maxRadius);
|
||
gradient.addColorStop(0, 'rgba(124, 92, 191, 0.45)');
|
||
gradient.addColorStop(1, 'rgba(240, 194, 122, 0.2)');
|
||
ctx.fillStyle = gradient;
|
||
ctx.fill();
|
||
ctx.strokeStyle = 'rgba(196, 181, 253, 0.6)';
|
||
ctx.lineWidth = 2;
|
||
ctx.stroke();
|
||
|
||
// 绘制数据点
|
||
for (var i = 0; i < sides; i++) {
|
||
var value = (dimensions[i] - 3) / 12;
|
||
value = Math.max(0, Math.min(1, value));
|
||
var r = maxRadius * value;
|
||
var angle = startAngle + angleStep * i;
|
||
var x = centerX + r * Math.cos(angle);
|
||
var y = centerY + r * Math.sin(angle);
|
||
|
||
ctx.beginPath();
|
||
ctx.arc(x, y, 4, 0, Math.PI * 2);
|
||
ctx.fillStyle = '#f0c27a';
|
||
ctx.fill();
|
||
ctx.strokeStyle = 'rgba(240, 194, 122, 0.4)';
|
||
ctx.lineWidth = 6;
|
||
ctx.stroke();
|
||
}
|
||
|
||
// 绘制维度标签
|
||
ctx.fillStyle = 'rgba(255, 255, 255, 0.7)';
|
||
ctx.font = '11px "Noto Sans SC", -apple-system, BlinkMacSystemFont, "PingFang SC", "Microsoft YaHei", sans-serif';
|
||
ctx.textAlign = 'center';
|
||
ctx.textBaseline = 'middle';
|
||
|
||
var labelRadius = maxRadius + 24;
|
||
for (var i = 0; i < sides; i++) {
|
||
var angle = startAngle + angleStep * i;
|
||
var x = centerX + labelRadius * Math.cos(angle);
|
||
var y = centerY + labelRadius * Math.sin(angle);
|
||
ctx.fillText(dimensionNames[i], x, y);
|
||
}
|
||
}
|
||
|
||
// ========================================
|
||
// 海报生成
|
||
// ========================================
|
||
function generatePoster() {
|
||
// 获取结果数据
|
||
var resultData = JSON.parse(localStorage.getItem(STORAGE_KEY + '_result') || '{}');
|
||
if (!resultData.typeKey) return;
|
||
|
||
var typeData = types[resultData.typeKey];
|
||
var dimensions = resultData.dimensions;
|
||
|
||
// 填充海报内容
|
||
$posterIcon.src = typeData.icon;
|
||
$posterName.textContent = typeData.name;
|
||
$posterDesc.textContent = typeData.desc;
|
||
$posterQuote.textContent = typeData.quote;
|
||
|
||
// 在海报canvas上重新绘制雷达图
|
||
drawRadarChart($posterRadarCanvas, dimensions, 400);
|
||
|
||
// 显示海报容器
|
||
$poster.classList.add('active');
|
||
$posterContent.style.display = 'block';
|
||
$posterPreview.style.display = 'none';
|
||
|
||
// 等待图片和canvas渲染完成后截图
|
||
setTimeout(function () {
|
||
capturePoster();
|
||
}, 500);
|
||
}
|
||
|
||
/** 使用 html2canvas 截图 */
|
||
function capturePoster() {
|
||
if (typeof html2canvas === 'undefined') {
|
||
alert('海报生成组件加载失败,请刷新页面重试');
|
||
return;
|
||
}
|
||
|
||
// 先显示加载提示
|
||
$posterPreview.style.display = 'block';
|
||
$posterImage.style.display = 'none';
|
||
var previewTitle = document.querySelector('.poster-preview-title');
|
||
if (previewTitle) previewTitle.textContent = '正在生成海报...';
|
||
|
||
html2canvas($posterContent, {
|
||
scale: 2,
|
||
useCORS: true,
|
||
allowTaint: true,
|
||
backgroundColor: '#0f0a1e',
|
||
width: 750,
|
||
height: 1334,
|
||
logging: false
|
||
}).then(function (canvas) {
|
||
$posterContent.style.display = 'none';
|
||
var dataUrl = canvas.toDataURL('image/png');
|
||
$posterImage.src = dataUrl;
|
||
$posterImage.style.display = 'block';
|
||
if (previewTitle) previewTitle.textContent = '长按图片保存分享';
|
||
}).catch(function (err) {
|
||
console.error('海报生成失败:', err);
|
||
$posterContent.style.display = 'none';
|
||
$posterPreview.style.display = 'block';
|
||
$posterImage.style.display = 'none';
|
||
if (previewTitle) previewTitle.textContent = '海报生成失败,请重试';
|
||
});
|
||
}
|
||
|
||
/** 关闭海报 */
|
||
function closePoster() {
|
||
$poster.classList.remove('active');
|
||
$posterContent.style.display = 'block';
|
||
$posterPreview.style.display = 'none';
|
||
}
|
||
|
||
// ========================================
|
||
// 重新测试
|
||
// ========================================
|
||
function retryQuiz() {
|
||
localStorage.removeItem(STORAGE_KEY);
|
||
localStorage.removeItem(STORAGE_KEY + '_result');
|
||
answers = new Array(questions.length).fill(0);
|
||
currentQuestion = 0;
|
||
showPage('home');
|
||
}
|
||
|
||
// ========================================
|
||
// localStorage 进度保存/恢复
|
||
// ========================================
|
||
function saveProgress() {
|
||
var data = {
|
||
currentQuestion: currentQuestion,
|
||
answers: answers
|
||
};
|
||
try {
|
||
localStorage.setItem(STORAGE_KEY, JSON.stringify(data));
|
||
} catch (e) {
|
||
// localStorage 不可用时静默失败
|
||
}
|
||
}
|
||
|
||
function loadProgress() {
|
||
try {
|
||
var raw = localStorage.getItem(STORAGE_KEY);
|
||
if (raw) {
|
||
var data = JSON.parse(raw);
|
||
if (data.answers && data.answers.length === questions.length) {
|
||
answers = data.answers;
|
||
currentQuestion = data.currentQuestion || 0;
|
||
// 如果有未完成的测试,恢复到答题页
|
||
var allAnswered = answers.every(function (a) { return a > 0; });
|
||
if (allAnswered) {
|
||
// 所有题都答完了,直接计算结果
|
||
calculateAndShowResult();
|
||
} else if (currentQuestion > 0) {
|
||
// 有部分进度,恢复答题
|
||
showPage('quiz');
|
||
renderQuestion();
|
||
}
|
||
}
|
||
}
|
||
} catch (e) {
|
||
// 解析失败时忽略
|
||
}
|
||
}
|
||
|
||
// ========================================
|
||
// 启动
|
||
// ========================================
|
||
if (document.readyState === 'loading') {
|
||
document.addEventListener('DOMContentLoaded', init);
|
||
} else {
|
||
init();
|
||
}
|
||
|
||
})();
|