React Native:动画式问卷选ScrollView还是FlatList?
嘿,这个带动画分支的健康评估问卷需求很实用!我给你整理一个基于HTML/CSS/JavaScript的实现方案,完全满足你的跳转、动画和回退需求,一起来看看吧:
核心实现思路
我们可以把整个流程拆成数据结构定义、UI布局规划、动画交互逻辑和回退功能处理四个部分,一步步实现:
1. 定义问卷的分支数据结构
首先用数组对象把所有问题和选项的关联关系写清楚,每个选项对应下一个问题的ID,这样跳转逻辑会非常清晰:
const questionnaireData = [ { id: 1, question: "你是否定期锻炼?", options: [ { text: "是", value: "yes", nextQuestionId: 2 }, { text: "否", value: "no", nextQuestionId: 3 } ] }, { id: 2, question: "好的,你多久锻炼一次?", options: [ { text: "每周3-5次", value: "3-5", nextQuestionId: 4 }, { text: "每周1-2次", value: "1-2", nextQuestionId: 5 } ] }, { id: 3, question: "那你平时有没有久坐的习惯?", options: [ { text: "每天坐8小时以上", value: "long-sit", nextQuestionId: 6 }, { text: "偶尔久坐", value: "short-sit", nextQuestionId: 7 } ] }, // 可以继续扩展更多问题... ];
2. 规划UI布局
页面分成两个核心区域:
- 顶部历史区:用来展示已经完成的问题和选中的选项,点击可以回退到对应步骤
- 中间当前问题区:展示当前要回答的问题和选项
基础HTML结构示例:
<div class="questionnaire-container"> <!-- 历史选择区 --> <div class="history-container"></div> <!-- 当前问题区 --> <div class="current-question"></div> </div>
3. 实现动画效果
用CSS的transition来实现平滑的滑入滑出动画,让选中的问题/选项自然移动到顶部,新问题从右侧滑入:
.questionnaire-container { max-width: 600px; margin: 2rem auto; padding: 0 1rem; } .history-container { display: flex; gap: 1rem; margin-bottom: 2rem; flex-wrap: wrap; } .history-item { background: #f0f4f8; padding: 0.5rem 1rem; border-radius: 20px; cursor: pointer; transition: all 0.3s ease; transform: translateY(-20px); opacity: 0; } .history-item.show { transform: translateY(0); opacity: 1; } .history-item:hover { background: #e2e8f0; } .current-question { background: white; padding: 1.5rem; border-radius: 12px; box-shadow: 0 2px 8px rgba(0,0,0,0.1); transition: all 0.3s ease; } .current-question.slide-out { transform: translateX(100%); opacity: 0; } .current-question.slide-in { transform: translateX(0); opacity: 1; } .option-list { list-style: none; padding: 0; margin-top: 1rem; display: flex; flex-direction: column; gap: 0.8rem; } .option-item { padding: 0.8rem 1rem; border: 1px solid #cbd5e1; border-radius: 8px; cursor: pointer; transition: background 0.2s ease; } .option-item:hover { background: #f8fafc; }
4. 编写交互逻辑(JavaScript)
核心是维护一个历史栈,记录用户的每一步选择,实现跳转和回退功能:
let currentQuestionId = 1; const historyStack = []; const historyContainer = document.querySelector('.history-container'); const currentQuestionContainer = document.querySelector('.current-question'); // 渲染当前问题 function renderQuestion(questionId) { const question = questionnaireData.find(q => q.id === questionId); if (!question) return; // 清空当前容器并渲染内容 currentQuestionContainer.innerHTML = ` <h3>${question.question}</h3> <ul class="option-list"> ${question.options.map(option => ` <li class="option-item" data-next-id="${option.nextQuestionId}" data-text="${option.text}"> ${option.text} </li> `).join('')} </ul> `; // 给选项绑定点击事件 document.querySelectorAll('.option-item').forEach(item => { item.addEventListener('click', () => { const nextId = parseInt(item.dataset.nextId); const optionText = item.dataset.text; handleOptionSelect(optionText, nextId); }); }); // 重置动画类 currentQuestionContainer.classList.remove('slide-out', 'slide-in'); setTimeout(() => { currentQuestionContainer.classList.add('slide-in'); }, 10); } // 处理选项选择(跳转下一题) function handleOptionSelect(optionText, nextId) { // 将当前步骤加入历史栈 historyStack.push({ questionId: currentQuestionId, optionText: optionText }); // 添加滑出动画 currentQuestionContainer.classList.add('slide-out'); // 动画结束后更新内容 setTimeout(() => { currentQuestionId = nextId; renderQuestion(currentQuestionId); // 渲染新的历史项 renderHistoryItem(historyStack[historyStack.length - 1]); }, 300); } // 渲染历史项到顶部 function renderHistoryItem(historyItem) { const itemEl = document.createElement('div'); itemEl.className = 'history-item'; itemEl.textContent = `${questionnaireData.find(q => q.id === historyItem.questionId).question}:${historyItem.optionText}`; // 绑定回退点击事件 itemEl.addEventListener('click', () => { const index = historyStack.findIndex(item => item.questionId === historyItem.questionId); handleGoBack(index); }); historyContainer.appendChild(itemEl); // 触发显示动画 setTimeout(() => { itemEl.classList.add('show'); }, 10); } // 处理回退操作 function handleGoBack(targetIndex) { // 移除目标索引之后的历史项 const removedItems = historyStack.splice(targetIndex + 1); removedItems.forEach(() => { historyContainer.removeChild(historyContainer.lastChild); }); // 跳转到目标问题 currentQuestionId = historyStack[targetIndex].questionId; currentQuestionContainer.classList.add('slide-out'); setTimeout(() => { renderQuestion(currentQuestionId); }, 300); } // 初始化渲染第一个问题 renderQuestion(currentQuestionId);
这样整个问卷就实现了你想要的所有功能:分支跳转、选中内容动画移到顶部、点击历史项回退。你可以根据需求修改样式和问题内容,扩展更多的分支逻辑~
内容的提问来源于stack exchange,提问作者HONGAA
相关产品推荐
相关产品推荐

