起点中文网 3D 书封之最佳实践

阅读 688
收藏 0
2018-05-29
原文链接: zhuanlan.zhihu.com
本文作者:杨晔
原创声明: 本文为阅文前端团队 YFE 成员出品,请尊重原创,转载请联系微信公众号 ( id: yuewen_YFE ) 获取授权,并注明作者、出处和链接。

前言

官网,一个作为网站第一入口的页面,直接影响着用户进入站点的第一印象。但纵览国内的所有文学类、小说类网站,大多都以推内容为主。在书本的视觉表现上都千篇一律 —— 一张简单的书封面,或是带有外边框的书封面,又或者是带有投影的书封面。其实到目前为止至少我见过的此类网站,都只是一张单调的运营平面图片。今天所要介绍的就是在2016年起点中文网改版中对整体视觉进行优化的一个环节 —— 可被运营的《批量3D书封面》。《起点中文网》作为文学类网站的领导者,此次改版我们希望能以“创新的面貌"给用户带来眼前一亮,在不影响业务的前提下,我们做出了在书本上优化视觉的尝试,赶紧来看一下是如何实现的吧!体验链接: www.qidian.com

索引

  1. HTML 结构的思路 - 1分钟阅读
  2. 透视与 3D空间 - 2分钟阅读
  3. 书封旋转的具体实现 - 4分钟阅读
  4. 兼容性 - 6分钟阅读

普通书封 与 3D 书封

一、HTML 结构的思路

也许你曾经在搜索引擎中找到过类似的 3D 书本效果,并且他可能还带有很炫的翻书特效:

但以上这个例子,开发者用了 2 组 ul 和多个 li(至少5个以上),并且带有各种 beforeafter 伪元素来实现层叠的纸张和翻书效果。这可能适合某个运营专题类网页的创意特效,让人觉得挺炫酷。但这显然是不适合放在官网作为内容维护,我们并不希望它是由那么多 HTML 标签来实现,一方面不好维护,另一方面我们还需要考虑对老浏览器进行优雅降级。

以下是起点中文网 3D 书封的结构:

<!-- 书封容器 -->
<div class="book-cover">
    <!-- 链接 -->
    <a class="link" href="//book.qidian.com/info/1004608738" target="_blank">
        <img src="//qidian.qpic.cn/qdbimg/349573/1004608738/90" alt="圣墟">
    </a>
    <!-- 阴影 -->
    <span></span>
    <!-- 灰色部分纸张厚度填充 -->
    ::after
</div>

二、透视与 3D 空间

在我们说 CSS 思路之前,先让我们来熟悉一下这个单词:perspective。字典中的解释:远景、透视图,透视的等含义。

也就是说,无论你如何的用 CSS3 的 transform,如果不给父级元素加透视属性,它始终是一张方方正正的平面 2 D图。而一旦某个容器被加上了这个 perspective 透视属性,你就会打开一个 3D空间概念,如下图:

3D 书封的实现是利用了 CSS3 中的旋转(rotate),所以先让我们来直观地看一下 CSS3 种 3D 旋转的动图:

你会发现,3D 旋转和 2D 旋转不同之处就是,它有 3 个轴 的方向,即上图中的 xyz 轴,并且每一轴都有对应的正负极。

而以上的 HTML 结构中的书封容器、阴影、和 after 纸张伪元素,他们都加上了 transform: perspective

这里值得提一下 perspective 的特点:

如果一个最上层父容器加上 transform: perspective,子元素会全部按照父级元素的视角来透视,虽然旋转值都是 45deg,但是每个旋转的角度在父元素的透视下都不一样,如下图第一排,但如果是各自加上 transform: perspective,表示自己独立成一个透视视角,如下图:

因此,我们在实现 3D 书封时更适合用第二种,给各元素独立加上透视属性。

三、书封旋转的具体实现

我们需要先来拆解一下最终的实现效果:

图中 3 个元素:
1. 往 Y 轴负极旋转的书封
2. 往 Y 轴正极旋转的纸张厚度
3. 旋转变形过的 box-shadow 阴影

要实现 3D 书封并且要兼顾良好的兼容性,我们必须区分两类浏览器阵营:
1. chrome、火狐、IE10-IE11、或其他 webkit 内核浏览器
2. IE7-IE9

对于第一类浏览器,我们使用 :root{ } 方法来让其渲染,第二类浏览器则不会执行透视和 CSS3 的代码,保持原来的平面风格。(代码+注释解说)

/*
 *SASS代码:demo 只列出关键属性,其余个性样式以及 CSS3 前缀不做一一列出
 */
//两类浏览器通用代码

.book-cover {
    //其他元素需要用 absolute 来调整位置,所以这里必须用 relative。
    position: relative;    
    //3d 运营图宽度需要 52px(具体原因看下方)
    width: 52px;
    height: 91px;    
    //a标签链接
    .link {
        position: relative;        
        //最上层链接
        z-index: 3;
    }    
    //运营图
    img {
        width: 60px;
        height: 87px;
    }
}
//第一类浏览器代码(书封旋转、纸张可见、阴影可见)
:root {
    .book-cover {
        //加透视 60px(图片原始宽度),Y轴负值旋转 10 度
        transform: perspective(60px) rotateY(-10deg);
        /* 白色书页填充 */
        &:after {
            position: absolute;            
            //第二层元素
            z-index: 2;
            top: 2%;
            left: 100%;            
            //白色书页的厚度
            width: 10%;            
            //白色书页的高度(注:不能超过 img 的高度,否则纸张会比封面长,与现实世界的透视视角不符)
            height: 92%;
            content: ' ';            
            //Y轴正值旋转 30 度(注:始终和你的书封反向轴旋转,否则会出现纸张还原度不真实)
            transform: perspective(60px) rotateY(30deg);            
            //你需要加上一点背景和内阴影,让纸张显得有点质感,而不是纯纯的白色
            background-color: #EFEFEF;
            box-shadow: inset 0 0 5px #333;
        }        
        /* 投影 */
        span {
            position: absolute;            
            //底层元素
            z-index: 1;            
            //位置信息,可根据当时实际情况微调
            top: 84%;
            left: 7px;            
            //尽量设置偏小,让旋转变形出箭头三角状
            width: 20px;
            height: 10px;
            content: '';            
            //反复调试的结果,按照接近设计稿为准
            box-shadow: 25px 0 5px 5px #ADADAD;            
            //透视和旋转角度需反复调试,按照接近设计稿为准,也可尝试用 transform:skew 来调整
            transform: perspective(74px) rotateX(-70deg) rotateY(-5deg);
        }
        img {            
            //(原始运营图宽度是 60,但在 3d 旋转时需要改小,这是因为书封旋转后太宽会遮住纸张)
            width: 52px;
        }
    }
}

四、兼容性

介于国内浏览器分布的情况,一旦你做出比较创新的布局就很容易带来一系列的兼容性问题,当然 3D 书封也不例外,而某些浏览器用户数量还是比较多的,我们并无法完全抛弃。在项目迭代的过程中,我们发现了一些怪异问题,总结如下:

  • 火狐运营图出现锯齿
  • 使用 :root{} 后,IE9 也执行了代码,但是 IE9 并不能做到 3D 旋转,导致阴影外露,图片宽度也变成了 52px
  • 我们这次改版是兼容所有 Mac 设备的,在 Safari 下出现了一个奇特的穿透问题。我们从简到繁,先从第一条开始分析。

1、火狐浏览器中出现锯齿

先来看一下火狐下的情况

一开始觉得有点费解,火狐虽然不是 Webkit 内核,但基本在渲染上也是接近 Chrome 的浏览器,为什么会出现那么难看的锯齿?经过一番研究,发现火狐只要是进行过透视渲染的元素,都会出现锯齿,比如这个翻开效果也会:

思考: 问题出在 img 标签上,能否在 CSS 上加一些样式修复掉这个问题?我们尝试了给 img 加上了边框,border: 1px solid #FFF ;(经实践,必须是淡色,深色依然有锯齿)。效果如下:

似乎锯齿问题解决了,但是 border 会增加宽高各两个像素,并且你无法保证始终让 border 的颜色和运营图的边缘一样,不管怎样都会露出边框线。这时候我们想到了另外一个更适合的属性:outline。(IE8+支持)

outline 是轮廓属性,不会影响宽高,但是如果你依然用淡色边框色,在某些背景不是白色的容器上,依然会出现锯齿,所以我们这里如果用 transparent 边框透明色就能完美地解决问题。

//运营图
img {
    width: 60px;
    height: 87px;    
    //处理火狐锯齿
    outline: 1px solid transparent;
}

2、IE9 不支持旋转,导致阴影外露和宽度不正确的问题

刚刚有提到,我们使用了 :root{} 方法区分了两类浏览器阵营,但是 IE9 是一个中间地带,他支持 :root{},但 transform 对他无效,同时他又支持 box-shadow,所以在 IE9 下 3D 书封是长这样的:

经过数据调研,我们的 IE9 用户并不多,但是作为一个开发者这样的效果显然是无法接受的。可是 IE9 并没有单独的 CSS hack。所以我们只能针对IE9的一些特定属性来对露出的一些元素进行优化:

  • 阴影:-ms- 是 IE 的 CSS3 前缀,同时 IE9 又支持 scale 缩放,所以我们用 -ms-transform: scale(0) 就可以将阴影设置成看不见,而 ie10 和 ie11 不受影响。
  • 书封宽度问题:width 就没有那么理想的方案了,hack 写法加 “\9\0” 都会影响其他 IE 浏览器,所以我们选择的方案是用 js 重新改变 CSS 宽度为 60。经测试,如果用 IE9 查看官网的话,由于官网数据比较多,在渲染出所有图片后,这个重新改变尺寸的 JS 行为用户其实是无感知的(IE9 本身渲染页面性能就比较差,在用户还没看到页面之前 JS 就已经将图片宽度改好了)

3、Safari 中奇特的层级穿透问题(无视 z-index)

先来看一下这个奇特的问题吧,书封穿透了一个 fixed 元素,而阴影又穿透了书封 >_<,这……看起来似乎很难搞定啊!

刚开始我在遇到这个「bug」的时候完全不能理解,Safari 虽然是独立引擎,但他至少也是 apple WebKit,为什么会出现这种怪异的现象?不管问题是如何产生的,这种还原度依然是无法接受的,上来我先按照处理 IE6 那些年的经验,给父级元素来个 overflow:hidden 看看,如下图:

果然问题可以解决!但是某些页面中的情况比较复杂,不能用 overflow:hidden 怎么办?比如上图,右下角投影也被裁切掉了,显然这个方法治标不治本。

我抛弃了 overflow:hidden 方法,尝试用 z-index 来控制层级,试图把 fixed 元素盖住它,但被 Safari 完全忽视,经过一番疯狂的胡乱无效调试,我决定要从根源了解问题,了解现象是如何产生的,最后得出以下几个结论:

  • Safari 的 3D 变形 transform 会忽略 z-index,哪怕你被穿透的元素提高到 9999 还是无效。
  • Safari 出现的这个现象并不能称之为 bug,其实是以真实世界来渲染页面元素的。

为了能让读者更直观的理解以上2 点,我画了两张示意图:

我们现在遇到的这个穿透问题是这样的:

而其实我们所希望的是这样的:

这时,让我们来看一个 Demo:fangyexu.com/tool-CSS3In…。 原来,我们在做 3D 旋转变换时,一直都在关注 X 和 Y 轴,在此 Demo 中有一个 translateZ 属性,是可以控制透视元素的远近(模拟真实视角远近)的属性。而其他浏览器如果你用上了 z-index,他会按照 z-index 层级来安排元素的层级,但 Safari,刚有提到它是按照真实世界来渲染的,所以 z-index 在这里完全无效,必须通过 translateZ 来控制。知道以上原理,兼容性的代码就很容易实现了。

首先,先解决阴影穿透书封:span(阴影)穿透了 a 和 img 标签(书封),那我们尝试 在a 标签上加上 transfor:translateZ(50px),书封右下角问题解决,如下图:

剩下的定位 fixed 元素想要盖住书封就更容易了,给 fixed 元素加上属性 transfor:translateZ(10px),还原度和 chrome 浏览器终于保持一致了。

五、写在最后

在前几年,想必每个攻城狮几乎都被 IE6 的各种怪异 BUG 给虐过,随着技术发展 IE6 终于退出了历史舞台,各大领先行业的网站几乎都开始不支持 IE7 和 IE8。原以为可以摆脱那些烦人的兼容性问题,但是按照目前的形势看来,浏览器的标准还是没有得到很好的统一,包括 CSS3 布局中的一些比较流行的 flex 和其他自适应布局在移动端上的兼容性依然不是很好,良好的兼容性是我们开发者永恒的话题,我们必须不断的学习和积累、经常地进行问题总结,才能更好的应对技术高速发展的现状。

如果你也遇到一些奇葩兼容性问题,欢迎进行留言贡献讨论,让我们共同进步,谢谢。

原文地址:

起点中文网 3D 书封之最佳实践