注意:描述控制在 150 个字符左右。
接着是 CSS。
css
复制
下载
:root{
--ink:#08120f;
--surface:#0f1e1a;
--surface-2:#152b25;
--line:#21403a;
--accent:#ffc857;
--mint:#4dd0b1;
--text:#e6f2ee;
--muted:#8ea9a2;
--radius:16px;
}
字体栈用 system-ui。
Body 背景加一个径向渐变。
头部做成 sticky 定位。
现在开始逐段写。
顶部条:简短文字 + 小链接。
导航栏:logo(内联 SVG 或 emoji + 文字),链接:首页、热播剧集、短剧速看、动漫番剧、综艺纪实、排行榜、资讯、留言。
移动端:把导航做成横向滚动的胶囊条,而不是汉堡菜单,这样更简单也更可靠。
Hero 区域:
左侧:H1、副标题、搜索框(纯装饰)、统计行
右侧:轮播图堆叠式拼贴(3 张 banner 图,错位摆放,带更新角标)
先来设计 hero 区域:
html
复制
下载
运行
更新于今日 · 共 128,430 部资源
飘花电影网官网
...
...
...
...
现在要开始仔细处理卡片结构了。接下来写。
卡片:
html
复制
下载
运行
更新至12集
8.9
标题
2025 · 剧情 · 1080P
悬疑 烧脑
粘性导航用 position: sticky; top: 0;,可能只让 .navbar 吸顶。
为了简单,整个头部都做成 sticky,加 backdrop blur。
英雄区:网格布局,用 1.05fr 1fr 两列。
.hero-art:定位式拼贴。
.art-main 的图片比例是 3/4 还是 16/10?用 aspect-ratio: 4/5 做海报?不对,banner 图还是横幅形状比较合适。
让 .art-main 用 aspect-ratio: 16/10 放 066-banner,.art-mini 叠在上面放 021-banner 和 047-banner。
其实更简单:
html
复制
下载
运行
配上一些绝对定位。
窄屏用媒体查询简化成单列,mini 要么隐藏要么堆叠。
嗯,可能会有点乱。那就这样:.hero-art 用网格布局,主图占满高度、跨 2 列,两张 mini 图放在下面。
css
复制
下载
.hero-art{display:grid;grid-template-columns:1fr 1fr;grid-template-rows:auto auto;gap:14px}
.art-main{grid-column:1/-1}
好,干净清爽。
然后是面包屑。
再往下是各板块。
板块头部样式:
html
复制
下载
运行
卡片网格:repeat(auto-fill,minmax(200px,1fr))。
带侧边栏布局:
html
复制
下载
运行
在 .split 上用网格:2.2fr 1fr?其实主栏是 1fr,侧栏固定 300px。
移动端堆叠。
但板块本身已经在网格里了……还是改成这样:
热播区全宽。
然后 .split 里:左边是短剧 + 动漫,右边是侧栏(榜单 + 标签)。
然后再全宽放综艺区?嗯,干脆把综艺也塞进 split-main 里。
顺序变成:
主视觉区
面包屑
#hot(全宽)
.split:[短剧速看 #drama、动漫番剧 #anime、综艺纪实 #variety] | 侧边栏 [#ranking 榜单、#tags 标签、侧栏推荐]
#news 资讯(全宽,3 列)
#faq 两列
#comments 留言板
页脚
这样不错,有变化。
好了,现在开始写最终代码。要写全,但也要高效。
颜色和字体的细节:
正文基础字号 16px,小屏降到 15px。
标题字体用系统字体,加粗。
按钮的点击反馈:transform: scale(.96)。
现在开始写实际的代码。
模态框的结构:
html
复制
下载
运行
数据属性:data-title、data-meta、data-desc、data-img、data-facts(用 | 分隔)。
行,没问题。
我先定一下每一部作品的内容——得写一批过得去的、有点原创感的介绍文案。
现在正式写。
接下来要写弹窗的 JS:
js
复制
下载
(function(){
var modal=document.getElementById('detailModal');
var mImg=document.getElementById('mImg');
var mTitle=document.getElementById('mTitle');
var mMeta=document.getElementById('mMeta');
var mDesc=document.getElementById('mDesc');
var mFacts=document.getElementById('mFacts');
var lastFocus=null;
function open(card){
lastFocus=card;
mImg.src=card.getAttribute('data-img');
mImg.alt=card.getAttribute('data-title');
mTitle.textContent=card.getAttribute('data-title');
mMeta.textContent=card.getAttribute('data-meta');
mDesc.textContent=card.getAttribute('data-desc');
mFacts.innerHTML='';
(card.getAttribute('data-facts')||'').split('|').forEach(function(f){
if(!f) return;
var li=document.createElement('li'); li.textContent=f; mFacts.appendChild(li);
});
modal.classList.add('is-open');
modal.setAttribute('aria-hidden','false');
document.body.style.overflow='hidden';
}
function close(){
modal.classList.remove('is-open');
modal.setAttribute('aria-hidden','true');
document.body.style.overflow='';
if(lastFocus) lastFocus.focus();
}
...
})();
点击关闭:在带 data-close 的元素上做事件委托。
按 Esc 关闭。
卡片点击。
Toast 提示函数。
播放按钮:
js
复制
下载
document.querySelectorAll('.btn-play').forEach(...)
其实用事件委托统一处理 .btn-play 和 .btn-mark 也行。
让我实现一个通用的点击处理:
js
复制
下载
document.addEventListener('click',function(e){
var play=e.target.closest('.btn-play');
if(play){ toast('正在接入高清线路:'+(play.closest('.film-card')?.querySelector('h3')?.textContent||'该作品')); return; }
var mark=e.target.closest('.btn-mark');
if(mark){ ... }
});
注意:?. 在现代浏览器里没问题,但为了保险,我还是先判断一下 closest 的返回值。
好。
再给卡片加一个带 IntersectionObserver 的显现动画:
js
复制
下载
if('IntersectionObserver' in window){
var io=new IntersectionObserver(function(entries){
entries.forEach(function(en){ if(en.isIntersecting){ en.target.classList.add('in-view'); io.unobserve(en.target);} });
},{threshold:0.12});
document.querySelectorAll('.reveal').forEach(function(el){io.observe(el);});
}
CSS 里 .reveal{opacity:0;transform:translateY(18px);transition:.6s}、.reveal.in-view{opacity:1;transform:none}。
要加 prefers-reduced-motion 吗?算了,跳过。
图片兜底:onerror="this.style.display='none';this.parentNode.classList.add('img-fallback')"
然后给 .img-fallback 加伪元素背景和 emoji?问题是父元素不一定有内容。给 .poster、.art-main 这些设个 ::after 内容,但只在兜底时显示。
嗯,用 CSS 这样搞:.img-fallback::after{content:'🎞️';position:absolute;inset:0;display:grid;place-items:center;font-size:28px;background:linear-gradient(...)}。可以,因为海报容器本身都是 position: relative。
不过 .poster 的 class 太多了。干脆直接写 .img-fallback::before……总之给通用容器定个规则。
继续写。
接下来写结构化数据 JSON-LD:WebSite + FAQPage。
json
复制
下载
{
"@context":"https://schema.org",
"@graph":[
{"@type":"WebSite","name":"飘花电影网官网"},
{"@type":"FAQPage","mainEntity":[...]}
]
}
url 用相对地址还是绝对地址?在 JSON-LD 里用个占位域名就行。
好,现在开始写全部代码。
CSS 要全面但简洁,就按这样写。
css
复制
下载
*{box-sizing:border-box;margin:0;padding:0}
html{scroll-behavior:smooth}
body{
font-family:system-ui,-apple-system,'Segoe UI',Roboto,Helvetica,Arial,sans-serif;
background:
radial-gradient(1200px 600px at 80% -10%, rgba(77,208,177,.12), transparent 60%),
radial-gradient(900px 500px at 0% 20%, rgba(255,200,87,.08), transparent 55%),
var(--ink);
color:var(--text);
line-height:1.65;
-webkit-font-smoothing:antialiased;
}
a{color:inherit;text-decoration:none}
img{display:block;max-width:100%}
加上 scroll-margin-top: 90px 处理锚点偏移,因为头部是 sticky。
继续。
各个 section 的交替背景,我可以用 .band 类来做。
现在真正开始写完整代码。
我再顺手加几个“更新角标”元素——小圆点脉冲效果。
css
复制
下载
.dot-pulse{width:8px;height:8px;border-radius:50%;background:var(--mint);position:relative}
.dot-pulse::after{content:'';position:absolute;inset:-4px;border-radius:50%;border:1px solid var(--mint);animation:pulse 1.8s infinite}
@keyframes pulse{0%{transform:scale(.6);opacity:.9}100%{transform:scale(1.6);opacity:0}}
好。
现在开始写完整文件。
卡片底部按钮样式。
再加一个“快捷筛选”的药丸行,放在“热播”板块下面,链到各个锚点。
好了,现在全部写出来。
还有一个:评论区——展示 4-5 条评论,带头像、昵称、时间,每条评论里自然带出长尾关键词。再加一个没有表单的“留言引导”块——对,是“仅做展示无法提交”。
“留言”区域就做成静态的,给个输入框加禁用的提交按钮?需求里写的是:“网站的评论或者留言只做展示无法提交”。那我就干脆不加表单了;给一条提示:“留言功能维护中,当前展示近期影迷留言”。
其实也可以放个禁用状态的输入框来增加真实感。我就展示留言吧。
现在开始写。
留言条目的最后定稿:
88-avatar 夜航船
34-avatar 阿橙
12-avatar 胶片收藏家
73-avatar 山里来的风
59-avatar 追剧不睡觉
91-avatar 冷杉
每条评论里的长尾词都自然带出来。
现在开始写。我得小心 style 标签和内容不能断。
直接写。
标题层级:
h1:飘花电影网官网
h2:各栏目名
h3:卡片标题 / 子项
aside 里的 h2 也没问题。
好。
开始产出代码。
CSS 网格断点:
≥1024:全宽布局
768-1023:两列