前言
在 uni-app 开发中 scroll-view 组件用到几率也是比较大滴,存在问题主要是:点击子元素,子元素在什么位置展示?
今天我们来好好总结一下 0.0~
Part.1 可能出现的需求
效果一:当前点击子元素靠左展示
效果二:当前点击子元素靠左留一展示
效果三:当前点击子元素居中展示
应该常见的用户体验效果就这三种了,我们看看怎么实现?go~
Part.2 我的思路
在 uni-app 的官方( https://uniapp.dcloud.io/component/scroll-view ) API中对 scroll-view 组件有详细的介绍和属性说明,今天我们主要用到的属性是:scroll-left (设置横向滚动条的位置)
一般偷懒或者常用的方式是使用 scroll-into-view 这个属性,随着当前点击元素的ID滚动,但是这个属性制作出来后会和我上面 效果一 一样靠左展示,这种展示方式体验不是太好(从前往后点击可能还好,但是从后往前就很...),这种方式配合 swiper 或者其它组件做长列表或者其它还可勉强接受,因为不用去点击,直接手动滑动就可切换。但是假如不存在手可滑动的话,就会很糟糕。
接下来我们具体来看看,这三种效果如何实现:
效果一: 可直接使用 scroll-into-view 属性实现 或者 也可使用 scroll-left
思路:第一种, scroll-into-view 绑定一个动态 ID,子元素循环产出ID,点击时进行绑定(这次就不做代码产出了)
第二种, 计算每个子元素的宽度,点击时获取当前点击元素前面的元素宽度之和
效果二:使用 scroll-left
思路:计算每个子元素的宽度,点击时获取当前点击元素索引 - 1 的前面元素宽度之和,相比于效果一的第二种情况,这里少算当前点击元素前面的一个元素的宽度,实现留一
效果三:使用 scroll-left
思路:当前点击子元素距离左边栏的距离 - scroll-view 宽度的一半 + 当前点击子元素一半的宽度 实现居中展示
Part.3 代码实现
1 <template> 2 <view class="lxy-content"> 3 <scroll-view scroll-x="true" 4 class="content-scroll" 5 scroll-with-animation 6 :scroll-left="scrollLeft"> 7 <view v-for="(item, index) in category" 8 :key="index" 9 class="scroll-item" 10 @click="changeTitle(index)"> 11 <text class="item-text" 12 :class="curIndex == index? 'active' : ''">{{item.name}}</text> 13 </view> 14 </scroll-view> 15 </view> 16 </template>
1 <script> 2 export default { 3 data() { 4 return { 5 category: [ 6 { 7 id: 1, 8 name: '星期一' 9 }, 10 { 11 id: 2, 12 name: '星期二' 13 }, 14 { 15 id: 3, 16 name: '星期三' 17 }, 18 { 19 id: 4, 20 name: '星期四' 21 }, 22 { 23 id: 5, 24 name: '星期五' 25 }, 26 { 27 id: 6, 28 name: '星期六' 29 }, 30 { 31 id: 7, 32 name: '星期七' 33 }, 34 { 35 id: 8, 36 name: '星期八' 37 }, 38 { 39 id: 9, 40 name: '星期九' 41 }, 42 { 43 id: 10, 44 name: '星期十' 45 } 46 ], 47 48 contentScrollW: 0, // 导航区宽度 49 curIndex: 0, // 当前选中 50 scrollLeft: 0, // 横向滚动条位置 51 } 52 }, 53 mounted() { 54 // 获取标题区域宽度,和每个子元素节点的宽度 55 this.getScrollW() 56 }, 57 methods: { 58 // 获取标题区域宽度,和每个子元素节点的宽度以及元素距离左边栏的距离 59 getScrollW() { 60 const query = uni.createSelectorQuery().in(this); 61 62 query.select('.content-scroll').boundingClientRect(data => { 63 // 拿到 scroll-view 组件宽度 64 this.contentScrollW = data.width 65 }).exec(); 66 67 query.selectAll('.scroll-item').boundingClientRect(data => { 68 let dataLen = data.length; 69 for (let i = 0; i < dataLen; i++) { 70 // scroll-view 子元素组件距离左边栏的距离 71 this.category[i].left = data[i].left; 72 // scroll-view 子元素组件宽度 73 this.category[i].width = data[i].width 74 } 75 }).exec() 76 }, 77 78 79 // 选择标题 80 changeTitle(index) { 81 this.curIndex = index; 82 83 // 效果一(当前点击子元素靠左展示) 局限性:子元素宽度相同 84 this.scrollLeft = index * this.category[index].width 85 86 // 效果一(当前点击子元素靠左展示) 子元素宽度不相同也可实现 87 /* this.scrollLeft = 0; 88 for (let i = 0; i < index; i++) { 89 this.scrollLeft += this.category[i].width 90 }; */ 91 92 93 // 效果二(当前点击子元素靠左留一展示) 局限性:子元素宽度相同 94 /* this.scrollLeft = (index - 1) * this.category[index].width */ 95 96 // 效果二(当前点击子元素靠左留一展示) 子元素宽度不相同也可实现 97 /* this.scrollLeft = 0; 98 for (let i = 0; i < index - 1; i++) { 99 this.scrollLeft += this.category[i].width 100 }; */ 101 102 103 // 效果三(当前点击子元素居中展示) 不受子元素宽度影响 104 /* this.scrollLeft = this.category[index].left - this.contentScrollW / 2 + this.category[index].width / 2; */ 105 106 } 107 } 108 } 109 </script>
1 <style lang="scss" scoped> 2 .lxy-content { 3 width: 100%; 4 height: 100rpx; 5 margin-top: 50rpx; 6 box-sizing: border-box; 7 .content-scroll { 8 height: 100rpx; 9 white-space: nowrap; 10 .scroll-item { 11 height: 100rpx; 12 padding: 0 20rpx; 13 display: inline-block; 14 text-align: center; 15 .item-text { 16 font-size: 30rpx; 17 line-height: 100rpx; 18 &.active { 19 color: #1468FF; 20 } 21 } 22 } 23 } 24 } 25 </style>