
简介学成在线PSD素材及代码是一套面向前端初学者的教学案例包源自黑马程序员与pink老师的前端课程涵盖了页面设计原稿与配套实现源码可帮助学习者理解从PSD设计图到HTML/CSS页面的完整还原流程。压缩包共11个文件约17.71MB其中包含4份PSD设计源文件首页、课程列表页、课程介绍页、职业规划页、5张页面所需的PNG切图、1个index.html页面结构以及1个style.css样式表。学习时既可以在Photoshop中逐层分析设计思路也可以对照源码研究盒模型、浮动布局、配色与字体设置等知识点掌握教学型页面的经典实现方案。这份案例既适合刚接触HTML/CSS的新手模仿练习也适合有一定基础的开发者查漏补缺。目前已有14050人学习或下载素材与代码组织清晰解压即可直接查看并修改是提升前端页面还原能力和编码熟练度的实用练习资源。1. 学成在线PSD素材及代码到底在做什么一份设计稿到可运行页面的完整链路你可能遇到过这样的情况下载了一个叫“学成在线”的压缩包解压后里面是一堆PSD文件和十几个散落的HTML、CSS、JS文件。打开PSD一看页面设计得挺完整但切图没做好代码和素材对不上浏览器里跑起来不是图片裂了就是样式全乱。这个标题看似简单实际上说的是一个完整的前端切图落地场景把学成在线的PSD设计稿通过切图、导出素材、写语义化页面代码、加交互脚本最终变成一个真正能部署运行的网页。它适合正在练手切图的初级前端、需要交付完整站点的外包工程师以及想搞懂设计稿如何转代码的产品同学。下面这套流程是我自己做这类“PSD素材及代码”项目时反复用过的方案。2. 把PSD拆成可用素材切图流程、命名规范与目录设计拿到PSD后的第一件事不是急着打开代码编辑器而是先花半小时把设计稿吃透。很多人跳过这一步直接右键“导出为PNG”结果导出几百张没有分类、没有命名规则的碎片图后面写CSS时根本找不到哪张是哪张。学成在线这类教育培训站点的PSD结构其实很规律顶部导航、轮播Banner、课程卡片列表、新闻资讯区、底部友链和版权。你需要先把这些区域在图层面板里标记出来然后才开始切图。2.1 先读设计稿识别模块层级与重复组件打开PSD后先在图层面板里把图层组按“header / banner / course / news / footer”这样的大模块重命名这个动作能让你后面切图时思路清晰。学成在线的设计稿里课程卡片通常是重复出现的组件比如四个课程卡片横排一行的结构在图层面板里往往是一个组里复制了多次。遇到这种情况不要逐个切先确定它的统一尺寸和样式再只切一次复用同一套类名和图片。具体我会这样做先建一个“切图清单”列的字段是模块名、元素名、类型图标/背景/插图、尺寸、格式、倍率。把PSD里所有需要导出的元素在清单里过一遍再动手导出。这一步看起来琐碎实际能省掉后续排查图片对不上的大量时间。学成在线这类页面的元素数量通常在五十到一百个之间不提前列清单导到后面必然乱。提示如果你电脑里没装Photoshop也可以先用蓝湖、PxCook这类工具解析PSD它们能标注尺寸和颜色。但真正切大图、处理透明图层时还是回到Photoshop操作最靠谱。2.2 从PSD导出三类素材图标、背景、插图我一般把需要导出的素材分成三类对待。第一类是图标类包括导航展开箭头、课程分类小图标、搜索按钮、社交平台小logo这类尺寸小、带透明通道的元素。在Photoshop里选中目标图层或组右键选择“快速导出为PNG”如果图层里有多个尺寸相同的图形可以把它们放到一个组里一起导出。注意导出时把“转换到sRGB”勾上不然放到网页上颜色会偏灰。第二类是背景类和装饰类比如Banner底图、渐变背景层、页面顶部的大色块。这类元素如果是有复杂照片内容的大图导出为JPG更合适体积只有PNG的几分之一如果是纯色或者简单渐变的背景尽量用CSS实现不要导出图片视觉更清晰、加载也更快。第三类是课程封面这类插图按设计稿的实际比例导出透明区域用PNG实底用JPG。每次导出时我都会在导出对话框里确定倍率切图默认导出1倍图但如果设计稿是按2倍尺寸制作的就要额外导一份2倍图。怎么判断设计稿是否是2倍看翼标打开“图像”菜单里的“图像大小”如果宽度是设计页面逻辑宽度的两倍左右比如逻辑宽度1920px、实际PSD宽度3840px那就是2倍稿需要导2x素材后面给srcset用。2.3 命名的复用目录结构与CSS类名的对应关系图片切完后要先把文件分类放到约定好的目录里。我习惯的项目结构是这样的学成在线/ ├── index.html ├── css/ │ ├── base.css │ ├── layout.css │ └── index.css ├── js/ │ ├── carousel.js │ └── tab.js └── assets/ ├── icons/ # 小图标PNG SVG ├── images/ # 课程封面、Banner大图 └── sprites/ # 雪碧图图片命名和CSS里的类名要保持一致的约定比如课程卡片里的图统一叫course-html.jpg、course-css.jpg对应的CSS类名就是.course-html、.course-css。这样做的好处是页面里找class时能直接猜到对应的图片文件名不需要反复打开资源管理器找。命名规则建议固定为全小写、单词之间用连字符连接比如icon-arrow-down.png、banner-main.jpg。禁止在文件名里出现空格、中文和“最终版”“V2”这种后缀否则进入自动化流程时大概率踩坑。文件名长度控制在二十个字符以内后面合并雪碧图时才不会被超长路径影响。2.4 适配策略2x素材与retina屏的约定素材切好之后要面对的一个现实问题是高清屏适配。学成在线的设计稿宽度一般按1920做但实际用户有的在普通屏看有的在Retina屏看。我的做法是图标和关键插图各导一份2x图1x图保留在同一目录用srcset或image-set()让浏览器自己选。img srcassets/images/course-html.jpg srcsetassets/images/course-html2x.jpg 2x altHTML课程封面这里逻辑很简单src是默认1x图srcset里声明的2x图在2倍屏上加载保证不模糊。CSS背景图方面用image-set()配合background-image实现类似效果。还有一种做法是把2x图作为主图1x图删掉然后用CSS里的background-size按50%缩放。但这种方式要求每个图都按统一的设计倍率切后期维护很麻烦。我只有在开发时间紧张时才会这么偷懒。正经项目还是用srcset或image-set()代码量就几行清晰度和体积能同时兼顾属于性价比最高的适配方案。3. 写页面代码用HTML5语义化骨架和Flex布局还原设计稿素材准备完接下来进入代码阶段。学成在线这类站点结构规整用HTML5的语义化标签加Flex布局基本能一比一还原设计稿。写代码的顺序我固定是从HTML骨架开始然后写CSS布局最后再处理交互。3.1 HTML骨架语义标签选型与模块划分先看一个标准的页面结构示例!DOCTYPE html html langzh-CN head meta charsetUTF-8 meta nameviewport contentwidthdevice-width, initial-scale1.0 title学成在线/title link relstylesheet hrefcss/base.css link relstylesheet hrefcss/layout.css link relstylesheet hrefcss/index.css /head body header classsite-header a href/ classlogo学成在线/a nav classmain-nav ul lia href#首页/a/li lia href#课程/a/li lia href#问答/a/li lia href#就业/a/li /ul /nav div classsearch-box input typetext placeholder搜索感兴趣的课程 button typesubmit搜索/button /div a href# classuser-entry登录 / 注册/a /header main section classbanner>/* base.css */ * { margin: 0; padding: 0; box-sizing: border-box; } body { font-family: -apple-system, BlinkMacSystemFont, Helvetica Neue, PingFang SC, Microsoft YaHei, sans-serif; color: #333; background: #fff; } /* layout.css */ body { display: flex; flex-direction: column; min-height: 100vh; } main { flex: 1; width: 100%; } .container { width: 1200px; margin: 0 auto; } .site-header { display: flex; align-items: center; justify-content: space-between; height: 64px; padding: 0 24px; background: #fff; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.04); } .main-nav ul { display: flex; gap: 32px; list-style: none; } .search-box { display: flex; align-items: center; gap: 8px; } .course-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 24px; margin-top: 24px; }这段CSS的核心参数都在为还原设计稿服务导航高度64px四个课程卡片并排用grid-template-columns: repeat(4, 1fr)实现卡片间距24px。为什么用grid而不是flex因为课程卡片是典型的网格布局grid能直接控制列数和间距不需要在子项上设置flex-basis再做减法计算间距。min-height: 100vh配合flex: 1的做法让页脚永远贴在视口底部即使内容不够一屏也不会出现页脚悬浮在半空的情况。排除了px写死高度的方案因为用户窗口高度不固定最稳妥的做法就是Flex纵向撑开。3.3 处理设计中隐藏的状态hover、active、下拉菜单设计稿上一般只呈现静态效果但实际交互页面必须有状态反馈。学成在线的导航栏就有典型的hover和active状态CSS里这样处理.main-nav a { display: block; padding: 6px 0; color: #333; text-decoration: none; border-bottom: 2px solid transparent; transition: border-color 0.2s ease, color 0.2s ease; } .main-nav a:hover { color: #ff7c32; border-bottom-color: #ff7c32; } .main-nav a.active { color: #ff7c32; border-bottom-color: #ff7c32; } .course-card { border-radius: 8px; overflow: hidden; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06); transition: transform 0.2s ease, box-shadow 0.2s ease; } .course-card:hover { transform: translateY(-4px); box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12); }这组CSS里最关键的是给导航链接加了2px透明下边框。透明边框不会改变盒子的占位尺寸所以hover时文字不会上下跳动这和直接加border-bottom: 2px solid有本质区别。课程卡片的阴影和位移都加了transition悬停时不会有生硬跳变。下拉菜单我倾向于用CSS的visibility加opacity实现而不是display: none因为前者可以配合transition做淡入淡出后者只能硬切换。这里不再展开完整代码但原理是下拉面板默认visibility: hidden和opacity: 0父级li:hover时改成visible和1配合position: absolute定位到导航条目下方。4. 给页面添加交互逻辑轮播、导航高亮与移动端菜单静态页面写完后学成在线的首页还需要几处常见交互Banner轮播、课程Tab切换、移动端导航菜单。这些功能用原生JavaScript完全够用不必引入jQuery或Vue。有人会觉得用框架更省事但一个静态页面用原生JS就能解决的问题加载Vue运行时反而增加几百KB体积得不偿失。4.1 轮播图实现使用原生JS还是框架轮播是首页视觉重心也是交互的核心。常见做法是在HTML里放一个.banner-viewport容器里面一张接一张排列.banner-track中的幻灯片再用JavaScript控制transform: translateX()。示例代码如下div classbanner-viewport div classbanner-track div classslide stylebackground-image: url(assets/images/banner-1.jpg)/div div classslide stylebackground-image: url(assets/images/banner-2.jpg)/div div classslide stylebackground-image: url(assets/images/banner-3.jpg)/div /div button classbanner-prev上一张/button button classbanner-next下一张/button /div// carousel.js const track document.querySelector(.banner-track); const slides document.querySelectorAll(.slide); const prevBtn document.querySelector(.banner-prev); const nextBtn document.querySelector(.banner-next); let currentIndex 0; const slideCount slides.length; const autoPlayInterval 4000; let timer null; function goToIndex(index) { if (index 0) index slideCount - 1; if (index slideCount) index 0; currentIndex index; track.style.transform translateX(-${currentIndex * 100}%); track.style.transition transform 0.35s ease; } function nextSlide() { goToIndex(currentIndex 1); } function prevSlide() { goToIndex(currentIndex - 1); } function startAutoPlay() { timer setInterval(nextSlide, autoPlayInterval); } function stopAutoPlay() { clearInterval(timer); timer null; } prevBtn.addEventListener(click, () { prevSlide(); stopAutoPlay(); startAutoPlay(); }); nextBtn.addEventListener(click, () { nextSlide(); stopAutoPlay(); startAutoPlay(); }); track.addEventListener(mouseenter, stopAutoPlay); track.addEventListener(mouseleave, startAutoPlay); startAutoPlay();这段代码里有个关键参数autoPlayInterval 4000也就是4秒自动切换一次。这个数字要看实际需求设置通常商城站3到5秒比较舒服学成在线这类内容站4秒合理。goToIndex方法里的边界判断让轮播到最后一张时能回到第一张形成循环。交互上鼠标悬停时暂停播放是个默认习惯。如果不暂停用户正在读Banner上的文字时突然切走体验会很差。这里用mouseenter和mouseleave两个事件绑定到track上用户进入轮播区域就暂停离开就恢复逻辑简单不产生额外变量。注意没有加“上一张/下一张”按钮的焦点管理。完整的无障碍轮播还需要处理键盘事件和aria-label但作为静态页练习先把视觉切换跑通再补无障碍也不会伤筋动骨。4.2 导航与tab切换事件委托与数据驱动思路学成在线首页通常有“最新课程”和“热门课程”之类的Tab切换。我使用事件委托来绑定点击事件而不是给每个按钮单独绑定。事件委托的好处是新增或删除Tab按钮时不需要改JavaScript代码。div classcourse-tabs button classtab-btn active>// tab.js const tabBtns document.querySelectorAll(.tab-btn); const tabContents document.querySelectorAll(.tab-content); document.querySelector(.course-tabs).addEventListener(click, (event) { const target event.target.closest(.tab-btn); if (!target) return; tabBtns.forEach(btn btn.classList.remove(active)); target.classList.add(active); const tabKey target.dataset.tab; tabContents.forEach(content { content.style.display content.id tabKey ? block : none; }); });这段JavaScript里最有用的写法是event.target.closest(.tab-btn)。用户可能点击到button内部的文字也可能点的是图标或内层元素closest()能直接从实际点击的元素向上查找到离它最近的Tab按钮一条代码解决事件目标不准的问题。>/* index.css 末尾追加 */ media (max-width: 768px) { .container { width: 100%; padding: 0 16px; } .site-header { height: auto; flex-wrap: wrap; padding: 12px 16px; } .main-nav { order: 3; width: 100%; overflow-x: auto; margin-top: 8px; } .main-nav ul { gap: 16px; flex-wrap: nowrap; white-space: nowrap; } .course-grid { grid-template-columns: repeat(2, 1fr); gap: 12px; } .search-box { margin-left: auto; } } media (max-width: 480px) { .course-grid { grid-template-columns: 1fr; } .search-box input { width: 120px; } }这里有没有必要用rem我的看法是如果页面主要是PC内容用px加媒体查询就够了如果页面内容大量文字排版或者面向长期运营内容更新rem才有优势。学成在线这类页面固定px布局配合断点调整实现和维护成本都更低。移动端菜单没有单独做汉堡按钮而是把导航变成横向可滑动的条放在头部第二行。这种处理对内容和导航都不多的页面非常实用省掉了一整套菜单展开与关闭的JavaScript逻辑。5. 边写边避坑PSD切图与页面还原的常见问题排查5.1 字体差距过大系统字体栈与设计稿的差距现象页面写完总体布局没问题但所有文字的字号和间距跟设计稿一比明显不对劲有的地方窄了有的地方行距高了。原因设计稿里用的字体在某台电脑上没有安装浏览器自动降级成另一个字体大部分中文字体的度量标准不同同一字号渲染出的高度和宽度差异很大。解决统一设置字体栈把市面上主流系统里都常见的中文字体按优先级写进font-familybody { font-family: -apple-system, BlinkMacSystemFont, Helvetica Neue, PingFang SC, Microsoft YaHei, sans-serif; }这里顺序很重要。-apple-system和BlinkMacSystemFont覆盖苹果系设备和Chrome浏览器PingFang SC表示Mac上的中文Microsoft YaHei是Windows上的微软雅黑最后sans-serif兜底保证任何系统都有可用字体。如果设计稿用了特殊字体比如思路极粗的黑体先确认字体版权再考虑用图片代替还是找免费字体。5.2 背景图和切图之间有白边、黑边现象图标或图片放到页面上后边缘总有一圈1像素左右的白色或黑色细线尤其在深色背景下特别明显。原因PSD里元素使用了羽化、描边或者没有严格贴着图形边缘裁切。导出时把半透明像素和背景残留一起导出来了。解决返回到PSD里选择目标图层后先用“选择”菜单里的“载入选区”把图层轮廓选出来再执行“选择”“修改”“收缩”1到2像素然后按选区导出。注意描边特效要在图层样式里检查如果是不需要的描边就先关闭再导出。注意收缩选区只对实底图形有效如果图标本身有半透明发光效果收缩会切掉光晕。这类素材更要保留原始图层直接导出不要在切图上做额外抠边。5.3 雪碧图坐标错位background-position的换算错误现象页面加载后所有小图标都是错位的或显示成别的图标背景位置完全对不上。原因图标在雪碧图中的位置和CSS里的background-position坐标不匹配或者错误地把2x图的坐标用在了1x图上。解决不要手写坐标。现在有很多合成雪碧图的工具比如打包脚本自动测量后生成坐标表。手动排版的唯一靠谱做法是在PSD里把每个图标排布在不同的小格子里格子大小统一比如全部32x32然后用Python或在线工具量取每个图标左上角的坐标再填进CSS。更省事的是完全放弃雪碧图现在HTTP/2和多通路的时代十几个小图标逐个加载的代价已经很低学成在线这类页面其实没必要合并雪碧图分开加载反而免除计算坐标这一步。5.4 垂直间距不对和margin塌陷现象两个区块之间的间距比设计稿大或者小而且margin设置了却没有生效。原因经典的外边距合并。CSS里上下相邻的块级元素它们的margin会合并取最大值而不是两个值相加父子元素也会有类似的塌陷问题。解决在这个项目里推荐使用display: grid或flex来避免间距问题它们内部的子项间距用gap属性控制完全不触发margin合并。对于必须使用margin的地方给父级设置display: flow-root修掉塌陷。flow-root是专门用来形成独立格式化上下文的属性副作用比overflow: hidden小得多。5.5 图片加载慢和路径失效现象页面图片显示成碎图标或者背景图加载很慢甚至排版在图片加载后跳动。原因路径写错了要么是HTML文件深层目录下找不到相对路径的图片要么是图片文件太大没做压缩。解决路径上我统一用项目内根路径例如/assets/images/banner-main.jpg这种以/开头的写法确保网页部署在任何子目录也不会失效。图片体积方面切图后必须用压缩工具处理一遍PNG压到百KB以内JPG压到两百KB以内超过这个量就要检查是不是导出尺寸太大了。CSS里的大背景图不要直接平铺原图优先考虑用压缩过的WebP格式体积能再降一半。6. 像素级验收与自动化发布你最后需要的三个小技巧页面写完离交付还差一步验收。我习惯在本地把这几个动作养成固定套路这套流程能挡住九成以上的低级错误。第一个技巧是浏览器像素比对。打开Chrome DevTools把页面缩放到设计稿的相同宽度然后利用扩展工具把PSD导出的大图放在页面顶层调整透明度逐区块比对。重点检查三处导航高度、卡片间距、页脚边距。肉眼看到差距后直接在DevTools里改padding和margin校验改完再回代码文件同步数值。不要凭感觉直接改数值那样大概率越改越偏。第二个技巧是自动化压缩。如果项目里装了Node环境我用一套简单Gulp脚本自动处理图片和CSS前缀几行代码就能把重复劳动全部接管const gulp require(gulp); const imagemin require(gulp-imagemin); const autoprefixer require(autoprefixer); gulp.task(images, () gulp.src(assets/images/**/*) .pipe(imagemin([ imagemin.optipng({ optimizationLevel: 3 }), imagemin.mozjpeg({ quality: 75 }) ])) .pipe(gulp.dest(dist/assets/images)) );这段代码里optimizationLevel是PNG压缩级别不是越高越好5级以上压得很慢且收益很小3级是日常推荐值JPG的quality设为75视觉上几乎看不出损失体积能比原图缩小一半以上。跑完这套任务把dist目录里的文件直接打包发布即可。第三个技巧是发布前检查清单。我会依次做五件事无痕窗口打开页面首屏确认图片不裂、控制台没有报错切换iPhone和Android尺寸确认断点没有横向滚动条把当前页面的HTML源码放进W3C校验器检查标签嵌套用Git在本地提交一次完整代码顺便检查有没有残留的绝对路径最后把页面部署到测试环境用真机走一遍轮播和Tab交互。说句实话我第一次做这类切图项目时就是图快直接拿PSD里的原图当网页图片用页面臃肿到加载半天。后来养成了切图前列清单、出图后统一压缩、写完页面逐区块比对的习惯交付质量和返工次数都明显改善。做前端这种偏实操的活与其追求新框架花活不如先把这一步一步的落地动作练熟。希望帮到你。本文还有配套的精品资源点击获取