巧用滑动选项卡,提升用户体验

滑动选项卡

目前针对移动设备的Cordova应用程序和渐进式的Web应用程序非常流行。提升用户体验和交互的关键是传递出原生的视觉效果和感觉,这并不总是一件容易的事情。当然,新建有样式装饰的多选复选框(checkboxes)和单选按钮(radio buttons)是很容易的,但是提供我们所追求的质量飞跃的真正特性是基于用户交互的。

滑动选项卡将内容分割成不同的页面,并且它允许用户使用手指将自己想要的页面滑到当前视图。那如果,在用户拖拽页面的同时,这个应用程序随着拖拽逐渐改变自己的外观呢?是不是听起来很酷炫但是有点难呢?让我们来看看用Vue.js实现有多么简单。

开始吧

首先,我们需要一个真正的滑动选项卡组件。有很多可供选择的提供了不同的特性的这样的组件,这里我们将会使用Onsen UI提供的选项卡,它允许在滑动的时候执行自定义操作。以防你不知道这个,Onsen UI针对Vue应用程序有一系列的iOS和安卓组件。针对已存在的项目,可以使用NPM或者Yarn安装。

$> npm install onsenui vue-onsenui --save-dev
$> yarn add onsenui vue-onsenui -D

应用程序里必须有的一些文件:

import 'onsenui/css/onsenui.css'; // Webpack CSS import
import 'onsenui/css/onsen-css-components.css'; // Webpack CSS import
import VueOnsen from 'vue-onsenui';
Vue.use(VueOnsen);

另外,新的项目通过Vue CLI马上就可以开始。还可以自行选择添加Vuex和一些其它的特性。

$> vue init OnsenUI/vue-cordova-webpack # For Cordova apps
$> vue init OnsenUI/vue-pwa-webpack # For PWA

这样之后,就可以在这个应用程序中使用这个组件了。

Vue里的滑动选项卡

在Vue模板里用Onsen UI添加一个最小的滑动选项卡非常简单。可以像下面这样定义:

<v-ons-tabbar swipeable :tabs="tabs" />

swipeable属性可以在应用程序的不同时刻切换允许滑动和不允许滑动,如果有必要的话。 tabs是一个简单的数组对象,描述了每个选项卡的外观和每个页面、标签和图标属性的内容。这个组件完整的参考页面点击这里。

在顶部,可以使用更多的设置来修改默认的表现形式,添加一些额外的自定义的属性设置,可以获得独一无二的应用程序样式。让我们一起来看看吧,例如,为了区分一个应用程序中不同的部分,怎样使用 on-swipe属性,让它可以在滑动的时候逐渐的改变界面的颜色呢?(在文章的最后有链接到真实的应用程序)。

颜色插值

代码片段:

<v-ons-page>
    <v-ons-toolbar :style="swipeTheme">
      <div class="center">Swiping Tab Bar</div>
    </v-ons-toolbar>

    <v-ons-tabbar
      swipeable
      position="top"
      :tabs="tabs"
      :on-swipe="onSwipe"
      :tabbar-style="swipeTheme"
    />
  </v-ons-page>
</template>

<script>
import Home from './pages/Home.vue';
import Forms from './pages/Forms.vue';
import Animations from './pages/Animations.vue';
// Just a linear interpolation formula
const lerp = (x0, x1, t) => parseInt((1 - t) * x0 + t * x1, 10);
// RGB colors
const red = [244, 67, 54];
const blue = [30, 136, 229];
const purple = [103, 58, 183];
export default {
  data () {
    return {
      colors: red,
      animationOptions: {},
      tabs: [
        {
          page: Home,
          label: 'Home',
          theme: red
        },
        {
          page: Forms,
          label: 'Forms',
          theme: blue
        },
        {
          page: Animations,
          label: 'Anim',
          theme: purple
        }
      ]
    };
  },
  computed: {
    swipeTheme() {
      return {
        backgroundColor: `rgb(${this.colors.join(',')})`,
        transition: `all ${this.animationOptions.duration || 0}s ${this.animationOptions.timing || ''}`
      }
    }
  },
  methods: {
    onSwipe(index, animationOptions) {
      // Apply the same transition as v-ons-tabbar
      this.animationOptions = animationOptions;
      // Interpolate colors
      const a = Math.floor(index), b = Math.ceil(index), ratio = index % 1;
      this.colors = this.colors.map((c, i) => lerp(this.tabs[a].theme[i], this.tabs[b].theme[i], ratio));
    }
  }
};
</script>

这个代码使用 v-ons-pagev-ons-toolbarv-ons-tabbar 组件新建了一个带有简单的工具栏和选项卡的页面。 tabs属性包括了一个选项卡数组。 pagelabel这两个属性都可以被选项卡组件自己使用来描述这个选项卡的内容和外观,但是这并不能阻止我们用自定义的属性如 theme或者其它的属性。这些主题都是RGB颜色的简单数组。我们马上就可以知道为什么这个形式可以很好的描述主题。

注意, swipeTheme计算属性是怎么传递给工具栏的(通过 style属性)和选项卡的(通过 tabbar-style属性)。无论什么时候改变这个属性,这两个组件的样式都会更新。在 on-swipe属性中,也提供了 onSwipe方法,当用户的手指在屏幕上滑动的时候总是会调用这个方法。但是我们现在怎么合适地改变界面的颜色呢?

线性插值

简单地射线,线性插值(在计算机图形学中的“lerp”)是基于一些输入,然后可以生成出两个数值(比如颜色)中间的点的一个公式。比如,我们想在屏幕上把一个点从初始位置X0逐渐移动到终点x1。我们只需要把这两个点一起给这个公式还有这个“完成率”以确定这个点在特定时刻该放置在哪个位置。换句话说,这个比率(或者叫alpha值)描述了我们距离终点有多远。

const lerp = (x0, x1, r) => (1 — r) * x0 + r * x1;

比如,如果我们想生成从x0到x1之间的三个点(不包括x0和x1),第一步需要 r=0.25(完成25%);第二步 r=0.5 (完成50%,换句话说,在中点);第三步 r=0.75(完成75%)。当然,通过提供不同的比率我们可以想生成多少就生成多少中间点。

这不仅仅适用于物理的距离,在之前的代码里,我们想根据滑动的位置逐渐把一个颜色变换成另一个颜色。为了实现这个,我们需要把颜色表示成离散的值并且知道两个页面之间滑动的比率。更精确来说,RGB颜色是由三个值组成,可以分开进行插值。其它的表示也可以进行插值但是也意味着需要更复杂的代码。

除了这些,滑动选项卡组件在 onSwipe钩子中,也提供了当前页面的十进制指数。比如 1.65的指数意思是当前滑动的是在页面1和页面2的65%( r=0.65)。

因此,如果不同的RGB颜色数组被分配给每个选项卡的话,我们就可以基于当前的滑动给每个值进行插值了:

onSwipe(index, animationOptions) {
  this.animationOptions = animationOptions;

  const x0 = Math.floor(index),
    x1 = Math.ceil(index),
    ratio = index % 1;

  this.colors = this.colors.map((c, i) =>
    lerp(
      this.tabs[x0].theme[i],
      this.tabs[x1].theme[i],
      ratio
    )
  );
}

这给 this.colors分派了一个新的颜色数组,基于这些颜色,将会被计算属性 this.swipeTheme被动的使用来创建一个有效的CSS语法。

注意 animationOptions也作为第二个参数给出来了,当滑动的时候它将会变空,当释放这次滑动的时候,选项卡将会使用手指的速度来结束这个滑动动画。这个速度在这个参数中将会被作为 durationtiming(Cubic Bezier curves)并且会用来创造出CSS动画。这样,所有的动画(所有页面,选项卡边界和颜色)将会同步。

接下来是什么呢

正如你知道的,Vue的声明的性质使得这些所有东西都保持得很简单。我们可以只更新特定的属性而不用先从DOM中获取到元素再手动修改样式。

一个完整的包涵之前(甚至更多)所有的代码的Cordova应用程序在这里。它根据相同的概念添加了更多的插值。如果你想了解更多关于针对Vue的Onsen UI,可以在这里看官网。

我邀请你尝试不同的动画,然后分享到Twitter :)

敲代码愉快!

感谢Erisu 和Naoki Matagawa.


往期精选文章

使用虚拟dom和JavaScript构建完全响应式的UI框架

扩展 Vue 组件

使用Three.js制作酷炫无比的无穷隧道特效

一个治愈JavaScript疲劳的学习计划

全栈工程师技能大全

WEB前端性能优化常见方法

一小时内搭建一个全栈Web应用框架

干货:CSS 专业技巧

四步实现React页面过渡动画效果

让你分分钟理解 JavaScript 闭包



小手一抖,资料全有。长按二维码关注京程一灯,阅读更多技术文章和业界动态。

本文分享自微信公众号 - 京程一灯(jingchengyideng)

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2017-11-03

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

扫码关注云+社区

领取腾讯云代金券