Cookbook-如何实现全屏左右滑动的效果
全屏左右滑动是快应用开发中比较常用的一个功能。一般使用场景是结合分类信息来左右滑动切换分类,展示不同分类下的信息;或者左右滑动切换不同的 tab 页(首页、我的页面等)
本篇文章简单介绍全屏左右滑动的几种不同实现方式。
首先,我们先设置一些模拟数据,后面所有的示例都会使用这些模拟数据
export const tabList = [
"热榜",
"推荐",
"原创",
"科技",
"娱乐",
"财经",
"民生",
"宠物",
"教育",
"旅游",
];
使用 tabs + tab-bar + tab-content 实现
实现全屏左右滑动的效果最简单的方式是使用引擎提供的 tabs + tab-bar + tab-content
三件套的方式来实现
tabs
中封装了常见功能和效果:页签支持横向滚动,支持手势滑动切换内容页等
tabs
内部仅支持子组件tab-bar
和tab-content
,也可以只包含一个子组件,使用说明如下:
tab-bar组件
用来包含所有页签的标题,属性mode
用来配置是否可滚动tab-content组件
用来包含所有页签的内容tab-bar组件
的第 n 个直接子节点对应tab-content
中第 n 个直接子节点,具有联动效果
首先,我们新建一个 ux 文件,并且添加上 ux 文件的通用内容
<template>
<div></div>
</template>
<script></script>
<style></style>
然后,在 script 标签里面,引入定义好的模拟数据
<script>
import { tabList } from '../../../helper/const.js'
export default {
data() {
return {
list: tabList
}
}
}
</script>
之后,在 template 标签中,使用 tabs + tab-bar + tab-content 三个标签和模拟数据,实现全屏左右滑动的效果
<template>
<div>
<tabs>
<tab-bar mode="scrollable">
<div class="bar-item" for="list">
<text>{{ $item.title }}</text>
</div>
</tab-bar>
<tab-content>
<div for="list" class="content">
<demo-list></demo-list>
</div>
</tab-content>
</tabs>
</div>
</template>
注意,tab-bar 的 mode 为 scrollable 时,子组件宽度为设置宽度,当宽度之和大于 tab-bar 宽度,子组件可以横向滚动;mode 为 fixed 时,子组件宽度均分 tab-bar 宽度,当宽度之和大于 tab-bar 宽度,子组件依旧均分宽度。如果 tab-bar 的子组件比较多,建议将 mode 设置为 scrollable,否则子组件的展示样式会和设置的 css 样式不一致
此时已经可以实现全屏左右滑动的效果了,但是无法分辨当前展示的是哪一个页签,交互体验不够友好,所以需要增加一些样式让我们能够知道当前展示的是哪一个页签。为此,我们需要先获取到当前处于活跃状态的 index。修改 script 标签里的内容,增加一个 curIdx 变量用于保存当前活跃的 index,并且在 tabs 的 change 处理事件中修改 curIdx 的值
<script>
import { tabList } from '../../../helper/const.js'
export default {
data() {
return {
list: tabList,
curIdx:0,
}
},
changeTab(e){
this.curIdx = e.index
}
}
</script>
然后修改 template 中的内容,给当前处于活跃状态的 tab-bar 的文字设置不一样的文字颜色
<template>
<div>
<tabs onchange="changeTab">
<tab-bar mode="scrollable" class="tab-bar">
<div class="bar-item" for="list">
<text style="color: {{curIdx === $idx ? '#456fff' : '#000000'}};">{{
$item
}}</text>
<div
style="background-color: {{curIdx === $idx ? '#456fff' : '#ffffff'}};"
class="line"
></div>
</div>
</tab-bar>
<tab-content>
<div for="list" class="content">
<demo-list></demo-list>
</div>
</tab-content>
</tabs>
</div>
</template>
这样,就能明显提升全屏左右滑动过程中的交互体验了
完整的代码如下
<import name="demo-list" src="../../../components/demoList.ux"></import>
<template>
<div>
<tabs onchange="changeTab">
<tab-bar mode="scrollable" class="tab-bar">
<div class="bar-item" for="list">
<text style="color: {{curIdx === $idx ? '#456fff' : '#000000'}};">{{
$item
}}</text>
<div
style="background-color: {{curIdx === $idx ? '#456fff' : '#ffffff'}};"
class="line"
></div>
</div>
</tab-bar>
<tab-content>
<div for="list" class="content">
<demo-list></demo-list>
</div>
</tab-content>
</tabs>
</div>
</template>
<script>
import { tabList } from "../../../helper/const.js";
export default {
data() {
return {
list: tabList,
curIdx: 0,
};
},
changeTab(e) {
this.curIdx = e.index;
},
};
</script>
<style lang="less">
@import url("../../../assets/styles/style.less");
.tab-bar {
border-bottom: 1px solid @border-color;
height: 7 * @size-factor;
}
.bar-item {
padding: 0 2 * @size-factor;
flex-direction: column;
align-items: center;
text {
font-size: 3 * @size-factor;
line-height: 3 * @size-factor;
}
.line {
width: 5 * @size-factor;
height: 0.5 * @size-factor;
margin-top: 1 * @size-factor;
border-radius: 0.5 * @size-factor;
}
}
.content {
justify-content: center;
text {
font-size: 5 * @size-factor;
}
}
</style>
使用 tab-content 加 list 实现
tabs内部可以只可以使用
tab-bar或者
tab-content,假设开发者需要在页签一侧增加一个按钮,同时页签需要和内容联动。由于tabs
仅支持子组件tab-bar
与tab-content
,且tab-bar
与tab-content
的直接子元素都被当做页签或内容页。因此,仅使用tabs
无法实现在页签右侧增加一个图标按钮。
这时,我们可以仅使用 tabs+tab-content,再用 list 来实现页签,并通过 js 代码动态绑定 tabs 的 index 属性来实现页签与内容的联动。
我们简单修改一下上面的代码,将 tab-bar 里的内容用 list 实现,并且在 list 的右侧增加一个按钮图标
<import name="q-icon" src="qaui/src/components/icon/index"></import>
<import name="demo-list" src="../../../components/demoList.ux"></import>
<template>
<div class="wrap">
<div class="header">
<list class="list" id="list">
<list-item class="bar-item" type="bar-item" for="list">
<text style="color: {{curIdx === $idx ? '#456fff' : '#000000'}};">{{
$item
}}</text>
<div
style="background-color: {{curIdx === $idx ? '#456fff' : '#ffffff'}};"
class="line"
></div>
</list-item>
</list>
<div class="shadow"></div>
<div class="icon-wrap" onclick="clickMenu">
<q-icon type="menu" size="20"></q-icon>
</div>
</div>
<tabs onchange="changeTab" index="{{curIdx}}">
<tab-content>
<div for="list" class="content">
<demo-list></demo-list>
</div>
</tab-content>
</tabs>
</div>
</template>
<script>
import { tabList } from "../../../helper/const.js";
import prompt from "@system.prompt";
export default {
data() {
return {
list: tabList,
curIdx: 0,
};
},
changeTab(e) {
this.curIdx = e.index;
},
clickMenu() {
prompt.showToast({
message: "你点击了menu",
});
},
};
</script>
<style lang="less">
@import url("../../../assets/styles/style.less");
.wrap {
flex-direction: column;
}
.header {
border-bottom: 1px solid @border-color;
}
.list {
flex-direction: row;
height: 7 * @size-factor;
}
.icon-wrap {
height: 7 * @size-factor;
padding: 1 * @size-factor 2 * @size-factor;
}
.shadow {
width: 1 * @size-factor;
height: 7 * @size-factor;
background: linear-gradient(270deg, #f8f8f8 0%, #ffffff 100%);
}
.bar-item {
padding: 0 2 * @size-factor;
flex-direction: column;
align-items: center;
text {
font-size: 3 * @size-factor;
margin-top: 1.2 * @size-factor;
}
.line {
width: 5 * @size-factor;
height: 0.5 * @size-factor;
margin-top: 1 * @size-factor;
border-radius: 0.5 * @size-factor;
}
}
.content {
justify-content: center;
text {
font-size: 5 * @size-factor;
}
}
</style>
注意,list 默认是竖向排列的,这里我们需要给 list 的 class 类里面加上flex-direction: row
,让 list 可以横向排列
此时内容左右滑动时,页签无法跟着一起滚动,体验不是很好;同时,点击页签也无法切换内容。所以我们需要做一些修改,让页签和内容能够联动起来。
首先,我们给页签添加一个点击事件,同时将当前页签的 index 作为参数传递给事件处理函数,然后将页签的 index 赋值给 curIdx 变量,并且将 curIdx 变量绑定到 tabs 组件的 index 属性上。这样,就可以实现点击页签切换内容了。最后,在 tabs 的 change 事件的处理函数里面,将页签 list 中当前活跃的页签项滚动到中间。
<import name="q-icon" src="qaui/src/components/icon/index"></import>
<import name="demo-list" src="../../../components/demoList.ux"></import>
<template>
<div class="wrap">
<div class="header">
<list class="list" id="list">
<list-item
class="bar-item"
type="bar-item"
for="list"
onclick="clickTab($idx)"
>
<text style="color: {{curIdx === $idx ? '#456fff' : '#000000'}};">{{
$item
}}</text>
<div
style="background-color: {{curIdx === $idx ? '#456fff' : '#ffffff'}};"
class="line"
></div>
</list-item>
</list>
<div class="shadow"></div>
<div class="icon-wrap" onclick="clickMenu">
<q-icon type="menu" size="20"></q-icon>
</div>
</div>
<tabs onchange="changeTab" index="{{curIdx}}">
<tab-content>
<div for="list" class="content">
<demo-list></demo-list>
</div>
</tab-content>
</tabs>
</div>
</template>
<script>
import { tabList } from "../../../helper/const.js";
import prompt from "@system.prompt";
export default {
data() {
return {
list: tabList,
curIdx: 0,
};
},
changeTab(e) {
this.curIdx = e.index;
this.$element("list").scrollTo({
index: this.curIdx - 2 >= 0 ? this.curIdx - 2 : 0,
behavior: "smooth",
});
},
clickTab(idx) {
this.curIdx = idx;
},
clickMenu() {
prompt.showToast({
message: "你点击了menu",
});
},
};
</script>
最终的效果如下,页签和内容能够正确联动,并且点击页签也能够正确切换内容
使用 swiper 加 list 实现
其实,内容部分除了使用 tabs 之外,也可以使用 swiper。
使用 swiper 承载内容部分有两个好处,一个是内容部分可以自动滚动,二是可以循环滚动,这在某些场景下十分有用。
使用 swiper 承载内容也有一些需要注意的点
- swiper 默认高度自适应内容,所以需要将其高度设为 100%,才能占满剩余空间
- swiper 默认会展示 indicator,如果不需要需要将 swiper 的 indicator 属性设置为 false
我们将上面的代码简单修改一下,将 tabs 及 tabcontent 替换为 swiper 组件
<import name="q-icon" src="qaui/src/components/icon/index"></import>
<import name="demo-list" src="../../../components/demoList.ux"></import>
<template>
<div class="wrap">
<div class="header">
<list class="list" id="list">
<list-item
class="bar-item"
type="bar-item"
for="list"
onclick="clickTab($idx)"
>
<text style="color: {{curIdx === $idx ? '#456fff' : '#000000'}};">{{
$item
}}</text>
<div
style="background-color: {{curIdx === $idx ? '#456fff' : '#ffffff'}};"
class="line"
></div>
</list-item>
</list>
<div class="shadow"></div>
<div class="icon-wrap" onclick="clickMenu">
<q-icon type="menu" size="20"></q-icon>
</div>
</div>
<swiper
class="swiper"
onchange="changeTab"
index="{{curIdx}}"
indicator="false"
loop="true"
autoplay="true"
>
<div for="list" class="content">
<demo-list></demo-list>
</div>
</swiper>
</div>
</template>
<script>
import { tabList } from "../../../helper/const.js";
import prompt from "@system.prompt";
export default {
data() {
return {
list: tabList,
curIdx: 0,
};
},
changeTab(e) {
this.curIdx = e.index;
this.$element("list").scrollTo({
index: this.curIdx - 2 >= 0 ? this.curIdx - 2 : 0,
behavior: "smooth",
});
},
clickTab(idx) {
this.curIdx = idx;
},
clickMenu() {
prompt.showToast({
message: "你点击了menu",
});
},
};
</script>
<style lang="less">
@import url("../../../assets/styles/style.less");
.wrap {
flex-direction: column;
}
.header {
border-bottom: 1px solid @border-color;
}
.swiper {
height: 100%;
}
.list {
flex-direction: row;
height: 7 * @size-factor;
}
.icon-wrap {
height: 7 * @size-factor;
padding: 1 * @size-factor 2 * @size-factor;
}
.shadow {
width: 1 * @size-factor;
height: 7 * @size-factor;
background: linear-gradient(270deg, #f8f8f8 0%, #ffffff 100%);
}
.bar-item {
padding: 0 2 * @size-factor;
flex-direction: column;
align-items: center;
text {
font-size: 3 * @size-factor;
margin-top: 1 * @size-factor;
}
.line {
width: 5 * @size-factor;
height: 0.5 * @size-factor;
margin-top: 1 * @size-factor;
border-radius: 0.5 * @size-factor;
}
}
.content {
justify-content: center;
text {
font-size: 5 * @size-factor;
}
}
</style>
此时的效果如下,内容能够自动滚动并且能够循环滚动
使用 QaUI 的 tabs 组件实现
除了使用快应用提供的原生组件之外,也可以使用封装好的一些组件库来实现这个功能。例如,我们可以使用 QaUI 的 tabs 组件来实现这个功能。对代码做一下简单修改
<import name="q-tabs" src="qaui/src/components/tabs/index"></import>
<import name="demo-list" src="../../../components/demoList.ux"></import>
<template>
<div>
<q-tabs
index="0"
type="default"
data="{{ list }}"
active-color="#456fff"
background="#FFF"
active-background="#FFF"
ontap="tap"
>
<block for="{{ list }}">
<demo-list></demo-list>
</block>
</q-tabs>
</div>
</template>
<script>
export default {
data() {
return {
list: [
{
label: '热榜'
},
{
label: '推荐'
},
{
label: '原创'
},
{
label: '科技'
},
{
label: '娱乐'
},
{
label: '财经'
},
{
label: '民生'
},
{
label: '宠物'
},
{
label: '教育'
},
{
label: '旅游'
}
]
}
}
}
</script>
<style></style>
注意:使用组件库时需要注意下使用的组件对于数据的格式是否有一些特殊的要求
此时的效果如下,QaUI 的 tabs 组件还支持很多其他的配置项,具体的可以去看官方文档
体验 Sample
扫码上面的二维码即可体验本文提到的相关 Sample
或者点击右侧链接查看源代码https://rpk.quickapp.cn/s/7cbb4d57ae6443c390a14b4ae4c744b2