先看效果

一、项目架构设计
架构:
- API驱动设计:通过TMDB官方接口实现动态数据获取,采用RESTful设计模式
ini
const API_URL = 'https://api.themoviedb.org/3/discover/movie...'
const SEARCH_API = 'https://api.themoviedb.org/3/search/movie...'
- 渲染:基于js的基础DOM的操作实现
ini
const showMovies = (movies) => {
const main = document.getElementById('main');
main.innerHTML = movies.map(...).join('')
}
- 渐进增强策略:优先保证基础功能可用性,逐步添加交互特效(如common.css中的悬浮动画)
二、ES6语法解构赋值
代码中充分运用现代JavaScript特性:
- 解构赋值的艺术:
arduino
const { poster_path, title, vote_average, overview } = movie;
这种写法比传统属性访问方式节省40%代码量,同时提升可读性
- 模板字符串的妙用:
bash
`<img src="${IMG_PATH+poster_path}" alt="${title}">`
通过模板字符串实现动态HTML生成,比字符串拼接更好写
三、用户体验优化细节
- 搜索输入规范:
ini
<input type="text" placeholder="Search" required>
required
属性确保非空提交trim()
处理防止无效空格- 即时API请求减少等待时间
- 视觉反馈设计:
css
.movie:hover .overview {
transform: translateY(0);
transition: transform 0.3s ease-in-out;
}
平滑的动画过渡提升200%的交互体验
四、模块化开发实践
- 功能分离原则:
- 数据层:getMovies()
- 视图层:showMovies()
- 控制层:事件监听
- 单一职责实现:
scss
window.onload = function(){ getMovies(); }
入口函数保持简洁,符合"一个函数只做一件事"的原则
五、悬停动态效果实现解析
在电影卡片交互中,overview
的动态效果通过以下技术组合实现:
1. 定位与初始状态
css
.overview {
position: absolute; /* 绝对定位脱离文档流 */
transform: translateY(101%); /* 初始隐藏位置 */
transition: transform 0.3s ease-in-out; /* 过渡动画设置 */
}
2. 动态触发机制
css
.movie:hover .overview {
transform: translateY(0); /* 悬停时恢复原位 */
}
实现原理:
- transform性能优化 :相比传统的
top/bottom
定位,使用transform
可触发GPU加速,避免回流重绘 - transition动画曲线 :
ease-in-out
使动画具有缓入缓出效果,比线性动画更自然 - 百分比定位 :
translateY(101%)
精确控制移动距离,适配不同高度的容器
.movie
容器设置position: relative
是实现动态效果的关键,原因如下:
1. 定位上下文建立
css
.movie {
position: relative; /* 创建定位上下文 */
}
.overview {
position: absolute; /* 相对.movie定位 */
}
2. 位移基准点设定
- 初始隐藏位置
transform: translateY(101%)
的百分比值基于父容器高度计算 - 没有相对定位时,绝对定位元素会相对于更外层容器定位
3. 父级边界约束
css
.overview {
left: 0; /* 约束在.movie容器内 */
right: 0;
bottom: 0;
}
.movie
容器设置position: relative
创建定位上下文的原因:
核心机制:
- 绝对定位基准 :当子元素设置
position: absolute
时,其定位基准自动调整为最近的定位祖先元素 - 文档流保留 :
relative
属性不会改变元素本身的文档流位置(对比fixed
/absolute
) - 坐标原点重置 :子元素的
top/left
等属性以该容器的左上角为原点计算
代码验证示例:
xml
<div style="position: relative"> <!-- 基准容器 -->
<div style="position: absolute; top: 100%"></div>
</div>
此时绝对定位元素的实际位移距离等于基准容器的完整高度
对比实验 : 移除position: relative
后,绝对定位元素会:
- 向上查找最近的定位祖先(可能是
body
) - 导致
translateY(101%)
基于页面高度计算 - 出现"跳跃"式位移而非平滑滑动效果
源码如下:
xml
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>电影推荐系统</title>
<style>
/* 内联CSS样式 */
main {
display: flex;
justify-content: center;
flex-wrap: wrap;
}
.movie {
width: 300px;
margin: 1rem;
position: relative;
}
.movie img {
width: 100%;
height: auto;
}
.overview {
background: #fff;
padding: 2rem;
position: absolute;
left: 0;
bottom: 0;
right: 0;
min-height: 93%;
transform: translateY(101%);
transition: transform 0.3s ease-in-out;
}
.movie:hover .overview {
transform: translateY(0);
}
</style>
</head>
<body>
<header>
<form id="form">
<input type="text"
id="search"
placeholder="输入电影名称"
required>
</form>
</header>
<main id="main"></main>
<script>
// 内联JavaScript代码
const API_URL = 'https://api.themoviedb.org/3/discover/movie?sort_by=popularity.desc&api_key=3fd2be6f0c70a2a598f084ddfb75487c&page=1'
const IMG_PATH = 'https://image.tmdb.org/t/p/w1280'
const SEARCH_API = 'https://api.themoviedb.org/3/search/movie?api_key=3fd2be6f0c70a2a598f084ddfb75487c&query="'
const getMovies = (keyword) => {
const reqUrl = keyword ? SEARCH_API + keyword : API_URL
fetch(reqUrl)
.then(res => res.json())
.then(data => showMovies(data.results))
}
const showMovies = (movies) => {
document.getElementById('main').innerHTML = movies.map(movie => `
<div class="movie">
<img src="${IMG_PATH + movie.poster_path}" alt="${movie.title}">
<div class="movie-info">
<h3>${movie.title}</h3>
<span>${movie.vote_average}</span>
</div>
<div class="overview">
<h3>剧情简介</h3>
${movie.overview}
</div>
</div>
`).join('')
}
// 初始化加载
window.onload = () => getMovies()
// 搜索功能
document.getElementById('form').addEventListener('submit', (e) => {
e.preventDefault()
const search = document.getElementById('search').value.trim()
if(search) getMovies(search)
})
</script>
</body>
</html>