目录

WordPress 对非计专的普通人还是很友好的,除了安装时需要接触一些 Linux 命令,其余情况下不太需要接触代码。就算想赋予网站一个美丽的皮囊也有一堆现成的主题可用。不过毕竟不是亲生的,再完美的主题也有不合己意的地方。小修小改倒也简单,但改的东西越多,需要的知识也就越多,这也是这篇记录的来由。

我做过的修改大致包括网站布局修改、引入数学公式渲染、引入代码块渲染、添加文章目录及平滑滚动跳转、移动端菜单及动画推倒重做、css 动画、css 媒体查询、资源按需加载等,可以说与原主题已经天壤之别了。其中相当大一部分修改本文没写,具体可以参考我 github 上的提交记录:主题文件修改额外 css

css、html 和 JavaScript

所以先说我以前一直不知道是个什么东西的 style.css 吧,这个文件是 wordpress 主题的 css 样式默认存放文件,那么 css 样式是什么?要想回答这个问题就要扯到 css、html 和 JavaScript 三者的关系了。打个比方吧,假如我们用上面提到的三个东西去描述一个姑娘,那么 html 就像她的骨骼,css 则代表她的皮相。html 文件内部的语句描述了一个人有一个头,头在肩膀上,肩膀下是胸腔,胸腔里有个小心脏之类的基本结构和它们的层级关系,css 则根据 html 里描述的基本结构,分别定义各个结构的样式,比如 脸 { 好看; } 个子 { 高; } 头发 { 又黑又长; }; 。至于 JavaScript,则是负责逻辑的工作,比如 if(meet boyfriend == true){ heart beat faster; }

我觉得我还挺有幽默细胞的。

那么回到网页开发上,对这三者的理解借用前辈的话总结就是:“最准确的网页设计思路是把网页分成三个层次,即:结构层(HTML)、表示层(CSS)、行为层(Javascript)。”

CSS改版内容

首先是 css,之前没经验的时候我都是直接修改主题里的 style.css 文件,这修一下那改一脚的,改到最后自己都忘了哪里改过哪里没改过。最后我才发现 WordPress 后台里 “额外 css” 这个神器,官方文档里介绍好长一段,最重要的是这句

the custom CSS you add to the CSS editor will be loaded after the theme’s original CSS which means that your rules can take precedence and override the theme’s styles.

也就是说额外 css 就像个独立的样式表,且在主题默认样式表加载之后加载,这意味着我们在额外 css 里定义的样式可以覆盖掉原样式。

下面是这次改版的额外 css。(我待会一定研究研究怎么折叠大段代码…)

/* ---------------------------------------------------------------------------------------------
   正文设置
--------------------------------------------------------------------------------------------- */

.post-content {
    font-family: 'Crimson Text', 'Times New Roman', 'Noto Serif', "Microsoft YaHei New", "Microsoft Yahei", "微软雅黑", STXihei, "华文细黑", sans-serif;
    font-size: 1.0em;
    line-height: 170%;
}

.post-content table { border: 1px solid #ddd; }
.post-content p,
.post-content blockquote,
.post-content ul,
.post-content ol,
.post-content address,
.post-content dl,
.post-content li,
.post-content .wp-caption,
.post-content pre {
    line-height: 170%;
    margin-bottom: 1.4em;
}





/* ---------------------------------------------------------------------------------------------
   各类超链接不同状态下的样式 
--------------------------------------------------------------------------------------------- */

/* 文章内链接样式 */
.post-content a {
    color:#444;
    border-bottom: 1px solid #444;
}

/* 除“阅读更多”以外的链接的悬停样式 */
.post-content a:not(.more-link):hover {  
    color:#999;
    border-bottom: 1px solid #999;
    transition-property: border-color, color;
    transition-duration: 0.2s;
    transition-timing-function: ease-in-out;
}

/* “下一页”链接 */
.archive-nav {
    padding: 60px 0;
    border-top: 1px solid #eee;
}
.archive-nav a {
    color: #444;
    vertical-align: middle;
    -webkit-transform: perspective(1px) translateZ(0);
    transform: perspective(1px) translateZ(0);
    position: relative;
    overflow: hidden;
}





/* ---------------------------------------------------------------------------------------------
   “阅读更多”按钮,“提交评论”按钮,样式自定义
--------------------------------------------------------------------------------------------- */

.post-content a.more-link {
    display: inline-block;
    padding: 0px 0px 6px;
    font-size: 1.1rem;
    background: #fff;
    line-height: 1;
    font-weight: normal;
    color: #444;
    font-family: 'Noto Serif', sans-serif;
    -webkit-transform: perspective(1px) translateZ(0);
    transform: perspective(1px) translateZ(0);
    position: relative;
    overflow: hidden;
}

.archive-nav a:before,
.post-content a.more-link:before {
    content: "";
    position: absolute;
    z-index: -1;
    left: 0;
    right: 100%;
    bottom: 0;
    background: #999;
    height: 1px;
    -webkit-transition-property: right;
    transition-property: right;
    -webkit-transition-duration: 0.2s;
    transition-duration: 0.2s;
    -webkit-transition-timing-function: ease-out;
    transition-timing-function: ease-out;
}

.archive-nav a:hover,
.post-content a.more-link:hover {
    background:#fff;
    color:#999;
    transition-property: color;
    transition-duration: 0.2s;
    transition-timing-function: ease-out;
}

.archive-nav a:hover:before,
.archive-nav a:focus:before,
.archive-nav a:active:before,
.post-content a.more-link:hover:before,
.post-content a.more-link:focus:before,
.post-content a.more-link:active:before {
    right: 0;
}

.comment-form input[type="submit"] {
    display: inline-block;
    padding: 14px 16px;
    background: #fff;
    border: 2px solid #121212;
    line-height: 1;
    font-weight: 700;
    color: #121212;
    letter-spacing:1px
}
.comment-form input[type="submit"]:hover {
    background: #121212;
    color: #fff;
}





/* ---------------------------------------------------------------------------------------------
   “引用”文章样式自定义 
--------------------------------------------------------------------------------------------- */

.post-quote {
    background: #fff;
    color: #444;
    font-style: italic;
    line-height: 170%;
    position: relative;
    padding-left: 60px;
}

.post-quote:before {
    content: '”';
    display: block;
    width: auto;
    font-family: 'Georgia', 'Times New Roman', serif;
    font-style: normal;
    font-size: 4em;
    line-height: 72px;
    font-weight: 700;
    color: #888;
    text-align: center;
    -webkit-font-smoothing: antialiased;
    position: absolute;
    top: 48px;
    left: -2px;
    -webkit-transform: translateZ(0);
    transform: translateZ(0);
    -webkit-transition-duration: 0.2s;
    transition-duration: 0.2s;
    -webkit-transition-property: transform;
    transition-property: transform;
    -webkit-transition-timing-function: ease-out;
    transition-timing-function: ease-out;
}

a.post-quote:hover { 
    background: #fff; 
    color: #121212;
}

a.post-quote:hover:before,
a.post-quote:focus:before,
a.post-quote:active:before { 
  -webkit-transform: rotate(20deg);
  transform: rotate(20deg);
}

.post-content blockquote:before { top: 27px; }





/* ---------------------------------------------------------------------------------------------
   杂项样式自定义
--------------------------------------------------------------------------------------------- */

/* 博客加载时,wrapper淡入动画 */
@keyframes fade-in {
0%{opacity:0;transform:translateY(50px)}
50%{opacity:0;transform:translateY(25px)}
100%{opacity:1;transform:translateY(0)}
}
@-webkit-keyframes fade-in {
0%{opacity:0;transform:translateY(50px)}
50%{opacity:0;transform:translateY(25px)}
100%{opacity:1;transform:translateY(0)}
}
#wrapper {
    animation: fade-in;
    animation-duration: 1s;
    -webkit-animation:fade-in 1s;
}

/* 搜索框 */
.post-content input[type="search"] { border: 2px solid #eee; }
.post-content textarea:focus,
.post-content input[type="search"]:focus {
    border: 2px solid #ccc; 
    background: #fff;
    transition-property: border-color;
    transition-duration: 0.2s;
    transition-timing-function: ease-in-out;
}

/* 文章信息栏 */
.post-meta-toggle { background: #eee; }
.post-meta-toggle p { color: #666; }
.post-meta-toggle .bar { background: #666; }
.post-meta-toggle.active { background: #444; }
.post-meta-toggle.active p { color: #fff; }
.post-meta-toggle.active .bar { background: #fff; }
.post-meta-inner {
    background: #444;
    color: #fff;
}
.post-meta-inner a { color: #fff; }
.post-meta-inner a:hover { color: #999; }
.post-meta-inner p strong { color: #999; }
.post-nav { border-top: 2px solid #555; }

/* 杂项颜色 */
.post-title { color: #222; }
.post-title a { color: #222; }
.comments-title { color: #222; }
.comment-reply-title { color: #222; }

/* 分割线 */
.post.format-quote + .post { border-top: 1px solid #eee; }
.post + .post { border-top: 1px solid #eee; }
.comments-title {
    border-top: 2px solid #eee;
    padding-top: 100px;
    width: 100%;
}





/* ---------------------------------------------------------------------------------------------
   媒体查询(不同分辨率)
--------------------------------------------------------------------------------------------- */

@media (min-width: 1200px) {

    /* 电脑端行高 */
    .post-content p,
    .post-content blockquote,
    .post-content ul,
    .post-content ol,
    .post-content address,
    .post-content dl,
    .post-content li,
    .post-content .wp-caption,
    .post-content pre {
        line-height: 180%;
    }

    /* body左边距、背景 */
    body{
        padding-left: 380px;
        background: #fff;
    }

    /* sidebar宽度等于body左边距 */
    .sidebar {
        width: 380px;
        padding: 80px 50px;
    }

    /* 页面容器外边距 */
    .wrapper { margin: 30px 0 0 0; }

    /* 页面容器内的元素宽度、最大宽度、外边距 */
    .wrapper-inner {
        width: 100%;
        max-width: 650px;
        margin: 0 auto;
    }

    /* 文章容器内边距,与wrapper外边距相加后
    和sidebar内元素等高,这样对称(强迫症没救了) */
    .post-inner { padding: 50px 0; }

    /* 侧边栏内各元素 */
    .blog-title { text-align: left; }
    .blog-title:after { width: 95%; }
    .main-menu li { text-align: left; }

    /* 各文章间距 */
    .posts .post,
    .posts .page { margin-bottom: 0; }

    /* 微调段落间距 */
    /* 利用了css里的兄弟类选择器,以区别对待文章内和文章外的特色图片 */
    .post-content { margin-bottom: -3px; }
    .post-content a.more-link { margin-top: 2px; }
    .featured-media img { margin: 80px auto 0; }
    .post-header + .featured-media img { margin-top: 0px; }
    .featured-media + .post-content { margin-top: 32px; }
    .post-header {
        margin-bottom: 32px;
        margin-top: 8px;
    }

    /* “页码”边距 */
    .page-title { margin-bottom: 30px; }
    .page-title h4 { padding-top: 50px; }

    /* 文章评论区边距 */
    .comment-respond { padding: 60px 0; }

}

@media (max-width: 1200px) {    

    /* body左边距、背景 */
    body{
        padding-left: 350px;
        background: #fff;
    }

    /* sidebar宽度等于body左边距 */
    .sidebar {
        width: 350px;
        padding: 80px 50px;
    }

    /* 页面容器内的元素宽度、最大宽度、外边距 */
    .wrapper-inner {
        width: 100%;
        max-width: 600px;
        margin: 0 auto;
    }

    /* 之后部分简单复制粘贴 min-width: 1200px */
    .wrapper { margin: 30px 0 0 0; }                /* 页面容器外边距 */
    .post-inner { padding: 50px 0; }                /* 文章容器内边距 */   
    .blog-title { text-align: left; }               /* 侧边栏内各元素边距 */
    .blog-title:after { width: 95%; }               /* 侧边栏内各元素边距 */
    .comment-respond { padding: 60px 0; }           /* 文章评论区边距 */
    .main-menu li { text-align: left; }
    .posts .post,                                   /* 各文章间距 */
    .posts .page { margin-bottom: 0; }
    .page-title { margin-bottom: 30px; }            /* “页码”边距 */
    .page-title h4 { padding-top: 50px; }
    .post-content { margin-bottom: -3px; }          /* 微调段落间距 */
    .post-content a.more-link { margin-top: 2px; }
    .featured-media img { margin: 80px auto 0; }
    .post-header + .featured-media img { margin-top: 0px; }
    .featured-media + .post-content { margin-top: 32px; }
    .post-header {
        margin-bottom: 32px;
        margin-top: 6px;
    }

}

@media (max-width: 1100px) {

    body{
        padding-left: 320px;
    }

    .sidebar {
        width: 320px;
        padding: 80px 40px;
    }

    .wrapper-inner {
        width: 100%;
        max-width: 560px;
        margin: 0 auto;
    }

}


@media (max-width: 1000px) {

    body{
        padding-left: 300px;
    }

    .sidebar {
        width: 300px;
        padding: 80px 40px;
    }

}


@media (max-width: 930px) {

    /* 移动设备侧边栏改为顶部导航 */
    body{ padding-left: 0px; }
    .wrapper { margin: 86px 0 0 0; }

    /* 注意,前面一直是通过“wrapperinner”调节文章边距,
    但从此处往下,重置了“wrapperinner”属性,并开始通过“post-inner”来调节文章边距,
    这样可以以最小代价保障文章分割线、文章评论区、“post-meta”、“引用样式”及“特色图片”在移动设备上显示正常。 */
    /* 我聪明死了 */
    .wrapper-inner {
        width: 100%;
        max-width: 100%;
        margin: 0 auto;
    }
    .post-inner {
        width: auto;
        max-width: 85%;
        margin: 0 auto;
    }

    /* 需要与post-inner宽度平齐的元素 */
    .post-quote:before { left: 7%; }
    .archive-nav { padding: 60px 7.5%; }
    .comments-title-container { margin: 8% 7.5%; }
    .comment-respond { padding: 60px 7.5%; }
    .sidebar-inner {
        width: auto;
        max-width: 85%; 
        margin: 0 auto;
    }

    /* “页码”边距 */
    .page-title { margin-bottom: 0px; }
    .page-title h4 {
        padding-top: 70px;
        border-bottom: 1px solid #eee;
        padding-bottom: 70px
    }

    /* 文章内部“特色图片“全屏展示 */
    .featured-media img {
        width: 100%;
        margin: 0 auto;
    }

    /* 顶部导航栏自动显示与隐藏动画 */
    .sidebar {
        width: 100%;
        padding: 30px 0;
        position: fixed;    
        top: 0;
        z-index: 99999;
        -webkit-transform: translate3d(0);
        -moz-transform: translate3d(0);
        -ms-transform: translate3d(0);
        -o-transform: translate3d(0);
        transform: translate3d(0);
        -webkit-transition: transform 0.5s ease-out;
        -moz-transition: transform 0.5s ease-out;
        -o-transition: transform 0.5s ease-out;
        transition: transform 0.5s ease-out;
    }

    .sidebar.is-hidden {
        -webkit-transform: translate3d(0,-100%,0);
        -moz-transform: translate3d(0,-100%,0);
        -ms-transform: translate3d(0,-100%,0);
        -o-transform: translate3d(0,-100%,0);
        transform: translate3d(0,-100%,0);
    }

    .mobile-menu { 
        background: #121212;
        color: #fff;
        overflow: auto;
        padding: 0 7.5% 0 7.5%;         /* 需要与post-inner宽度平齐的元素 */
        position: absolute;
            top: 85px;
            bottom: calc(100% - 387px); /* 拿尺子搁屏幕上量的... */
            left: 0;
            right: 0;
        z-index: 10000;
    }

}


@media (max-width: 700px) {

    .post-inner {
        width: auto;
        max-width: 90%;
        padding: 40px 0;
        margin: 0 auto;
    }

    /* 需要与post-inner宽度平齐的元素 */
    .mobile-menu { padding: 0 5% 0 5%; }
    .archive-nav { padding: 60px 5%; }
    .post-quote { padding-left: 50px; }
    .post-quote:before { left: 4.5%; top: 38px;}
    .comments-title-container { margin: 0; }
    .comment-respond { padding: 40px 5%; }
    .sidebar-inner {
        width: auto;
        max-width: 90%; 
        margin: 0 auto;
    }

    /* “页码”边距 */
    .page-title h4 {
        padding-top: 60px;
        padding-bottom: 60px
    }

    /* 取消个别分割线(移动端显示效果不佳) */
    .comments-title { border-top: 2px solid transparent; }
    .comment-reply-title { border-top: 2px solid transparent; }

    /* 覆盖,等600px再调小这部分字体 */
    .mobile-menu a { font-size: 16px; }

}

@media (max-width: 600px) {

    .sidebar { padding: 20px 0; }
    .wrapper { margin: 64px 0 0 0; }

    .post-content a.more-link { font-size: 1.0rem; }
    .comment-form input[type="submit"] { padding: 12px 14px; }
    .post-quote { padding-left: 50px; }
    .post-quote:before {
        top: 36px;
        left: 4.5%;
    }

    .mobile-menu {
        top: 63px;
        bottom: calc(100% - 337px);
    }

    /* 微调段落间距 */
    .post-header { margin-bottom: 26px; }
    .post-header + .featured-media { margin-top: -10px; }
    .featured-media + .post-content { margin-top: 22px; }
    .post-content a.more-link { margin-top: 0px; }

    /* “页码”边距 */
    .page-title h4 {
        padding-top: 50px;
        padding-bottom: 50px
    }

    /* 到600px了,调小 */
    .mobile-menu a { font-size: 14px; }

}

@media (max-width: 500px) {

    .wrapper { margin: 60px 0 0 0; }
    .mobile-menu { top: 59px; }

    /* “页码”边距 */
    .page-title h4 {
        padding-top: 40px;
        padding-bottom: 40px
    }

}

@media ( max-width: 400px ) {

    .post-quote { padding-left: 45px; }

}





/* ---------------------------------------------------------------------------------------------
   媒体查询(不同移动平台)
--------------------------------------------------------------------------------------------- */

/* 针对除ios以外的其他移动设备 */ 
@supports not (-webkit-overflow-scrolling: touch) {

    .archive-nav a,
    .more-link {
        -webkit-tap-highlight-color:rgba(255,0,0,0);
    }

}

/* 此项规则仅用于傻逼safari */
@supports (-webkit-overflow-scrolling: touch) {

    /* 菜单下拉时使body成为可点击对象并隐藏点击遮罩 */
    body.enable-click {
        cursor: pointer;
        -webkit-tap-highlight-color:rgba(255,0,0,0);
    }

}

但实际上额外 css 改多了还是会出现改到最后自己都忘了哪改过哪没改过的情况,所以我“被迫”学习了一下版本控制工具 git,学习过程有时间我再另开一文记录。总之会用 git 后,我的后续修改就直接放 github 上了,这里是仓库地址

css 的零碎知识点太多了大概注释里都有,不想一个一个挨着写了。我就详细写两个感觉值得记的东西吧,一是动画,二是媒体查询。不过话说我还真是生在一个好时代,上面这俩东西在 CSS3 出现并普及之前大家是只能通过 JavaScript 才能实现的。但有了 CSS3 后,这两个东西变得简单了许多。

CSS动画

css 的动画分为两种,分别为“过渡(transition)”和“动画(animation)”。不过在介绍这两者之前得先说一下CSS里的2D转换(2D transform)和3D转换(3D transform),下面给出所有的2D转换和3D转换函数。

2D转换函数 描述
matrix(n,n,n,n,n,n) 定义 2D 转换,使用六个值的矩阵。
translate(x,y) 定义 2D 转换,沿着 X 和 Y 轴移动元素。
translateX(n) 定义 2D 转换,沿着 X 轴移动元素。
translateY(n) 定义 2D 转换,沿着 Y 轴移动元素。
scale(x,y) 定义 2D 缩放转换,改变元素的宽度和高度。
scaleX(n) 定义 2D 缩放转换,改变元素的宽度。
scaleY(n) 定义 2D 缩放转换,改变元素的高度。
rotate(angle) 定义 2D 旋转,在参数中规定角度。
skew(x-angle,y-angle) 定义 2D 倾斜转换,沿着 X 和 Y 轴。
skewX(angle) 定义 2D 倾斜转换,沿着 X 轴。
skewY(angle) 定义 2D 倾斜转换,沿着 Y 轴。
3D转换函数 描述
matrix3d(n,n,n,…,n) 使用 16 个值的 4×4 矩阵。
translate3d(x,y,z) 定义 3D 转化。
translateX(x) 定义 3D 转化,仅使用用于 X 轴的值。
translateY(y) 定义 3D 转化,仅使用用于 Y 轴的值。
translateZ(z) 定义 3D 转化,仅使用用于 Z 轴的值。
scale3d(x,y,z) 定义 3D 缩放转换。
scaleX(x) 定义 3D 缩放转换,通过给定一个 X 轴的值。
scaleY(y) 定义 3D 缩放转换,通过给定一个 Y 轴的值。
scaleZ(z) 定义 3D 缩放转换,通过给定一个 Z 轴的值。
rotate3d(x,y,z,angle) 定义 3D 旋转。
rotateX(angle) 定义沿 X 轴的 3D 旋转。
rotateY(angle) 定义沿 Y 轴的 3D 旋转。
rotateZ(angle) 定义沿 Z 轴的 3D 旋转。
perspective(n) 定义 3D 转换元素的透视视图。

使用方法十分简单,直接跟到我们想要实现转换效果的某个 div 元素后面就行。举个 2D 转换的例子如下(加上私有前缀防止浏览器兼容问题)

div {
    transform: rotate(30deg);
    -o-transform: rotate(30deg);        /* Opera */
    -ms-transform: rotate(30deg);       /* IE 9 */
    -moz-transform: rotate(30deg);      /* Firefox */
    -webkit-transform: rotate(30deg);   /* Safari and Chrome */
}

可惜上述代码并不能让你看到一个赏心悦目的过渡动画,而是浏览器根据你指定的转换函数与参数计算好元素位置后直接呈现元素。要想显示动画,我们需要用到 CSS3 中的 transition 和 animation。


先说 transition,transition 有五个属性(如下表格),其中为简便起见我们可以在 transition 属性中同时指定四个属性。

属性 描述
transition 简写属性,可在一个属性中设置四个过渡属性。
transition-property 规定应用过渡的 CSS 属性的名称。
transition-duration 定义过渡效果花费的时间。默认是 0。
transition-timing-function 规定过渡效果的时间曲线。默认是 “ease”。
transition-delay 规定过渡效果何时开始。默认是 0。

在使用 transition 之前首先要记住,transition 需要事件触发,所以没法在网页加载时自动发生。且 transition 只需要指定首末两个状态,中间状态由浏览器自动帮你计算。假如有这么个元素,css 属性如下(简洁起见以下不带浏览器私有前缀)

div {

    /* 此元素中四个要添加transition的css属性,指定它们的初始状态 */
    width: 100px;
    height: 100px;
    background: #ccc;
    transform:rotate(0);

    /* 指定transition属性,分别是要过渡的属性,过渡时间,过渡速度曲线,延迟时间 */
    transition: width 1s ease 0s,
                height 1s ease-in 0s,
                background 2s ease-out 1s,
                transform 2s ease-in-out 2s;

}

假如触发事件是鼠标悬停,那么下面是这个元素在鼠标悬停后触发的状态,中间状态交给浏览器自动计算。

div:hover {
    width: 200px;
    height: 200px;
    background: black;
    transform:rotate(90deg);
}

下面是演示结果:

transition 的使用大概就是这样,其余没写的 2D 和 3D 的转换与例子中的 rotate 类似,关于 transition 更详细的内容不再赘述。


前面已经说过 transition 动画必须要有事件触发且只能有首末两个状态,另外我们还要知道,transition 动画是一次性的,除非不停地有事件触发它才能循环往复。对于简单的过渡需求来说这几个特点是优点,但要想替代传统网页里的动画图片、Flash 动画以及 JavaScript 动画,transition 就显得有些力有未逮。幸运的是 CSS3 里还有另一个专门为解决这些问题而生的新东西,也就是Animation。

依然先给出animation各项属性

属性 描述
animation 所有动画属性的简写属性,除了animation-play-state。
animation-name 规定 @keyframes 动画的名称。
animation-duration 规定动画完成一个周期所花费的秒或毫秒。默认是 0。
animation-timing-function 规定动画的速度曲线。默认是 “ease”。
animation-delay 规定动画何时开始。默认是 0。
animation-iteration-count 规定动画被播放的次数。默认是 1。
animation-direction 规定动画是否在下一周期逆向地播放。默认是 “normal”。
animation-play-state 规定动画是否正在运行或暂停。默认是 “running”。
animation-fill-mode 规定对象动画时间之外的状态。

在使用 animation 之前我们需要先学习 @keyframes 规则,这么说吧,不定义 animation,keyframes 写了毫无意义,keyframes 不写,animation 也动不起来。

熟悉动画制作视频剪辑之类的同学估计会对 @keyframes 规则比较亲切,因为我感觉 @keyframes 规则和 Pr 里的关键帧动画挺像的,不过有一点区别是在 @keyframes 里时间是以动画进行百分比的形式表示的。

举个例子,首先定义@keyframes规则如下

@keyframes anim {
    0%   { background: #cccccc; transform:translate3d(0); }
    25%  { background: #999999; transform:translate3d(100px,0,0);}
    50%  { background: #555555; transform:translate3d(100px,100px,0);}
    75%  { background: #999999; transform:translate3d(0,100px,0);}
    100% { background: #cccccc; transform:translate3d(0);}
}

然后把anim这个动画绑定到某个元素上就行了,比如:

.test {
    width:100px;
    height:100px;
    background:#eee;
    position:relative;
    /*以下animation属性分别为@keyframes动画名称,动画周期,
    速度曲线,延迟时间,播放次数,是否循环逆向播放,动画状态*/
    animation: anim 5s linear 2s infinite alternate running;
}

演示结果如下:

本站在载入时有个简单的淡入动画,就是使用的 @keyframes 实现的,至于引用样式的引号动画和各个链接的悬停则是使用 transition 实现,css 动画简单的使用方法就是这样,更复杂的使用方法我先不提,至于不提的原因在这里我要强调一下,无论是产品设计亦或是编码实现,我们都要尽量用最简单的实现去完成最复杂的效果,也就是说我们要记住:大道至简。什么是大道至简?大道至简的意思就是更复杂的我真的不会…


CSS媒体查询

使用 @media 查询,可以针对不同的媒体特征(media feature)或媒体类型(media type)来定义不同的样式。不过在 CSS3 之前只能查询多媒体类型(media type)但不能查询媒体特征(media feature),CSS3 中新引入了十分有用的媒体特征(media feature)。其中可以查询的媒体特征(media feature)非常多,不过我主要是利用最大宽度(max-width)和最小宽度(min-width)这个媒体特征去设计响应式的页面,其他的了解的不多。

@media 的语法包含了零个或多个表达式,如果所使用的设备匹配表达式中指定的媒体特征(media feature)或媒体类型(media type),该条 @media 的结果为true,然后文档会在匹配的设备上显示指定样式效果。首先列出所有可供查询的媒体特征(media feature)或媒体类型(media type)。

一、媒体类型(media type)

media type 描述
all 用于所有多媒体类型设备
print 用于打印机
screen 用于电脑屏幕,平板,智能手机等
speech 用于屏幕阅读器

二、媒体特征(media feature)

media feature 描述
aspect-ratio 定义输出设备中的页面可见区域宽度与高度的比率
color 定义输出设备每一组彩色原件的个数。如果不是彩色设备,则值等于0
color-index 定义在输出设备的彩色查询表中的条目数。如果没有使用彩色查询表,则值等于0
device-aspect-ratio 定义输出设备的屏幕可见宽度与高度的比率
device-height 定义输出设备的屏幕可见高度
device-width 定义输出设备的屏幕可见宽度
grid 用来查询输出设备是否使用栅格或点阵
height 定义输出设备中的页面可见区域高度
max-aspect-ratio 定义输出设备的屏幕可见宽度与高度的最大比率
max-color 定义输出设备每一组彩色原件的最大个数
max-color-index 定义在输出设备的彩色查询表中的最大条目数
max-device-aspect-ratio 定义输出设备的屏幕可见宽度与高度的最大比率
max-device-height 定义输出设备的屏幕可见的最大高度
max-device-width 定义输出设备的屏幕最大可见宽度
max-height 定义输出设备中的页面最大可见区域高度
max-monochrome 定义在一个单色框架缓冲区中每像素包含的最大单色原件个数
max-resolution 定义设备的最大分辨率
max-width 定义输出设备中的页面最大可见区域宽度
min-aspect-ratio 定义输出设备中的页面可见区域宽度与高度的最小比率
min-color 定义输出设备每一组彩色原件的最小个数
min-color-index 定义在输出设备的彩色查询表中的最小条目数
min-device-aspect-ratio 定义输出设备的屏幕可见宽度与高度的最小比率
min-device-width 定义输出设备的屏幕最小可见宽度
min-device-height 定义输出设备的屏幕的最小可见高度
min-height 定义输出设备中的页面最小可见区域高度
min-monochrome 定义在一个单色框架缓冲区中每像素包含的最小单色原件个数
min-resolution 定义设备的最小分辨率
min-width 定义输出设备中的页面最小可见区域宽度
monochrome 定义在一个单色框架缓冲区中每像素包含的单色原件个数。如果不是单色设备,则值等于0
orientation 定义输出设备中的页面可见区域高度是否大于或等于宽度
resolution 定义设备的分辨率。如:96dpi, 300dpi, 118dpcm
scan 定义电视类设备的扫描工序
width 定义输出设备中的页面可见区域宽度

三、逻辑运算符

描述
not 对整个媒体查询取反,true 变 false,false 变 true
and 用于合并多个媒体特征或合并媒体类型与媒体特征,所有表达式都为真时结果才为真
only 老式浏览器只支持查询 media type 故遇到 only 直接忽略掉此样式,现代浏览器有没有 only 没区别

总结下来@media语法就是(注意各个逻辑运算符可用的地方):

@media not|only mediatype and (media feature and|or|not mediafeature) 
{
    CSS-Code;
}

例如,如果你想在符合最小宽度为700像素的所有设备上应用一组样式可以这么写:

@media (min-width: 700px) { ... }

这个例子跟我在额外css里用的基本就很像了,本站的媒体查询省略 css 样式后大概就是下面这样:

@media (min-width: 1200px) {
    css code1
}
@media (max-width: 1200px) {
    css code2
}
@media (max-width: 930px) {
    css code3
}
@media (max-width: 700px) {
}
@media (max-width: 600px) {
}
@media (max-width: 500px) {
}
@media (max-width: 400px) {
}
@media (max-width: 350px) {
}

上面的代码意思就是当显示区域的宽度大于等于 1200px 时,应用样式 css code1,小于等于 1200px 时应用样式 css code2,小于等于 930px 时应用样式 css code3 … 以此类推。

需要注意的是 CSS 样式跟 DOM 结构和 JS 一样都是从前面往后依次加载,那么假如 css code 构成覆盖关系的话(就是定义的属性一样属性值不一样),那么会发生什么?比如下面这样的:

@media (min-width: 930px) {
   .wrapper { width: 100%; }    /* css code1 */
}
@media (min-width: 930px) and (max-width: 1200px){
  .wrapper { width: 80%; }      /* css code2 */
}
@media (max-width: 1200px) {
  .wrapper { width: 60%; }      /* css code3 */
}

答案是:

  • 当 width => 1200px 时,应用 css code1。
  • 当 930px <= width <= 1200px 时,css code2 被 css code3 覆盖,也就是实际上应用 css code3 。
  • 当 width <= 930px 时,应用 css code3。

上面这道题绕明白了,什么都懂了。总结一下就是,前面定义的 @media 中的 css code1 假如同样符合后面的 @media 条件且在后面的 @media 中没有被覆盖,那么后面的 @media 中依然应用 css code1 定义的样式,再往后的 @media 中依然如此直到 css code1 被覆盖为止。

至此 @media 的内容基本就完了,但是我必须得给你们分享一个 Stack Overflow 里看到的奇巧淫技!我本来写了几条打算只用于被誉为新时代 IE6 的傻逼 Safari 浏览器的 css 规则,本来还得对应着写几句 JS 去判断浏览器 UA 是否为 iOS 之类的代码,但本着能不用 JS 就不用 JS 的态度(因为不咋会)我就开始 google,接着我看到了这条脑洞无敌的回答Is there a @media query to target only the iOS running devices ? 。回答如下:

Yes, you can.

@supports (-webkit-overflow-scrolling: touch) {
  /* CSS specific to iOS devices */ 
}
@supports not (-webkit-overflow-scrolling: touch) {
  /* CSS for other than iOS devices */ 
}

YMMV…It works because only Safari Mobile implements -webkit-overflow-scrolling: https://developer.mozilla.org/en-US/docs/Web/CSS/-webkit-overflow-scrolling
Please note that @supports does not work in IE. IE will skip both of the above @support blocks above. To find out more see https://hacks.mozilla.org/2016/08/using-feature-queries-in-css/. It is recommended to not use @supports not because of this.

我也知道这有点耍小聪明,but it does works…


JS上的一点改动

前几天折腾网站的时候,研究了一下怎么实现在移动端随着网页滑动顶部菜单栏自动隐藏与显示的问题。大概思路是先给窗口绑定一个滚动监听事件,当滚动触发时执行方法 autoHideHeader(),在 autoHideHeader() 中先获取当前 scrollTop 值(当前滚动条位置,也就是隐藏在显示窗口之上的页面长度)并赋给 currentTop,然后与初始为 0 的 previousTop 比较来判断向上还是向下滚动。

CSS 代码如下:

.sidebar {
    width: 100%;
    padding: 30px 0;
    position: fixed;
    top: 0;
    z-index: 99999;
    -webkit-transform: translate3d(0);
    -moz-transform: translate3d(0);
    -ms-transform: translate3d(0);
    -o-transform: translate3d(0);
    transform: translate3d(0);
    -webkit-transition: transform 0.5s ease-out;
    -moz-transition: transform 0.5s ease-out;
    -o-transition: transform 0.5s ease-out;
    transition: transform 0.5s ease-out;
}

.sidebar.is-hidden {
    -webkit-transform: translate3d(0,-100%,0);
    -moz-transform: translate3d(0,-100%,0);
    -ms-transform: translate3d(0,-100%,0);
    -o-transform: translate3d(0,-100%,0);
    transform: translate3d(0,-100%,0);
}

JS 代码如下:

var mainHeader = $('.sidebar'),
    headerHeight = mainHeader.height();

//set scrolling variables
var scrolling = false,
    previousTop = 0,
    currentTop = 0,
    scrollDelta = 2,
    scrollOffset = 150;

$(window).on('scroll', function(){
    if( !scrolling ) {
        scrolling = true;
        (!window.requestAnimationFrame)
            ? setTimeout(autoHideHeader, 250)
            : requestAnimationFrame(autoHideHeader);
    }
});

$(window).on('resize', function(){
    headerHeight = mainHeader.height();
});

function autoHideHeader() {
    var currentTop = $(window).scrollTop();
    //scrolling up...
    if (previousTop - currentTop > scrollDelta) {
        mainHeader.removeClass('is-hidden');
    }
    //scrolling down...
    else if( currentTop - previousTop > scrollDelta && currentTop > scrollOffset) {
        mainHeader.addClass('is-hidden');
    }
    previousTop = currentTop;
    scrolling = false;
}

同时设置了在菜单下拉后,点击页面任何部位都可以收起下拉菜单,思路很简单直接看代码吧:

$(document).ready(function () {
    $(document).click(function (event) {
        var clickover = $(event.target);
        var _opened = $(".nav-toggle").hasClass("nav-toggle hidden active");
        if (_opened === true && !clickover.hasClass("navbar-toggle")) {
            $(".nav-toggle").click();
        }
    });
});

踩得各种坑

第一个坑我没办法,从我在 ios 11 上测试的结果看,貌似 Safari 内核的浏览器里 css hover 效果的显示逻辑跟其他移动浏览器不太一样,不是点击触发而是长按触发。由于我之前出于美观考量去除了某些链接在点击时默认的浏览器蓝色遮罩。所以在 iOS 端点击这几个连接时不会有任何反馈,会让人很没安全感,所以我利用上文说过的那个脑洞无敌的媒体查询只针对非 iOS 应用了去除蓝色遮罩规则,至于 iOS 端的点击动画就凑合着用浏览器默认的遮罩效果吧,我不管了烦人。至于你说 iOS 的其他浏览器岂不是被我误伤了?哈,不用担心,我也是刚知道所有 iOS 端第三方浏览器都必须强制使用 Safari 内核,也就是说 iOS 设备上的所谓第三方浏览器就是换了个图标的 Safari 罢了。

第二个坑是在点击页面任何部位都可以收起下拉菜单的那个逻辑里遇到的,仍然与 Safari 有关,一些情况下对非可点击元素监听click事件,iOS 下不会触发,也就是说上面那个逻辑在 iOS 端变成了点击页面上任何按钮才可以自动收起菜单了。解决办法加两段代码如下,使 iOS 端的网页 body 在菜单显示时成为可点击状态并隐藏点击时的默认蓝色遮罩。

CSS:

@supports (-webkit-overflow-scrolling: touch) {
    body.enable-click {
        cursor: pointer;
        -webkit-tap-highlight-color:rgba(255,0,0,0);
    }
}

JS:

$( 'body' ).toggleClass("enable-click");

第三个坑,因为最开始下拉菜单我是设置成全屏样式的,所以打算设置菜单下拉后禁止页面滚动的以防误操作。防止滚动的第二个考量是因为滚动时顶部导航栏还会隐藏,但菜单 position 是 fixed,所以相对于浏览器窗口位置固定,导航栏一隐藏菜单上面就会出现一大块空白很不好看。解决办法是自然就是防止滚动,在菜单下拉时使body.lock-scroll { position: fixed; },或者设置body.lock-scroll { overflow; hidden; },收起菜单恢复就行即可。结果这又引入了新的问题,假设用第一个代码,那么页面设为 fixed 和恢复的一瞬间页面都会丢失滚动位置。那么按理说可以添加如下js代码解决:

 (function(){
   var scrollTop = 0;
   menu.onclick = function(){
      scrollTop = getScrollTop();                   // 在菜单显示之前,记录当前的滚动位置
      document.body.classList.add('lock-scroll');   // 使body脱离文档流
      document.body.style.top = -scrollTop + 'px';  // 把脱离文档流的body拉上去!否则页面会回到顶部!
   }
   close.onclick = function(){
     document.body.classList.remove('lock-scroll'); // body又回到了文档流中
     to(scrollTop);                                 // 滚回到老地方
   }

   function to(scrollTop){
     document.body.scrollTop = document.documentElement.scrollTop = scrollTop;
   }
   function getScrollTop(){
     return document.body.scrollTop || document.documentElement.scrollTop;
   }
 }());

不幸的是,别人用这个经典的思路都能解决,我的就是死活不行。最多只能做到在拉出菜单时,body能被拉上来(不丢失滚动位置)且被设为fixed,但收起菜单时,fixed无法解除且丢失滚动位置。我 JS 水平有限,折腾好长时间实在找不出哪错了,真没办法了。

至于body.lock-scroll { overflow; hidden; }到不会丢失滚动位置,但是桌面端和安卓端都能工作,iOS 不行,PASS。

最后一想算了放弃吧,菜单呼出时页面能滚动也没啥。不过既然放弃了 Plan A,我就得面对 Plan B 的问题,为了防止用户在菜单下拉时无意识的滚动,让菜单不要全屏显示露出下面页面就行了,这个简单。但滚动时顶部导航栏会隐藏导致菜单上面出现一大块空白的问题还没解决。我动脑子一想,菜单的本来的 position 是 fixed,也就是相对于浏览器窗口固定,我只要想办法让它和导航栏固定在一起,也就是相对于导航栏固定就行了。css里的position属性一共可指定五个值,分别是是absolute、fixed、relative、static 和 inherit,其中 absolute 表示相对于 static 定位以外的第一个父元素进行定位。所以我在在 html 结构里把菜单放在了导航栏的子项,然后 position 设为 absolute,所有问题就都迎刃而解了。也就是说在菜单下拉时滑动时,当导航栏隐藏,菜单会跟着导航栏上移一段距离。

最后,这些折腾过程中,w3school.comCSS 菜鸟教程之类的网站给了我很大帮助,感谢感谢~