360 AI音箱官网开发手记

来自:奇舞周刊,作者:李松峰 ,资深技术图书译者,翻译出版过40余部技术及交互设计专著,现任360奇舞团高级前端开发工程师,360前端技术委员会委员、W3C AC代表

360 AI音箱官网开发手记

标题说的是官网,但实际上本文主要介绍的是展示大图的页面,也就是概述页。为简便起见,本文以下用“官网”代称“360 AI音箱官网概述页”。

这次官网的设计稿是提前4天才开始出的,最后一个页面是上线前一天下午2点给的。总之,时间非常紧,没有充裕的时间去调研多种实现方案。开发中采用了比较保守、兼容性好的技术。

基本数据

说到大图,就得知道主流显示器的分辨率。京东在销显示器的分辨率如下:

  • 3840×2160

  • 3840×1080

  • 3440×1440

  • 2560×1440

  • 2560×1080

  • 1920×1200

  • 1920×1080

维基百科2019年1月调查结果如下(https://p4.ssl.qhimg.com/t01fff3464ffc757200.png):

标准比例宽度(px)高度(px)用户(%)
nHD16:96403604.26
VGA4:38006000.42
XGA4:310247683.29
WXGA16:912807203.15
WXGA16:1012808003.88
SXGA5:4128010243.37
HD~16:913607681.65
HD~16:9136676824.27
WXGA+16:1014409006.55
非标16:915368645.5
HD+16:916009004.81
WSXGA+16:10168010502.52
FHD16:91920108019.20
WUXGA16:10192012001.20
QWXGA16:9204811520.46
非标21:925601080n/a
QHD16:9256014402.09
21:934401440n/a
4K UHD16:938402160n/a

也就是说,2560像素及以下宽度屏幕的用户占86%以上。为此,官网所有大图最大宽度为2560像素。

图片数25
大图数15
页面体积6.8MB

接下来,本文主要从以下几方面概述官网开发涉及的技术点:

  • 呈现大图

  • 弹性文字

  • HTML结构

  • CSS布局

  • 自定义字体

  • 固定页脚

呈现大图:imgbackground-image

为简单起见,我们将整个页面的开发分成几个部分。首先从大图的呈现说起。

img标签可以直接把包裹它的元素撑起来,并且能在浏览器窗口缩放时保持宽高比同步缩放。

background-image因为是背景图片,所以需要明确设置包裹元素的宽度和高度,才能在浏览器窗口缩放时不至于少显示图片。而且,即使使用了background-size: cover,也需要额外通过媒体查询在某个断点重新设置包裹元素的宽度和高度,以保持元素与图片具有相同的宽高比。

出于快速开发的目的,我们选择了使用img标签:

<figure class="full-img">


<img src="https://p1.ssl.qhimg.com/t01f061f44107fd8ed1.jpg">


</figure>

如下图所示:

弹性文字:remem

大图页需要适应屏幕缩放同步缩放文字大小。因此,需要在使用remem单位间做出选择。

rem是根元素htmlfont-size值,而em则是body元素的font-size值。使用这两个单位都可以实现对应文字大小的全局性缩放。区别在于,rem是全局性唯一参照大小,直接修改htmlfont-size就可以全局缩放文字大小;而em单位基于从父元素继承的大小缩放,只能通过缩放父元素的font-size来修改页面局部的继承大小。

为此我们决定使用简单直接的rem。 首先,根据设计稿确定某个屏幕宽度下对应的htmlfont-size值。比如,大约1200像素时,对应的rem为100像素(之所以设置为100像素,是为了换算方便):

html {
  font-size: 100px;
  body {
    font-size: .16rem;
    min-width: 1226px;
    background-color: #ffffff;
    font-family: sans-serif;
  }
}

然后再设定一个缩放系数,比如12,通过JavaScript来根据浏览器窗口大小动态修改rem大小。我们定义了一个模块ajust_rem.js,导出adjustRemOnResize类:

import throttle from 'lodash.throttle'


export default class adjustRemOnResize {

  constructor ({divisor, remBaseSize}) {

    this.divisor = divisor

    this.remBaseSize = remBaseSize

    this.html = $('html')

    this.resize()

    $(window).on('resize', throttle(this.resize.bind(this), 300))

  }

  resize () {

    const currentWidth = $(window).width()

    const remDyncSize = currentWidth / this.divisor

    const fontSize = remDyncSize < this.remBaseSize ? this.remBaseSize : remDyncSize

    this.html.css({fontSize})

  }

}

在相应页面引入这个类,传入参数,创建实例:

import adjustRemOnResize from './ajust_rem.js'


const adjustRemOnResizeOptions = {

  divisor: 12,

  remBaseSize: 100

}

new adjustRemOnResize(adjustRemOnResizeOptions)

remBaseSize其实就是最小rem值。从前面的代码可知,当浏览器窗口宽度为1200像素时,除以12就是100像素。相应地,当浏览器窗口宽度为2400像素时,除以12,rem就是200像素。

而所有尺寸(文字大小、内外边距、定位偏移等)需要根据窗口大小动态改变的值,都使用类似如下的方式来声明:

small {
  font-size: .12rem;
}
.double-engine {
  h2 {
    margin-bottom: 0.2rem;
  }
  figure {
    margin-top: 0.6rem;
  }

比如,small标签中的文本大小在rem为100像素时为12px:

而在rem为200像素时则会变成24px:

HTML结构

分别确定了处理大图和文字的方式,接下来更进一步,处理二者结合在一起的情况。换句话说,就是要把文字定位到大图上面,此时的HTML结构如下:

<section>
  <article>
    <h3>囊括三大音乐平台资源,千万音乐随心畅听</h3>
    <p>目前已经接入腾讯音乐集团旗下酷狗音乐,QQ音乐、酷我音乐接入中,囊括市场排名前三音乐平台,千万音乐随心畅听。</p>
    <small>* 部分音乐服务需要登录对应账号后才能享用免费服务,免费服务有效期1年,有效期结束后服务方式以官方公告为准</small>
  </article>
  <figure>
    <img src="https://p4.ssl.qhimg.com/t010b8bdd20c5668ae4.png">
  </figure>
</section>

其中,每个section代表音箱的一个卖点,卖点要通过大图和文字来展现:

  • article:文字区

  • h3:标题

  • p:内容

  • small:说明

  • figure:大图区

  • img:大图

CSS布局

基于上一节的HTML结构,CSS技术使用了最为保守、兼容性最好的定位和行内块居中。

首先,来看一下定位技术的运用:

section {
  position: relative;
  article {
    position: absolute;
    p {
      text-align: justify;
    }
  }
  figure {
    width: 100%;
    margin-top: 0.2rem;
    img {
      width: 100%;
    }
  }
}

如前所述,包含卖点的section默认自然是一个块级元素,它的直接子元素只有两个:articlefigure。这里的CSS把section设置为相对定位,为其子元素创建一个定位上下文。而通过把article设置为绝对定位,就可以实现将其移出正常布局流同时又相对于其定位上下文section定位的目的。

figure呢?自然还包含在section内部,并且其包含的img通过引用大图,并将宽度设置为100%,最终会将整个figure乃至section撑至满屏。

这样,对于包含卖点的article通过设置百分比单位的left属性和width值,就可以将文字相对于大图任意定位,比如“儿童模式”的HTML:

<li class="child-mode">
  <section>
   <article class="intro-left intro7">
    <h3>专属儿童模式<br>精选内容助力成长</h3>
    <p>内容运营团队针对不同年龄段儿童,精选定制化的音频内容与百科知识库,提供科学的儿童内容。
    <br>云端建立儿童内容黑名单,屏蔽不适合小朋友收听的音乐与音频内容,避免受到不良内容的伤害。</p>

    <figure><img src="https://p5.ssl.qhimg.com/t017963a80a701e29ba.png"></figure>
   </article>
  <figure class="full-img">
    <img src="https://p5.ssl.qhimg.com/t0172cdc426c2f437c3.jpg">
  </figure>
 </section>
</li>

CSS如下:

.intro-left {
  left19%;
}
.child-mode {
  .intro7 {
    top: 1.2rem;
    width: 26%;
    h2 {
      margin: 0.1rem 0;
    }
    figure {
      width: 100%;

      margin-top: 0.2rem;

      img {
        width: 100%;
      }
    }
  }
}


除了使用绝对定位将文字定位到任意位置,还有一个通用需求是把文字定位在页面/大图水平中间。

这时候,我们使用了传统的display: inline-block;技术,即把包含文字的块级元素变成行内块,而在其父级通过text-align将其居中:

<article class="middle-heading-position-top intro6">
  <div>
    <h2>双重智能引擎<br>大人孩子都喜欢</h2>
    <p>双重智能唤醒,内置两套独立唤醒词与应答语音,可以温柔贴心,也可以稚气天真。<br>儿童专属童声语音,节奏更轻缓,语调活泼,更符合小朋友的沟通习惯。</p>
  </div>
</article>

CSS:

.middle-heading-position-top {
    text-align: center;
    width: 100%;
    div {
      display: inline-block;
    }
}


注意,无论是使用定位,还是居中行内块,都要恰当配合百分比单位的使用。

自定义字体

官网需要使用多种不同字重的思源黑体。这正是奇字库派上用场的地方,只要在模板包含文件里加上如下script代码:

奇字库是360内部自建的针对版权和开源字体一个字体托管和动态截取服务,使用基于Web Font Loader(由Goolge和Adobe联合开发)定制的Web字体加载脚本,可以一键式向网页中引入自定义Web字体。

<script>
(function(d{
  var config = {
    fontFamily: ['siyuan-regular''siyuan-normal'],
    fontHash: ['/*siyuan-regular-hash*/''/*siyuan-normal-hash*/'],
    urlType'data',
    scriptTimeout3000,
    asynctrue,
  },
  // ……省略加载脚本……
})(document);
</script>

就可以在CSS里像使用本地字体一样使用自定义字体了:

.wf-siyuanregular-n4-active .feature-list h2 {
  font-family: siyuan-regular, sans-serif;
}
.wf-siyuannormal-n4-active .feature-list p,
.wf-siyuannormal-n4-active .feature-list small {
  font-family: siyuan-normal, sans-serif;
}


固定页脚

“让该死的页脚始终呆在页面底部”是所有页面的共同需求。推荐一下纯CSS解决方案:

* {
 box-sizing: border-box;
}
*:before,
*:after {
 box-sizing: border-box;
}
html,
body {
 height100%;
 position: relative;
}
.main-container {
 min-height100vh/* 与视口同高 */
 overflow: hidden;
 display: block;
 position: relative;
 padding-bottom100px/* 控制页脚高度 */
}
footer {
 position: absolute;
 bottom0;
 width100%;
}

这个方案的优点:

  • 纯CSS,无JavaScript

  • 适用于所有支持视口单位(vh)的浏览器

唯一的缺点:

  • 需要通过容器元素的padding-bottom控制页脚的实际高度

感谢作者 @zero(https://medium.com/@zerox)。

小结

由于时间紧,官网没有采用Flexbox等比较先进的技术,而是选择了保守但兼容性好的定位和行内块居中技术,当然别忘了在父级和子级配合使用百分比宽度。另外,由于有了奇字库(360内部字体托管和截取服务),让使用自定义Web字体也变得非常简单。本文最后还顺便推荐了一个解决页脚始终显示在底部的纯CSS方案。

推荐↓↓↓
前端开发
上一篇:我在阿里云做前端 下一篇:Web与传感器:Generic Sensor API