转载请注明出处,未经同意,不可修改文章内容。
🔥🔥🔥"前端一万小时"两大明星专栏------"从零基础到轻松就业"、"前端面试刷题",已于本月大改版,合二为一,干货满满,欢迎点击公众号菜单栏各模块了解。
1 样式组件------HeaderWrapper 和 Logo 相关
❗️CSS 声明顺序(在正式开始编写项目的 CSS 之前,啰嗦几句------请尽量养成好的习惯,相关的属性声明应当归为一组,以后的工作定能左右逢源):
- Position 定位
- Box model 盒模型
- Typography 印刷
- Visual 视觉
💡例如:
css
.declaration-order {
/* Positioning 定位 */
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
z-index: 100;
/* Box-model 盒模型 */
display: block;
float: right;
width: 100px;
height: 100px;
/* Typography 印刷 */
font: normal 13px "Helvetica Neue", sans-serif;
line-height: 1.5;
color: #333;
text-align: center;
/* Visual 视觉 */
background-color: #f5f5f5;
border: 1px solid #e5e5e5;
border-radius: 3px;
/* Misc 杂项 */
opacity: 1;
}
由于"定位"(Position)可以从正常的文档流中移除元素,并且还能覆盖"盒模型"(Box model)相关的样式,因此排在首位。
"盒模型"(Box model)排在第二位,因为它决定了组件的尺寸和位置。
其他属性只是影响组件的"内部",或者是不影响前两组属性,因此排在后面。
1️⃣在 header 目录下创建一个 style.js
文件,用于写 header 部分的"样式组件":
2️⃣编写下图红框部分的 JSX 和 styled-components:
- JSX------打开 header 目录下的
index.js
文件:
jsx
import React, {Component} from "react";
// ❗️2️⃣-②:从当前目录下的 style.js 中引入各个定义好的"样式组件";
import {
HeaderWrapper,
Logo,
} from "./style";
class Header extends Component {
render() {
// ❗️❗️❗️2️⃣-①:将"标签"拆分成一个个的"样式组件";
return (
<HeaderWrapper>
<Logo>
<img src="https://qdywxs.github.io/jianshu-images/logo.png" alt="logo" />
</Logo>
</HeaderWrapper>
)
}
}
export default Header;
- styled-components------打开 header 目录下的
style.js
文件:
javascript
import styled from "styled-components"; /*
2️⃣-③:首先,从 styled-components 中
引入 styled 方法;
*/
// 2️⃣-④:整个 <header> 部分的样式可以写成一个大的 Header 组件;
export const HeaderWrapper = styled.header`
box-sizing: content-box;
padding: 0 20px;
height: 56px;
line-height: 56px; /*
❗️header 的高度可以写一个固定的高度,也可以由内容去撑开。
但当前的场景下,写一个固定的高度比较合适。因为由内容去撑开的话,
很容易导致上下的缝隙不太合适。
如果写一个固定的 height,然后 line-height 等于这个 height,
那很容易就实现里边的整个元素都处于这个区域的正中心!
*/
/*
❗️但要注意:由于继承性,它的子元素也会去继承 line-height 等于
56px。故,若子元素有特别需要,要单独设置 line-height 的值!
*/
border-bottom: 1px solid #eee;
&:after {
content: "";
display: block;
clear: both;
} /*
❗️给 header 清除浮动,当然也可以在 JSX 中,给 HeaderWrapper 加一个"类"clearfix,
用"通用样式"清除浮动。之所以加在这里,主要是为了"好看"。
*/
`;
// 2️⃣-⑤:"简书"这个 logo 可以写成一个可点击的(a 标签)Logo 组件;
export const Logo = styled.a.attrs({
href: "/"
})`
float: left;
height: 56px;
& > img {
height: 50px; /*
❗️由于 header 一开始就设置了 height=line-height,
故 logo 图片的高度设置好后,宽度就自适应了。
*/
}
`;
返回页面查看效果:
2 样式组件------Navbar 相关
3️⃣编写下图红框部分的 JSX 和 styled-components:
- JSX------打开 header 目录下的
index.js
文件:
jsx
import React, {Component} from "react";
import {
HeaderWrapper,
Logo,
Navbar,
ItemList,
LinkList,
} from "./style";
class Header extends Component {
render() {
return (
<HeaderWrapper>
<Logo>
<img src="https://qdywxs.github.io/jianshu-images/logo.png" alt="logo" />
</Logo>
<Navbar className="clearfix">
<ItemList className="active">
<LinkList href="/">
首页
</LinkList>
</ItemList>
<ItemList>
<LinkList href="/">
下载APP
</LinkList>
</ItemList>
</Navbar>
</HeaderWrapper>
)
}
}
export default Header;
- styled-components------打开 header 目录下的
style.js
文件:
javascript
import styled from "styled-components";
export const HeaderWrapper = styled.header`
box-sizing: content-box;
padding: 0 20px;
height: 56px;
line-height: 56px;
border-bottom: 1px solid #eee;
&:after {
content: "";
display: block;
clear: both;
}
`;
export const Logo = styled.a.attrs({
href: "/"
})`
float: left;
height: 56px;
& > img {
height: 50px;
}
`;
// 🚀Navbar 相关~
export const Navbar = styled.ul`
float: left;
`;
export const ItemList = styled.li`
float: left;
padding: 0 4px; /*
❗️给 li 加点 padding,可以使相邻的两个可点击的按钮中间有一个缝。
当然,要实现这个功能,给 li 加 margin,或给 a 加 margin 也可以!
*/
&.active > a{
color: #e86f5e;
}
&.active > a:hover {
background-color: #fff;
}
`;
export const LinkList = styled.a`
display: block;
padding: 0 10px;
font-size: 17px;
line-height: 56px;
color: #333;
&:hover {
background-color: #eee;
}
`;
返回页面查看效果:
3 样式组件------SearchArea 相关
4️⃣编写下图红框部分的 JSX 和 styled-components:
- JSX------打开 header 目录下的
index.js
文件:
jsx
import React, {Component} from "react";
import {
HeaderWrapper,
Logo,
Navbar,
ItemList,
LinkList,
SearchArea,
SearchInput,
SearchPanel,
PanelTitle,
PanelChange,
PanelLabels,
LabelLink,
} from "./style";
class Header extends Component {
render() {
return (
<HeaderWrapper>
<Logo>
<img src="https://qdywxs.github.io/jianshu-images/logo.png" alt="logo" />
</Logo>
<Navbar className="clearfix">
<ItemList className="active">
<LinkList href="/">
首页
</LinkList>
</ItemList>
<ItemList>
<LinkList href="/">
下载APP
</LinkList>
</ItemList>
</Navbar>
<SearchArea>
<SearchInput />
<span className="iconfont icon-search"></span>
<SearchPanel>
<PanelTitle>
热门搜索
<PanelChange>
<span className="iconfont icon-refresh"></span>
换一批
</PanelChange>
</PanelTitle>
<PanelLabels className="clearfix">
<LabelLink href="/">
区块链
</LabelLink>
<LabelLink href="/">
故事
</LabelLink>
<LabelLink href="/">
小程序
</LabelLink>
<LabelLink href="/">
前端一万小时
</LabelLink>
</PanelLabels>
</SearchPanel>
</SearchArea>
</HeaderWrapper>
)
}
}
export default Header;
- styled-components------打开 header 目录下的
style.js
文件:
javascript
import styled from "styled-components";
export const HeaderWrapper = styled.header`
box-sizing: content-box;
padding: 0 20px;
height: 56px;
line-height: 56px;
border-bottom: 1px solid #eee;
&:after {
content: "";
display: block;
clear: both;
}
`;
export const Logo = styled.a.attrs({
href: "/"
})`
float: left;
height: 56px;
& > img {
height: 50px;
}
`;
// 🚀Navbar 相关~
export const Navbar = styled.ul`
float: left;
`;
export const ItemList = styled.li`
float: left;
padding: 0 4px;
&.active > a{
color: #e86f5e;
}
&.active > a:hover {
background-color: #fff;
}
`;
export const LinkList = styled.a`
display: block;
padding: 0 10px;
font-size: 17px;
line-height: 56px;
color: #333;
&:hover {
background-color: #eee;
}
`;
// 🚀SearchArea 相关~
export const SearchArea = styled.div`
position: relative;
float: left; /* ❗️先成为浮动元素,变小。 */
margin-left: 30px;
.icon-search {
position: absolute;
top: 10px;
right: 10px;
width: 32px;
height: 32px;
color: #aaa;
line-height: 32px; /*
❗️对图标的样式进行重置,height=line-height 实现字体图标
在设置的盒子里"垂直居中"。
*/
text-align: center; /* ❗️使图标在其盒子里水平居中。 */
border-radius: 50%;
}
`;
export const SearchInput = styled.input.attrs({
placeholder: "搜索"
})`
width: 200px;
padding: 0 20px;
font-size: 15px;
line-height: 36px;
border: none; /*
❗️一般来说,input 被点击的时候,会出现一个蓝色的框,
这里我们不需要这个默认样式。
*/
background-color: #eee;
border-radius: 18px;
outline: none;
transition: all .3s; /* ❗️添加"过渡"动画。 */
&:focus {
width: 240px;
}
&:focus~div {
display: block;
}
&:focus + .icon-search {
color: #fff;
background-color: #969696;
}
`;
export const SearchPanel = styled.div`
position: absolute;
z-index: 1;
width: 250px;
padding: 16px;
line-height: 1; /* ❗️重置行高。 */
background-color: #fff;
border-radius: 6px;
box-shadow: 0 1px 4px 2px rgba(0,0,0,0.1);
/*
❗️box-shadow 语法规则:
"X 偏移量 | Y 偏移量 | 阴影模糊面积 | 阴影扩散半径 | 阴影颜色"
*/
display: none;
&::before { /* ❗️做出向上的"三角"。 */
content: "";
display: block;
position: absolute;
top: -5px;
left: 30px;
width: 14px;
height: 14px;
background-color: #fff;
transform: rotateZ(45deg);
box-shadow: -2px -2px 2px -2px rgba(0,0,0,0.1);
}
`;
export const PanelTitle = styled.h3`
color: #888;
font-weight: normal;
`;
export const PanelChange = styled.div`
float: right;
color: #888;
font-size: 13px;
font-weight: normal;
cursor: pointer; /* ❗️做一个"手"的形状。 */
`;
export const PanelLabels = styled.div`
margin-top: 10px;
`;
export const LabelLink = styled.a`
float: left;
padding: 2px 4px;
margin: 10px 10px 0 0;
color: #888;
border: 1px solid #ccc;
border-radius: 4px;
&:hover {
border-color: #888;
}
`;
返回页面查看效果: ❗️整体效果没有问题,但请将进度条拉到最后几秒(0:22),这里是有问题的:
按照"简书"官网的交互逻辑,除了点击 input 框,下边的 panel 会出现外。当鼠标滑动至 panel 区域后,鼠标做任何点击,panel 区域也都不会隐藏(我们现在实现的效果会隐藏,因为我们只是在 input 上设置了 :focus
,当点击 panel 区域时,input 框就"失焦"了,"失焦"的后果就是 panel 区域再次隐藏),只有当鼠标离开 panel 区域后,panel 才会隐藏!
对于视频中官网这个效果,我们直接用 CSS 中的"伪类选择器"也能很轻松地实现。继续在 header 目录下的 style.js
中,定位到 SearchPanel
样式组件:
javascript
export const SearchPanel = styled.div`
position: absolute;
z-index: 1;
width: 250px;
padding: 16px;
line-height: 1; /* ❗️重置行高。 */
background-color: #fff;
border-radius: 6px;
box-shadow: 0 1px 4px 2px rgba(0,0,0,0.1);
display: none;
&::before {
content: "";
display: block;
position: absolute;
top: -5px;
left: 30px;
width: 14px;
height: 14px;
background-color: #fff;
transform: rotateZ(45deg);
box-shadow: -2px -2px 2px -2px rgba(0,0,0,0.1);
}
&:hover { /*
❗️直接在里加一个 :hover,当这个区域出现后,只要它处于 hover 状态,
则显示为 block。
*/
display: block;
}
`;
返回页面查看效果:
4 样式组件------Extra 相关
5️⃣编写下图红框部分的 JSX 和 styled-components:
- JSX------打开 header 目录下的
index.js
文件:
jsx
import React, {Component} from "react";
import {
HeaderWrapper,
Logo,
Navbar,
ItemList,
LinkList,
SearchArea,
SearchInput,
SearchPanel,
PanelTitle,
PanelChange,
PanelLabels,
LabelLink,
Extra,
ExtraLink
} from "./style";
class Header extends Component {
render() {
return (
<HeaderWrapper>
<Logo>
<img src="https://qdywxs.github.io/jianshu-images/logo.png" alt="logo" />
</Logo>
<Navbar className="clearfix">
<ItemList className="active">
<LinkList href="/">
首页
</LinkList>
</ItemList>
<ItemList>
<LinkList href="/">
下载APP
</LinkList>
</ItemList>
</Navbar>
<SearchArea>
<SearchInput />
<span className="iconfont icon-search"></span>
<SearchPanel>
<PanelTitle>
热门搜索
<PanelChange>
<span className="iconfont icon-refresh"></span>
换一批
</PanelChange>
</PanelTitle>
<PanelLabels className="clearfix">
<LabelLink href="/">
区块链
</LabelLink>
<LabelLink href="/">
故事
</LabelLink>
<LabelLink href="/">
小程序
</LabelLink>
<LabelLink href="/">
前端一万小时
</LabelLink>
</PanelLabels>
</SearchPanel>
</SearchArea>
<Extra>
<span className="iconfont icon-textsize" ></span>
<ExtraLink className="login" href="/">
登录
</ExtraLink>
<ExtraLink className="register" href="/">
注册
</ExtraLink>
<ExtraLink className="writing" href="/">
<span className="iconfont icon-pen"></span>
写文章
</ExtraLink>
</Extra>
</HeaderWrapper>
)
}
}
export default Header;
- styled-components------打开 header 目录下的
style.js
文件:
javascript
import styled from "styled-components";
export const HeaderWrapper = styled.header`
box-sizing: content-box;
padding: 0 20px;
height: 56px;
line-height: 56px;
border-bottom: 1px solid #eee;
&:after {
content: "";
display: block;
clear: both;
}
`;
export const Logo = styled.a.attrs({
href: "/"
})`
float: left;
height: 56px;
& > img {
height: 50px;
}
`;
// 🚀Navbar 相关~
export const Navbar = styled.ul`
float: left;
`;
export const ItemList = styled.li`
float: left;
padding: 0 4px;
&.active > a{
color: #e86f5e;
}
&.active > a:hover {
background-color: #fff;
}
`;
export const LinkList = styled.a`
display: block;
padding: 0 10px;
font-size: 17px;
line-height: 56px;
color: #333;
&:hover {
background-color: #eee;
}
`;
// 🚀SearchArea 相关~
export const SearchArea = styled.div`
position: relative;
float: left;
margin-left: 30px;
.icon-search {
position: absolute;
top: 10px;
right: 10px;
width: 32px;
height: 32px;
color: #aaa;
line-height: 32px;
text-align: center;
border-radius: 50%;
}
`;
export const SearchInput = styled.input.attrs({
placeholder: "搜索"
})`
width: 200px;
padding: 0 20px;
font-size: 15px;
line-height: 36px;
border: none;
background-color: #eee;
border-radius: 18px;
outline: none;
transition: all .3s;
&:focus {
width: 240px;
}
&:focus~div {
display: block;
}
&:focus + .icon-search {
color: #fff;
background-color: #969696;
}
`;
export const SearchPanel = styled.div`
position: absolute;
z-index: 1;
width: 250px;
padding: 16px;
line-height: 1;
background-color: #fff;
border-radius: 6px;
box-shadow: 0 1px 4px 2px rgba(0,0,0,0.1);
display: none;
&::before {
content: "";
display: block;
position: absolute;
top: -5px;
left: 30px;
width: 14px;
height: 14px;
background-color: #fff;
transform: rotateZ(45deg);
box-shadow: -2px -2px 2px -2px rgba(0,0,0,0.1);
}
&:hover {
display: block;
}
`;
export const PanelTitle = styled.h3`
color: #888;
font-weight: normal;
`;
export const PanelChange = styled.div`
float: right;
color: #888;
font-size: 13px;
font-weight: normal;
cursor: pointer;
`;
export const PanelLabels = styled.div`
margin-top: 10px;
`;
export const LabelLink = styled.a`
float: left;
padding: 2px 4px;
margin: 10px 10px 0 0;
color: #888;
border: 1px solid #ccc;
border-radius: 4px;
&:hover {
border-color: #888;
}
`;
// 🚀Extra 相关~
export const Extra = styled.div`
float: right;
.icon-textsize {
float: left;
font-size: 25px;
color: #888;
cursor: pointer;
}
`;
export const ExtraLink = styled.a`
float: left;
padding: 10px 20px;
margin: 10px 0 0 20px;
font-size: 15px;
line-height: 1;
&.login {
color: #888;
background-color: #fff;
}
&.register {
color: #e56e5d
border: 1px solid #e56e5d
border-radius: 30px;
}
&.writing {
color: #fff;
background-color: #e56e5d;
border-radius: 30px;
}
`;
返回页面查看效果:
祝好,qdywxs ♥ you!