查看原文
其他

Web用户体验设计提升实践(上)

Coco Shopee技术团队 2021-12-16

前言

本文是基于 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 {
    width1200px;
    margin0 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-width1200px;
}
.g-sidebar {
    flex-basis250px;
    margin-right10px;
}
.g-main {
    flex-grow1;
}

这里利用了 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 {
    height100vh;
    display: flex;
    flex-direction: column;
}

.g-footer {
    margin-top: auto;
    flex-shrink0;
    height30px;
    background: deeppink;
}

Codepen Demo - sticky footer by flex margin auto

当然,实现它的方法有很多,这里仅给出一种推荐的解法。

1.2 重点内容的排布设计

下面这一块关于重点内容的展示。

1.2.1 重要内容及功能的展示

让吸引用户注意力的元素前置。如果我们的页面存在需要让用户了解、处理的核心信息或者表单,尽可能将其位置放在上方,让用户更容易获取这部分信息。

将用户需要的信息、重要的功能展示出来而不是藏起来。

类似于导航、搜索等高频操作,一定不要让用户多次点击才能用到。

1.2.2 处理动态内容:文本超长

对于所有接收后端接口字段的文本展示类的界面,都需要考虑全面。正常情况如下,是没有问题的:

但是我们是否考虑到了文本会超长?超长了会折行还是换行?

对于单行文本,使用单行省略:

{
    width200px;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

当然,目前对于多行文本的超长省略,兼容性也已经非常好了:

{
    width200px;
    overflow : hidden;
    text-overflow: ellipsis;
    display: -webkit-box;
    -webkit-line-clamp2;
    -webkit-box-orient: vertical;
}

1.2.3 处理动态内容:保护边界

对于一些动态内容,我们经常使用 min/max-widthmin/max-height 对容器的高宽限度进行合理的控制。

在使用它们的时候,也有一些细节需要考虑到。譬如经常会使用 min-width 控制按钮的最小宽度:

.btn {
    ...
    min-width120px;
}

当内容比较少的时候是没问题的,但是如果内容比较长,就容易出现问题。下图就是使用了 min-width 却没考虑到按钮的过长的情况:

这里就需要配合 padding 一起:

.btn {
    ...
    min-width88px;
    padding0 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 {
    width150px;
}

假设后端接口出现一张非正常大小的图片,上述不加保护的布局就会出问题:

所以对于图片,我们总是建议同时写上高和宽,避免因为图片尺寸错误带来的布局问题。

ul li img {
    width150px;
    height100px;
}

另外,给 <img> 标签同时写上高宽,可以在图片加载之前提前占住位置,避免图片从未加载状态到渲染完成状态高宽变化引起的重排问题。

1.4.2 object-fit

限制高宽也可能会出现问题,比如图片被拉伸了,非常难看:

这个时候我们可以借助 object-fit,它能够指定可替换元素的内容(也就是图片)该如何适应它的父容器的高宽。

ul li img {
    width150px;
    height100px;
    object-fit: cover;
}

利用 object-fit: cover,使图片内容在保持宽高比的同时填充元素的整个内容框。

object-fit 还有一个配套属性 object-position,它可以控制图片在其内容框中的位置(类似于 background-position),m 默认是 object-position: 50% 50%。如果你不希望图片居中展示,可以使用它去改变图片实际展示的位置。

ul li img {
    width150px;
    height100px;
    object-fit: cover;
    object-position50% 100%;
}

像是这样,object-position: 100% 50% 指明从底部开始展示图片。

这里有一个很好的 Demo 可以帮助你理解 object-position

CodePen 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"";
    /** 定位代码 **/
    backgroundurl(error-default.png);
}
 
img.error::after {
    contentattr(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 有点类似,遇到一些耗时操作,比如下载时,我们可以通过定制一个特殊的动画,减缓用户等待的烦躁、焦虑感。

上述动画的代码,你可以猛击:

CodePen Demo - Download interaction By Milan Raring

当然,除了下载等待,我们也可以在一些重要的操作交互上,例如点赞、关注等,定制特殊的动画,让过程更加生动有趣。下面这个是某网站的点赞动画:

上述点赞动画的代码,你可以猛击:

CodePen Demo - Twitter 点赞动画

1.5.8 使用过渡与动画的误区

合理使用动画能让页面增色不少,但同时要避免踩入下面的一些坑:

  • 动画没有关联性
  • 为了动画而动画,没有目的性
  • 过于缓慢,阻碍交互
  • 不够明确

简单解释一下。譬如动画关联性,关联性背后的逻辑能帮助用户在界面布局中理解刚发生的变化,是什么导致了变化。

下图中,左边是关联性差的,右边是关联性好的:

还有一点,大部分动画不宜过久,要足够迅速。

缓慢的动画产生了不必要的停顿。过渡动画应该保持简短,因为用户会频繁看到它们。让动画持续时间保持在 300ms 或更短。

看下图演示,同一个转场动画会被频繁触发,所以尽可能地让每次的动画不要持续过久,能够帮助用户节省更多时间。

总而言之,动画和过渡要用得恰当好处,避免为了动画而动画非常重要

《Web 用户体验设计提升实践》(上篇)到这里就告一段落啦,下期我们将为大家带来关于交互设计和可访问性的实践分享,欢迎继续关注 “Shopee 技术团队”。

本文作者

Coco,前端开发工程师,来自 Shopee 供应链仓储管理(Warehouse management system, WMS)团队。

加入我们

Shopee WMS 支撑着 Shopee 所有自营业务和越来越多 Shopee 平台商家的仓储业务,是供应链核心系统之一,目前服务范围覆盖整个东南亚市场,支撑日百万级别包裹出库履约。我们致力于打造一套能面向不同市场不同文化能差异化操作的,支持药品、生鲜等多垂直领域的,未来能支撑日千万级包裹出库履约的仓储系统;我们持续优化流程、算法、升级系统架构,引入自动化设备,以达到降本增效的目的,同时使某些新的商业模式成为可能。

目前团队大量岗位持续招聘中,海量 HC 涵盖后端、前端、测试、产品等,感兴趣的同学可将简历发送至 kingsley.huang@shopee.com (亦可进行咨询,注明来自 Shopee 技术博客)。

: . Video Mini Program Like ,轻点两下取消赞 Wow ,轻点两下取消在看

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存