Web用户体验设计提升实践(上)
前言
本文是基于 Shopee 供应链团队内部 WMS(Warehouse Management System,仓库管理系统) 项目的整体重构,总结而出的一份 Web 用户体验设计提升指南。
因为是对已经存在的项目进行完全的推翻重构,所以在整个过程中,我们一直在思考如何尽可能地站在用户的角度,通过前端设计去提升改进用户的体验与感受,真正达到重构的目的及意义。
一个 Web 页面或是一个 App,想让别人用得爽,获得良好的用户体验,可能需要包括但不限于:
急速的打开速度 令人眼前一亮的 UI 设计 酷炫的动画效果 丰富的个性化设置 易用、友好的便捷操作 贴心的细节 关注残障人士,良好的可访问性 ……
所谓的用户体验设计,是秉承着以用户为中心的思想的一种设计手段,以用户需求为目标而进行的设计。设计过程注重以用户为中心,用户体验的概念从开发的最早期就开始进入整个流程,并贯穿始终。
良好的用户体验设计,是团队在产品开发中每一个环节共同努力的结果。
本文将主要从页面呈现、交互细节、可访问性三个方面入手,分享一些在实际开发过程中积攒的有益经验。通过这篇文章,你将能:
了解到一些细节是如何影响用户体验的; 了解到如何在尽量小的开发改动下,提升页面的用户体验; 了解到一些优秀的交互设计细节; 了解基本的无障碍功能及页面可访问性的含义; 了解基本的提升页面可访问性的方法。
文章内容较多,将分为上下两篇发出。本篇为上篇,主要讲述“页面呈现”的部分。
1. 页面呈现
就整个页面的展示和页面内容的呈现而言,不同的展示方式,所得到的效果截然不同。
这其中有非常多值得注意的细节。接下来分为几个要点进行阐述:
自适应的布局 重点内容的排布设计 兼容不同场景与异常回退 图片的呈现及异常处理 适当的过渡与动画
1.1 自适应的布局
先来看一些布局相关的问题。
布局,是前端在重构页面过程中需要提前进行规划思考的,一般应该考虑清楚以下几个问题:
对于 PC 端,项目是全屏布局还是定宽布局?用户是否还在使用 IE? 对于全屏布局,需要适配的最小宽度是多少? 对于移动端布局,你知道用户设备的分布吗?最少兼容到 Android 什么版本?iOS 什么版本? 内容应该以什么样的方式呈现?
到今天,各种设备浩如烟海,移动端屏幕尺寸纷繁复杂(下图仅仅是到 2019 年各种安卓设备屏幕尺寸图的分布):
不过,我们的重构项目整体是以 PC 为主的 ToB 项目,所以这里主要以 PC 端为例进行讲解。
对于大部分 PC 端的项目,首先需要考虑的肯定是最外面的一层包裹。假设就是 .g-app-wrapper
。
<div class="g-app-wrapper">
<!-- 内部内容 -->
</div>
首先,对于 .g-app-wrapper
,有几点是我们在项目开发前必须弄清楚的:
项目是全屏布局还是定宽布局? 对于全屏布局,需要适配的最小的宽度是多少?
对于定宽布局,就比较方便了,假设定宽为 1200px
,那么:
.g-app-wrapper {
width: 1200px;
margin: 0 auto;
}
利用 margin: 0 auto
实现布局的水平居中。在屏幕宽度大于 1200px
时,两侧留白;屏幕宽度小于 1200px
时,则出现滚动条,保证内部内容不乱。
现代布局更多的是全屏布局。其实现在也更提倡这种布局,即可随用户设备的尺寸和能力而变化的自适应布局。
自适应布局通常有左右两栏,左侧定宽,右侧自适应剩余宽度。另外还会有一个最小宽度。那么,它的布局应该是这样:
<div class="g-app-wrapper">
<div class="g-sidebar"></div>
<div class="g-main"></div>
</div>
.g-app-wrapper {
display: flex;
min-width: 1200px;
}
.g-sidebar {
flex-basis: 250px;
margin-right: 10px;
}
.g-main {
flex-grow: 1;
}
这里利用了 flex 布局下的 flex-grow: 1
,让 .main
进行伸缩,占满剩余空间,利用 min-width 保证了整个容器的最小宽度。
这是最基本的自适应布局。对于现代布局,我们应该尽可能考虑到更多的场景。做到:
底部 footer
下面一种情形也非常常见:
页面存在一个 footer(页脚)部分,如果整个页面的内容高度小于视窗的高度,则 footer 固定在视窗底部,如果整个页面的内容高度大于视窗的高度,则 footer 正常流排布(也就是需要滚动到底部才能看到 footer)。
看看效果:
这个需求如果能够使用 flex 的话,用 justify-content: space-between
可以很好地解决。同理,使用 margin-top: auto
也非常容易完成:
<div class="g-container">
<div class="g-real-box">
...
</div>
<div class="g-footer"></div>
</div>
.g-container {
height: 100vh;
display: flex;
flex-direction: column;
}
.g-footer {
margin-top: auto;
flex-shrink: 0;
height: 30px;
background: deeppink;
}
Codepen Demo - sticky footer by flex margin auto
当然,实现它的方法有很多,这里仅给出一种推荐的解法。
1.2 重点内容的排布设计
下面这一块关于重点内容的展示。
1.2.1 重要内容及功能的展示
让吸引用户注意力的元素前置。如果我们的页面存在需要让用户了解、处理的核心信息或者表单,尽可能将其位置放在上方,让用户更容易获取这部分信息。
将用户需要的信息、重要的功能展示出来而不是藏起来。
类似于导航、搜索等高频操作,一定不要让用户多次点击才能用到。
1.2.2 处理动态内容:文本超长
对于所有接收后端接口字段的文本展示类的界面,都需要考虑全面。正常情况如下,是没有问题的:
但是我们是否考虑到了文本会超长?超长了会折行还是换行?
对于单行文本,使用单行省略:
{
width: 200px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
当然,目前对于多行文本的超长省略,兼容性也已经非常好了:
{
width: 200px;
overflow : hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
}
1.2.3 处理动态内容:保护边界
对于一些动态内容,我们经常使用 min/max-width
或 min/max-height
对容器的高宽限度进行合理的控制。
在使用它们的时候,也有一些细节需要考虑到。譬如经常会使用 min-width
控制按钮的最小宽度:
.btn {
...
min-width: 120px;
}
当内容比较少的时候是没问题的,但是如果内容比较长,就容易出现问题。下图就是使用了 min-width
却没考虑到按钮的过长的情况:
这里就需要配合 padding 一起:
.btn {
...
min-width: 88px;
padding: 0 16px
}
借用 “Min and Max Width/Height in CSS” 中一张非常好的图,作为释义:
1.3 兼容不同场景与异常回退:空数据内容展示
这个模块是兼容不同场景与异常回退,是一个常常被忽略的地方。
页面经常会有列表搜索、列表展示。那么,既然存在数据的正常情况,当然也会存在搜索不到结果或者列表无内容可展示的情形。
对于这种情况,一定要注意空数据结果页面的设计;同时要知道,这也是引导用户的好地方。对于空数据结果页面,分清楚:
(1)数据为空
用户无权限——要告知用户无权限访问的原因和解决方案 搜索无结果——告知用户搜索无数据的结果,如有必要可推荐相关内容 筛选无结果——一般直接告知筛选无结果 页面无数据——文案设计有几个方向:
告诉用户这里将会存放什么数据
给用户一个主动创造数据的理由,比如通过话术引起用户心理共鸣
若页面无数据会给用户造成困惑,则可以说明原因打消用户的困惑
(2)异常状态
网络异常——指出当前状态为网络异常,并给出解决方案 服务器异常——指出当前状态为服务器异常,并给出解决方案 加载失败——加载失败主要会由网络异常或服务器异常造成 不同的情况可能对应不同的空数据结果页面,附带不同的操作引导
例如网络异常,可以引导用户刷新页面:
或者确实是零结果,譬如没有订单信息,可以引导用户去进行订单的创建(引导消费):
小小总结一下,上述篇幅一直都在阐述一个道理:开发时,不能仅仅关注正常现象,要多考虑各种异常情况,思考全面,做好各种可能情况的处理。
1.4 图片的呈现及异常处理
图片在我们的业务中非常常见。但是要完美处理图片,并不轻松。
1.4.1 给图片同时设置高宽
有的时候产品和设计会商定,只能使用固定尺寸大小的图片,我们的布局可能是这样:
对应的布局:
<ul class="g-container">
<li>
<img src="http://placehold.it/150x100">
<p>图片描述</p>
</li>
</ul>
ul li img {
width: 150px;
}
假设后端接口出现一张非正常大小的图片,上述不加保护的布局就会出问题:
所以对于图片,我们总是建议同时写上高和宽,避免因为图片尺寸错误带来的布局问题。
ul li img {
width: 150px;
height: 100px;
}
另外,给 <img>
标签同时写上高宽,可以在图片加载之前提前占住位置,避免图片从未加载状态到渲染完成状态高宽变化引起的重排问题。
1.4.2 object-fit
限制高宽也可能会出现问题,比如图片被拉伸了,非常难看:
这个时候我们可以借助 object-fit
,它能够指定可替换元素的内容(也就是图片)该如何适应它的父容器的高宽。
ul li img {
width: 150px;
height: 100px;
object-fit: cover;
}
利用 object-fit: cover
,使图片内容在保持宽高比的同时填充元素的整个内容框。
object-fit
还有一个配套属性 object-position
,它可以控制图片在其内容框中的位置(类似于 background-position
),m 默认是 object-position: 50% 50%
。如果你不希望图片居中展示,可以使用它去改变图片实际展示的位置。
ul li img {
width: 150px;
height: 100px;
object-fit: cover;
object-position: 50% 100%;
}
像是这样,object-position: 100% 50%
指明从底部开始展示图片。
这里有一个很好的 Demo 可以帮助你理解
object-position
:
1.4.3 考虑屏幕 dpr:响应式图片
正常情况下,图片的展示应该没有什么问题了,但我们还可以做得更好。
在移动端或者一些高清的 PC 屏幕(例如苹果的 MacBook),屏幕的 dpr 可能大于 1。这种时候,我们可能还需要考虑利用多倍图去适配不同 dpr 的屏幕。
正好,<img>
标签提供了相应的属性 srcset
供我们操作。
<img src='photo@1x.png'
srcset='photo@1x.png 1x,
photo@2x.png 2x,
photo@3x.png 3x'
/>
不过,这是比较旧的写法,srcset
增加了新的 w
宽度描述符,需要配合 sizes
一起使用,所以更好的写法是:
<img
src = "photo.png"
sizes = “(min-width: 600px) 600px, 300px"
srcset = “photo@1x.png 300w,
photo@2x.png 600w,
photo@3x.png 1200w,
>
利用 srcset
,我们可以给不同 dpr 的屏幕提供最适合的图片。
1.4.4 图片丢失
接下来我们还需要考虑,当图片链接挂了,应该如何处理。
处理的方式有很多种。最好的处理方式,是我在张鑫旭老师《图片加载失败后 CSS 样式处理最佳实践》这篇文章中看到的。这里简单讲讲:
利用图片加载失败,触发 <img>
元素的onerror
事件,给加载失败的<img>
元素新增一个样式类;利用新增的样式类,配合 <img>
元素的伪元素,展示默认兜底图的同时,还能一起展示<img>
元素的alt
信息。
<img src="test.png" alt="图片描述" onerror="this.classList.add('error');">
img.error {
position: relative;
display: inline-block;
}
img.error::before {
content: "";
/** 定位代码 **/
background: url(error-default.png);
}
img.error::after {
content: attr(alt);
/** 定位代码 **/
}
我们利用伪元素 before
,加载默认错误兜底图,利用伪元素 after
,展示图片的 alt
信息:
到此,完整的对图片的处理就算完成了,完整的 Demo 你可以戳这里看看:CodePen Demo - 图片处理。
1.5 适当的过渡与动画
好的页面呈现需要适当的过渡与动画,让整体交互体验更加流畅。适当增加过渡与动画,能够很好地让用户感知到页面的变化,它们有如下作用:
减少认知负荷 防止变化视盲 空间上营造更好的印象 让用户界面鲜活起来
这一块内容也可以放在交互设计优化,读者朋友们了解就好。
1.5.1 loading 等待动画
页面上随处可见的 loading 效果,其实就是这样一种作用,让用户感知页面正在加载,或者正在处理某些事务。
1.5.2 骨架屏动画
骨架屏的布局与页面的视觉呈现保持一致,能引导用户将关注点聚焦到感兴趣的位置,并且能避免过长时间的等待。
1.5.3 滚动平滑:使用 scroll-behavior: smooth
让滚动丝滑
使用 scroll-behavior: smooth
,可以让滚动框实现平稳地滚动,而不是突兀地跳动。看看效果,假设如下结构:
<div class="g-container">
<nav>
<a href="#1">1</a>
<a href="#2">2</a>
<a href="#3">3</a>
</nav>
<div class="scrolling-box">
<section id="1">First section</section>
<section id="2">Second section</section>
<section id="3">Third section</section>
</div>
</div>
不使用 scroll-behavior: smooth
,就会突兀地跳动切换:
要改善这种现象,可以给可滚动容器添加 scroll-behavior: smooth
,实现平滑滚动:
{
scroll-behavior: smooth;
}
1.5.4 粘性滚动:使用 scroll-snap-type
优化滚动效果
sroll-snap-type
可能算得上是新的滚动规范里面最核心的一个属性样式。
scroll-snap-type:属性定义在滚动容器中的一个临时点(snap point)如何被严格地执行。
光看定义有点难理解,简单而言,这个属性规定了一个容器是否对内部滚动动作进行捕捉,并且规定了如何去处理滚动结束状态。让滚动操作结束后,元素停止在适合的位置。
看个简单示例:
当然,scroll-snap-type
用法非常多,可控制优化的点很多,限于篇幅无法一一展开。
1.5.5 控制滚动层级,避免页面大量重排
这个优化可能稍微有一点难理解。需要了解 CSS 渲染优化的相关知识。
先说结论,控制滚动层级的意思是尽量让需要进行 CSS 动画(可以是元素的动画,也可以是容器的滚动)的元素的 z-index 保持在页面最上方,避免浏览器创建不必要的图形层(GraphicsLayer),能够很好地提升渲染性能。
这一点怎么理解呢?一个元素触发创建一个 Graphics Layer 层的其中一个因素是:
元素有一个 z-index 较低且包含一个复合层的兄弟元素。
根据上述这点,我们对滚动性能进行优化的时候,需要注意两点:
通过生成独立的 GraphicsLayer,利用 GPU 加速,提升滚动的性能; 如果本身滚动没有性能问题,不需要独立的 GraphicsLayer,也要注意滚动容器的层级,避免因为层级过高而被其他创建了 GraphicsLayer 的元素合并,被动地生成一个 GraphicsLayer ,影响页面整体的渲染性能。
1.5.6 转场动画
从一个模块跳转到另外一个模块的时候,转场动画就派上了用场。它的作用在于:在合适的时机,将视线引导到适当的位置。
看下面的例子:
在点击按钮弹出弹窗的过程中,弹窗不是突兀地出现,而是从点击的地方放大至视窗中间,这个引导的过程让体验更加丝滑。
1.5.7 操作动画
这个和 loading 有点类似,遇到一些耗时操作,比如下载时,我们可以通过定制一个特殊的动画,减缓用户等待的烦躁、焦虑感。
上述动画的代码,你可以猛击:
当然,除了下载等待,我们也可以在一些重要的操作交互上,例如点赞、关注等,定制特殊的动画,让过程更加生动有趣。下面这个是某网站的点赞动画:
上述点赞动画的代码,你可以猛击:
1.5.8 使用过渡与动画的误区
合理使用动画能让页面增色不少,但同时要避免踩入下面的一些坑:
动画没有关联性 为了动画而动画,没有目的性 过于缓慢,阻碍交互 不够明确
简单解释一下。譬如动画关联性,关联性背后的逻辑能帮助用户在界面布局中理解刚发生的变化,是什么导致了变化。
下图中,左边是关联性差的,右边是关联性好的:
还有一点,大部分动画不宜过久,要足够迅速。
缓慢的动画产生了不必要的停顿。过渡动画应该保持简短,因为用户会频繁看到它们。让动画持续时间保持在 300ms 或更短。
看下图演示,同一个转场动画会被频繁触发,所以尽可能地让每次的动画不要持续过久,能够帮助用户节省更多时间。
总而言之,动画和过渡要用得恰当好处,避免为了动画而动画非常重要。
《Web 用户体验设计提升实践》(上篇)到这里就告一段落啦,下期我们将为大家带来关于交互设计和可访问性的实践分享,欢迎继续关注 “Shopee 技术团队”。
本文作者
Coco,前端开发工程师,来自 Shopee 供应链仓储管理(Warehouse management system, WMS)团队。
加入我们
Shopee WMS 支撑着 Shopee 所有自营业务和越来越多 Shopee 平台商家的仓储业务,是供应链核心系统之一,目前服务范围覆盖整个东南亚市场,支撑日百万级别包裹出库履约。我们致力于打造一套能面向不同市场不同文化能差异化操作的,支持药品、生鲜等多垂直领域的,未来能支撑日千万级包裹出库履约的仓储系统;我们持续优化流程、算法、升级系统架构,引入自动化设备,以达到降本增效的目的,同时使某些新的商业模式成为可能。
目前团队大量岗位持续招聘中,海量 HC 涵盖后端、前端、测试、产品等,感兴趣的同学可将简历发送至 kingsley.huang@shopee.com (亦可进行咨询,注明来自 Shopee 技术博客)。