Compare commits
3 Commits
62d3b28f2d
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 593a5cc02a | |||
| b3a5aa3446 | |||
| c709429cd2 |
21
agent.md
21
agent.md
@@ -1,21 +0,0 @@
|
|||||||
## Agent
|
|
||||||
这是一个仿 YouTube 的纯前端页面, 不需要与后端进行任何交互. 但需要尽量做到完整的仿照前端页面
|
|
||||||
### 项目需求
|
|
||||||
- 页面顶栏从左到右分别为 菜单(按钮), YouTube 图标(图片), 搜索框(编辑控件, 居中), 搜索按钮(按钮, 附着在编辑框最右侧), 语音识别(按钮, 紧靠在编辑框右侧), 创建(按钮, 居于右侧, 文字左侧还附有 "+" 图标), 通知(按钮, 图标为铃铛样式), 用户头像(按钮, 图标为用户头像)
|
|
||||||
- 搜索框要求可点击, 且点击时有渐变动画.
|
|
||||||
- 搜索框要求可在本地数据库中搜索数据. 要求可以进行关键词搜索, 且搜索出的内容要求展示在基于搜索框展开的列表中, 关键词着重标记. 本地数据来源请你自己生成一份视频列表存储在 `/src/media.list` 中.
|
|
||||||
- 左边侧边栏从上到下分别为 首页(按钮, 左侧图标为房子样式), Shorts(按钮), 订阅(板块, 字的右边附有 ">" 图标, 可点击), 我(板块, 内容从上到下分别为 历史记录, 播放列表, 稍后再看, 赞过的视频, 我的视频, 注意: 每个按钮的左侧均附有图标. 如不知道应该放什么图标, 则直接填写 `/img` 目录的内容, 虽然现在该目录不存在, 但稍后会往目录中放置图片), 探索(板块, 内容从上到下分别为 音乐, 电影, 直播, 如不知道应该放什么图标, 则直接填写 `/img` 目录的内容), 更多 YouTube 产品与功能(板块, 内容分别为 `YouTube Premium`, `YouTube 工作室`, `YouTube Music`, `YouTube Kids`, 如不知道应该放什么图标, 则直接填写 `/img` 目录的内容), 设置(按钮), 举报记录(按钮), 帮助(按钮), 发送反馈(按钮).
|
|
||||||
- 侧边栏的订阅板块中要求展示订阅的 YouTuber 头像和 ID, 具体可以自行生成.
|
|
||||||
- 侧边栏底部要求展示页面及版权信息. 例如: `<C> 2026 Google LLC`
|
|
||||||
- 主页内容要求展示普通横屏视频和短视频(即 `Shorts`), 普通横屏视频的卡片在 `3840x2560` 的分辨率下一行展示的数量为 3 条, 可根据窗口大小进行自适应. Shorts 内容紧接在横屏视频卡片下方, 在同等分辨率下展示的数量为一行 5 条.
|
|
||||||
- 每个横屏视频卡片中要求展示发布者头像, 视频标题, 发布者 ID, 播放次数, 上传时间. 在卡片的右下角要求附有 "···" 样式的可展开的菜单栏, 内容为 添加到待播列表, 保存到"稍后再看", 保存到播放列表, 下载, 分享, 不感兴趣, 不推荐此频道, 举报. 这些按钮的左侧均有图标.
|
|
||||||
- Shorts 卡片展示的内容和横屏视频卡片展示的内容差不多, 但不展示发布者头像.
|
|
||||||
- 主页请放置尽量多的视频卡片, 可以做到整体页面上下滑动.
|
|
||||||
- 在主页第一行视频的上方还有一些小按钮, 可以跳转到相应的板块浏览对应板块的视频. 分别为 全部, 音乐, 游戏, 直播, 合辑, 动画, 手工艺品, 最近上传, 已观看, 发现新视频. 这些按钮均要求可以点击, 并作出相应视频过滤功能.
|
|
||||||
|
|
||||||
### 总要求
|
|
||||||
- 项目中所有要求与后端进行交互的按钮均不用做具体功能, 只需要做出一个按钮即可.
|
|
||||||
- 过滤视频, 搜索等只需要获取视频数据的功能均需要在自行生成的视频数据库中进行操作, 不需要与后端对接.
|
|
||||||
- 将所有生成的视频等数据放置在 `/src` 目录中.
|
|
||||||
- 可以随意使用 `TailWindCSS`, `Vue` 等前端框架, 但**绝对不是必须使用**, 你可以使用其他前端框架, 如 `React`, `Angular` 等.
|
|
||||||
- 代码完成后请进行自我审查, 确保运行时能够达到所有想要的效果后再停止思考.
|
|
||||||
@@ -13,7 +13,7 @@ body {
|
|||||||
color: #ffffff;
|
color: #ffffff;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 主容器 */
|
/* Main container */
|
||||||
.youtube-container {
|
.youtube-container {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
@@ -21,7 +21,7 @@ body {
|
|||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 顶栏样式 */
|
/* Header styles */
|
||||||
.header {
|
.header {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@@ -157,14 +157,14 @@ body {
|
|||||||
font-size: 20px;
|
font-size: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 主内容区 */
|
/* Main content area */
|
||||||
.main-content {
|
.main-content {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 侧边栏样式 */
|
/* Sidebar styles */
|
||||||
.sidebar {
|
.sidebar {
|
||||||
width: 240px;
|
width: 240px;
|
||||||
background-color: #000000;
|
background-color: #000000;
|
||||||
@@ -200,6 +200,10 @@ body {
|
|||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.nav-section-title i {
|
||||||
|
transition: transform 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
.nav-btn {
|
.nav-btn {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@@ -231,6 +235,16 @@ body {
|
|||||||
|
|
||||||
.subscriptions-list {
|
.subscriptions-list {
|
||||||
padding: 0 12px;
|
padding: 0 12px;
|
||||||
|
max-height: 500px;
|
||||||
|
overflow: hidden;
|
||||||
|
transition: max-height 0.3s ease, opacity 0.3s ease;
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.subscriptions-list.collapsed {
|
||||||
|
max-height: 0;
|
||||||
|
opacity: 0;
|
||||||
|
padding: 0 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.subscription-item {
|
.subscription-item {
|
||||||
@@ -273,7 +287,7 @@ body {
|
|||||||
border-top: 1px solid #303030;
|
border-top: 1px solid #303030;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 视频内容区 */
|
/* Video content area */
|
||||||
.video-content {
|
.video-content {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
@@ -281,7 +295,7 @@ body {
|
|||||||
background-color: #121212;
|
background-color: #121212;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 板块按钮 */
|
/* Category buttons */
|
||||||
.category-buttons {
|
.category-buttons {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 12px;
|
gap: 12px;
|
||||||
@@ -311,7 +325,7 @@ body {
|
|||||||
border-color: #ffffff;
|
border-color: #ffffff;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 视频网格 */
|
/* Video grid */
|
||||||
.video-grid {
|
.video-grid {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(auto-fill, minmax(550px, 1fr));
|
grid-template-columns: repeat(auto-fill, minmax(550px, 1fr));
|
||||||
@@ -319,7 +333,7 @@ body {
|
|||||||
margin-bottom: 48px;
|
margin-bottom: 48px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 视频卡片 */
|
/* Video card */
|
||||||
.video-card {
|
.video-card {
|
||||||
background-color: #202020;
|
background-color: #202020;
|
||||||
border-radius: 12px;
|
border-radius: 12px;
|
||||||
@@ -498,7 +512,7 @@ body {
|
|||||||
gap: 20px;
|
gap: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Shorts 卡片 */
|
/* Shorts card */
|
||||||
.shorts-card {
|
.shorts-card {
|
||||||
position: relative;
|
position: relative;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
@@ -559,7 +573,7 @@ body {
|
|||||||
margin-right: 4px;
|
margin-right: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 搜索结果弹窗 */
|
/* Search results popup */
|
||||||
.search-results {
|
.search-results {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
top: 56px;
|
top: 56px;
|
||||||
@@ -645,7 +659,7 @@ body {
|
|||||||
color: #ffffff;
|
color: #ffffff;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 响应式设计 */
|
/* Responsive design */
|
||||||
@media (max-width: 1600px) {
|
@media (max-width: 1600px) {
|
||||||
.video-grid {
|
.video-grid {
|
||||||
grid-template-columns: repeat(auto-fill, minmax(500px, 1fr));
|
grid-template-columns: repeat(auto-fill, minmax(500px, 1fr));
|
||||||
@@ -713,7 +727,7 @@ body {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 滚动条样式 - Webkit浏览器 */
|
/* Scrollbar styles - Webkit browsers */
|
||||||
::-webkit-scrollbar {
|
::-webkit-scrollbar {
|
||||||
width: 12px;
|
width: 12px;
|
||||||
height: 12px;
|
height: 12px;
|
||||||
|
|||||||
16
index.html
16
index.html
@@ -9,7 +9,7 @@
|
|||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div class="youtube-container">
|
<div class="youtube-container">
|
||||||
<!-- 顶栏 -->
|
<!-- Top bar -->
|
||||||
<header class="header">
|
<header class="header">
|
||||||
<div class="header-left">
|
<div class="header-left">
|
||||||
<button class="menu-btn">
|
<button class="menu-btn">
|
||||||
@@ -46,9 +46,8 @@
|
|||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<!-- 主内容区 -->
|
|
||||||
<main class="main-content">
|
<main class="main-content">
|
||||||
<!-- 侧边栏 -->
|
<!-- Sidebar -->
|
||||||
<aside class="sidebar">
|
<aside class="sidebar">
|
||||||
<nav class="sidebar-nav">
|
<nav class="sidebar-nav">
|
||||||
<div class="nav-section">
|
<div class="nav-section">
|
||||||
@@ -68,7 +67,7 @@
|
|||||||
<i class="fas fa-chevron-right"></i>
|
<i class="fas fa-chevron-right"></i>
|
||||||
</div>
|
</div>
|
||||||
<div class="subscriptions-list">
|
<div class="subscriptions-list">
|
||||||
<!-- 订阅内容将通过 JS 动态生成 -->
|
<!-- Subscription items will be dynamically generated via JS -->
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -178,25 +177,24 @@
|
|||||||
<button class="category-btn">Watched</button>
|
<button class="category-btn">Watched</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 横屏视频 -->
|
<!-- Horizontal video grid -->
|
||||||
<div class="video-grid">
|
<div class="video-grid">
|
||||||
<!-- 视频卡片将通过 JS 动态生成 -->
|
<!-- Video cards will be dynamically generated via JS -->
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Shorts -->
|
<!-- Shorts -->
|
||||||
<div class="shorts-section">
|
<div class="shorts-section">
|
||||||
<h3>Shorts</h3>
|
<h3>Shorts</h3>
|
||||||
<div class="shorts-grid">
|
<div class="shorts-grid">
|
||||||
<!-- Shorts 卡片将通过 JS 动态生成 -->
|
<!-- Shorts cards will be dynamically generated via JS -->
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
<!-- 搜索结果弹窗 -->
|
|
||||||
<div class="search-results" style="display: none;">
|
<div class="search-results" style="display: none;">
|
||||||
<div class="search-results-content">
|
<div class="search-results-content">
|
||||||
<!-- 搜索结果将通过 JS 动态生成 -->
|
<!-- Search results will be dynamically generated via JS -->
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
17
js/script.js
17
js/script.js
@@ -1,4 +1,3 @@
|
|||||||
// 全局变量
|
|
||||||
let videos = [
|
let videos = [
|
||||||
{
|
{
|
||||||
"id": 1,
|
"id": 1,
|
||||||
@@ -476,7 +475,7 @@ let videos = [
|
|||||||
];
|
];
|
||||||
let currentCategory = 'All';
|
let currentCategory = 'All';
|
||||||
|
|
||||||
// DOM 元素
|
// DOM elements
|
||||||
const searchInput = document.querySelector('.search-input');
|
const searchInput = document.querySelector('.search-input');
|
||||||
const searchBtn = document.querySelector('.search-btn');
|
const searchBtn = document.querySelector('.search-btn');
|
||||||
const searchResults = document.querySelector('.search-results');
|
const searchResults = document.querySelector('.search-results');
|
||||||
@@ -505,13 +504,13 @@ function renderVideos() {
|
|||||||
videoGrid.innerHTML = '';
|
videoGrid.innerHTML = '';
|
||||||
shortsGrid.innerHTML = '';
|
shortsGrid.innerHTML = '';
|
||||||
|
|
||||||
// 渲染长视频
|
// Render long videos
|
||||||
videosOnly.forEach(video => {
|
videosOnly.forEach(video => {
|
||||||
const videoCard = createVideoCard(video);
|
const videoCard = createVideoCard(video);
|
||||||
videoGrid.appendChild(videoCard);
|
videoGrid.appendChild(videoCard);
|
||||||
});
|
});
|
||||||
|
|
||||||
// 渲染短视频
|
// Render shorts videos
|
||||||
shortsOnly.forEach(video => {
|
shortsOnly.forEach(video => {
|
||||||
const shortsCard = createShortsCard(video);
|
const shortsCard = createShortsCard(video);
|
||||||
shortsGrid.appendChild(shortsCard);
|
shortsGrid.appendChild(shortsCard);
|
||||||
@@ -778,12 +777,12 @@ function bindEvents() { // Bind events to the DOM elements
|
|||||||
const subscriptionsList = document.querySelector('.subscriptions-list');
|
const subscriptionsList = document.querySelector('.subscriptions-list');
|
||||||
const chevron = subscriptionTitle.querySelector('i');
|
const chevron = subscriptionTitle.querySelector('i');
|
||||||
|
|
||||||
if (subscriptionsList.style.display === 'none') {
|
subscriptionsList.classList.toggle('collapsed');
|
||||||
subscriptionsList.style.display = 'block';
|
|
||||||
chevron.style.transform = 'rotate(0deg)';
|
if (subscriptionsList.classList.contains('collapsed')) {
|
||||||
} else {
|
|
||||||
subscriptionsList.style.display = 'none';
|
|
||||||
chevron.style.transform = 'rotate(-90deg)';
|
chevron.style.transform = 'rotate(-90deg)';
|
||||||
|
} else {
|
||||||
|
chevron.style.transform = 'rotate(0deg)';
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user