Flutter年龄选择页添加随滑块变化的人物剪影过渡效果
最佳实现方案
核心思路是基于滑块值的渐变透明度叠加,将不同年龄段的人物剪影重叠放置,通过滑块值实时计算每个剪影的显示透明度,配合CSS过渡实现平滑的年龄视觉变化。具体步骤:
- 准备5个阶段的剪影资源:覆盖10岁(男孩)、30岁(少年)、50岁(青年)、70岁(中年)、90岁(老人)关键节点,确保所有剪影尺寸统一。
- 用绝对定位将所有剪影叠加在同一容器内,初始状态仅10岁男孩剪影完全显示。
- 监听滑块的
input事件,根据当前滑块值(10-90)计算每个剪影的透明度:滑块值越接近某年龄段阈值,对应剪影透明度越高,其他剪影透明度线性降低。 - 额外添加轻微缩放、旋转效果,模拟年龄增长带来的体态变化,强化视觉层次感。
可运行代码片段
以下代码用CSS绘制简化的人物剪影(可直接替换为实际剪影图片),实现完整的平滑过渡效果:
<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>健身App年龄选择</title> <style> * { margin: 0; padding: 0; box-sizing: border-box; font-family: Arial, sans-serif; } .age-select-container { max-width: 400px; margin: 50px auto; padding: 20px; text-align: center; } .slider-container { margin: 30px 0; } #age-slider { width: 100%; height: 6px; border-radius: 3px; background: #ddd; outline: none; -webkit-appearance: none; } #age-slider::-webkit-slider-thumb { -webkit-appearance: none; appearance: none; width: 20px; height: 20px; border-radius: 50%; background: #2196F3; cursor: pointer; } #age-slider::-moz-range-thumb { width: 20px; height: 20px; border-radius: 50%; background: #2196F3; cursor: pointer; border: none; } .age-display { font-size: 24px; margin-bottom: 20px; font-weight: bold; } /* 剪影容器:所有剪影叠加 */ .silhouette-container { position: relative; width: 120px; height: 200px; margin: 30px auto; } .silhouette { position: absolute; width: 100%; height: 100%; transition: opacity 0.3s ease, transform 0.3s ease; opacity: 0; } /* 男孩剪影(10岁) */ .silhouette.boy { opacity: 1; transform: scale(0.7); } .silhouette.boy::after { content: ''; position: absolute; width: 40px; height: 70px; background: #333; border-radius: 20px 20px 0 0; top: 20px; left: 50%; transform: translateX(-50%); } .silhouette.boy::before { content: ''; position: absolute; width: 20px; height: 40px; background: #333; top: 90px; left: 35%; border-radius: 0 0 10px 10px; } .silhouette.boy .leg-right { position: absolute; width: 20px; height: 40px; background: #333; top: 90px; right: 35%; border-radius: 0 0 10px 10px; } /* 少年剪影(30岁) */ .silhouette.teen { transform: scale(0.85); } .silhouette.teen::after { content: ''; position: absolute; width: 45px; height: 90px; background: #333; border-radius: 22px 22px 0 0; top: 15px; left: 50%; transform: translateX(-50%); } .silhouette.teen::before { content: ''; position: absolute; width: 22px; height: 50px; background: #333; top: 105px; left: 32%; border-radius: 0 0 11px 11px; } .silhouette.teen .leg-right { position: absolute; width: 22px; height: 50px; background: #333; top: 105px; right: 32%; border-radius: 0 0 11px 11px; } /* 青年剪影(50岁) */ .silhouette.youth { transform: scale(1); } .silhouette.youth::after { content: ''; position: absolute; width: 50px; height: 95px; background: #333; border-radius: 25px 25px 0 0; top: 10px; left: 50%; transform: translateX(-50%); } .silhouette.youth::before { content: ''; position: absolute; width: 25px; height: 55px; background: #333; top: 105px; left: 30%; border-radius: 0 0 12px 12px; } .silhouette.youth .leg-right { position: absolute; width: 25px; height: 55px; background: #333; top: 105px; right: 30%; border-radius: 0 0 12px 12px; } /* 中年剪影(70岁) */ .silhouette.middle { transform: scale(0.95); } .silhouette.middle::after { content: ''; position: absolute; width: 50px; height: 85px; background: #333; border-radius: 25px 25px 0 0; top: 20px; left: 50%; transform: translateX(-50%) translateY(5px) rotate(-5deg); } .silhouette.middle::before { content: ''; position: absolute; width: 25px; height: 50px; background: #333; top: 105px; left: 32%; border-radius: 0 0 12px 12px; transform: rotate(5deg); } .silhouette.middle .leg-right { position: absolute; width: 25px; height: 50px; background: #333; top: 105px; right: 32%; border-radius: 0 0 12px 12px; transform: rotate(-5deg); } /* 老人剪影(90岁) */ .silhouette.elder { transform: scale(0.8); } .silhouette.elder::after { content: ''; position: absolute; width: 45px; height: 75px; background: #333; border-radius: 22px 22px 0 0; top: 30px; left: 50%; transform: translateX(-50%) translateY(10px) rotate(-10deg); } .silhouette.elder::before { content: ''; position: absolute; width: 22px; height: 45px; background: #333; top: 105px; left: 35%; border-radius: 0 0 11px 11px; transform: rotate(10deg); } .silhouette.elder .leg-right { position: absolute; width: 22px; height: 45px; background: #333; top: 105px; right: 35%; border-radius: 0 0 11px 11px; transform: rotate(-10deg); } .continue-btn { padding: 12px 40px; background: #4CAF50; color: white; border: none; border-radius: 25px; font-size: 16px; cursor: pointer; margin-top: 20px; } .continue-btn:hover { background: #45a049; } </style> </head> <body> <div class="age-select-container"> <h2>选择你的年龄</h2> <div class="slider-container"> <input type="range" id="age-slider" min="10" max="90" value="10"> <div class="age-display">当前年龄: <span id="age-value">10</span></div> </div> <!-- 人物剪影容器 --> <div class="silhouette-container"> <div class="silhouette boy"> <div class="leg-right"></div> </div> <div class="silhouette teen"> <div class="leg-right"></div> </div> <div class="silhouette youth"> <div class="leg-right"></div> </div> <div class="silhouette middle"> <div class="leg-right"></div> </div> <div class="silhouette elder"> <div class="leg-right"></div> </div> </div> <button class="continue-btn">Continue</button> </div> <script> const ageSlider = document.getElementById('age-slider'); const ageValue = document.getElementById('age-value'); const silhouettes = { boy: document.querySelector('.silhouette.boy'), teen: document.querySelector('.silhouette.teen'), youth: document.querySelector('.silhouette.youth'), middle: document.querySelector('.silhouette.middle'), elder: document.querySelector('.silhouette.elder') }; // 计算每个剪影的透明度 function updateSilhouettes(age) { // 各年龄段阈值 const thresholds = { boy: 10, teen: 30, youth: 50, middle: 70, elder: 90 }; // 计算每个剪影的显示权重 Object.keys(silhouettes).forEach(key => { const threshold = thresholds[key]; let opacity = 0; // 当年龄在当前阈值和下一个阈值之间时,计算透明度 if (age <= threshold) { // 年龄小于等于当前阈值,和前一个阈值比较 const prevKey = Object.keys(thresholds)[Object.keys(thresholds).indexOf(key) - 1]; if (prevKey) { const prevThreshold = thresholds[prevKey]; if (age >= prevThreshold) { opacity = (threshold - age) / (threshold - prevThreshold); } else { opacity = 1; } } else { opacity = 1; } } else { // 年龄大于当前阈值,和后一个阈值比较 const nextKey = Object.keys(thresholds)[Object.keys(thresholds).indexOf(key) + 1]; if (nextKey) { const nextThreshold = thresholds[nextKey]; if (age <= nextThreshold) { opacity = (age - threshold) / (nextThreshold - threshold); } } } silhouettes[key].style.opacity = opacity; }); } // 监听滑块变化 ageSlider.addEventListener('input', function() { const age = parseInt(this.value); ageValue.textContent = age; updateSilhouettes(age); }); // 初始化 updateSilhouettes(10); </script> </body> </html>
代码说明
- 用CSS绘制了5个不同年龄段的简化剪影,你可以直接替换为实际剪影图片(将
.silhouette::after等伪元素的background属性替换为url('你的图片路径')即可)。 - 通过JavaScript监听滑块输入,实时计算每个剪影的透明度,实现平滑过渡。
- 添加了缩放和旋转效果,模拟年龄增长带来的体态变化,增强视觉体验。
内容的提问来源于stack exchange,提问作者Promaster
相关产品推荐
相关产品推荐

