u-scroll-list.vue 6.02 KB
Newer Older
潘永坪's avatar
潘永坪 committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224
<template>
	<view
		class="u-scroll-list"
		ref="u-scroll-list"
	>
		<!-- #ifdef APP-NVUE -->
		<!-- nvue使用bindingX实现,以得到更好的性能 -->
		<scroller
			class="u-scroll-list__scroll-view"
			ref="u-scroll-list__scroll-view"
			scroll-direction="horizontal"
			:show-scrollbar="false"
			:offset-accuracy="1"
			@scroll="nvueScrollHandler"
		>
			<view class="u-scroll-list__scroll-view__content">
				<slot />
			</view>
		</scroller>
		<!-- #endif -->
		<!-- #ifndef APP-NVUE -->
		<!-- #ifdef MP-WEIXIN || APP-VUE || H5 || MP-QQ -->
		<!-- 以上平台,支持wxs -->
		<scroll-view
			class="u-scroll-list__scroll-view"
			scroll-x
			@scroll="wxs.scroll"
			@scrolltoupper="wxs.scrolltoupper"
			@scrolltolower="wxs.scrolltolower"
			:data-scrollWidth="scrollWidth"
			:data-barWidth="$u.getPx(indicatorBarWidth)"
			:data-indicatorWidth="$u.getPx(indicatorWidth)"
			:show-scrollbar="false"
			:upper-threshold="0"
			:lower-threshold="0"
		>
			<!-- #endif -->
			<!-- #ifndef APP-NVUE || MP-WEIXIN || H5 || APP-VUE || MP-QQ -->
			<!-- 非以上平台,只能使用普通js实现 -->
			<scroll-view
				class="u-scroll-list__scroll-view"
				scroll-x
				@scroll="scrollHandler"
				@scrolltoupper="scrolltoupperHandler"
				@scrolltolower="scrolltolowerHandler"
				:show-scrollbar="false"
				:upper-threshold="0"
				:lower-threshold="0"
			>
				<!-- #endif -->
				<view class="u-scroll-list__scroll-view__content">
					<slot />
				</view>
			</scroll-view>
			<!-- #endif -->
			<view
				class="u-scroll-list__indicator"
				v-if="indicator"
				:style="[$u.addStyle(indicatorStyle)]"
			>
				<view
					class="u-scroll-list__indicator__line"
					:style="[lineStyle]"
				>
					<view
						class="u-scroll-list__indicator__line__bar"
						:style="[barStyle]"
						ref="u-scroll-list__indicator__line__bar"
					></view>
				</view>
			</view>
	</view>
</template>

<script
	src="./scrollWxs.wxs"
	module="wxs"
	lang="wxs"
></script>

<script>
/**
 * scrollList 横向滚动列表
 * @description 该组件一般用于同时展示多个商品、分类的场景,也可以完成左右滑动的列表。
 * @tutorial https://www.uviewui.com/components/scrollList.html
 * @property {String | Number}	indicatorWidth			指示器的整体宽度 (默认 50 )
 * @property {String | Number}	indicatorBarWidth		滑块的宽度 (默认 20 )
 * @property {Boolean}			indicator				是否显示面板指示器 (默认 true )
 * @property {String}			indicatorColor			指示器非激活颜色 (默认 '#f2f2f2' )
 * @property {String}			indicatorActiveColor	指示器的激活颜色 (默认 '#3c9cff' )
 * @property {String | Object}	indicatorStyle			指示器样式,可通过bottom,left,right进行定位
 * @event {Function} left	滑动到左边时触发
 * @event {Function} right	滑动到右边时触发
 * @example
 */
// #ifdef APP-NVUE
const dom = uni.requireNativePlugin('dom')
import nvueMixin from "./nvue.js"
// #endif
import props from './props.js';
export default {
	name: 'u-scroll-list',
	mixins: [uni.$u.mpMixin, uni.$u.mixin, props],
	// #ifdef APP-NVUE
	mixins: [uni.$u.mpMixin, uni.$u.mixin, nvueMixin, props],
	// #endif
	data() {
		return {
			scrollInfo: {
				scrollLeft: 0,
				scrollWidth: 0
			},
			scrollWidth: 0
		}
	},
	computed: {
		// 指示器为线型的样式
		barStyle() {
			const style = {}
			// #ifndef APP-NVUE || MP-WEIXIN || H5 || APP-VUE || MP-QQ
			// 此为普通js方案,只有在非nvue和不支持wxs方案的端才使用、
			// 此处的计算理由为:scroll-view的滚动距离与目标滚动距离(scroll-view的实际宽度减去包裹元素的宽度)之比,等于滑块当前移动距离与总需
			// 滑动距离(指示器的总宽度减去滑块宽度)的比值
			const scrollLeft = this.scrollInfo.scrollLeft,
				scrollWidth = this.scrollInfo.scrollWidth,
				barAllMoveWidth = this.indicatorWidth - this.indicatorBarWidth
			const x = scrollLeft / (scrollWidth - this.scrollWidth) * barAllMoveWidth
			style.transform = `translateX(${ x }px)`
			// #endif
			// 设置滑块的宽度和背景色,是每个平台都需要的
			style.width = uni.$u.addUnit(this.indicatorBarWidth)
			style.backgroundColor = this.indicatorActiveColor
			return style
		},
		lineStyle() {
			const style = {}
			// 指示器整体的样式,需要设置其宽度和背景色
			style.width = uni.$u.addUnit(this.indicatorWidth)
			style.backgroundColor = this.indicatorColor
			return style
		}
	},
	mounted() {
		this.init()
	},
	methods: {
		init() {
			this.getComponentWidth()
		},
		// #ifndef APP-NVUE || MP-WEIXIN || H5 || APP-VUE || MP-QQ
		// scroll-view触发滚动事件
		scrollHandler(e) {
			this.scrollInfo = e.detail
		},
		scrolltoupperHandler() {
			this.scrollEvent('left')
			this.scrollInfo.scrollLeft = 0
		},
		scrolltolowerHandler() {
			this.scrollEvent('right')
			// 在普通js方案中,滚动到右边时,通过设置this.scrollInfo,模拟出滚动到右边的情况
			// 因为上方是用过computed计算的,设置后,会自动调整滑块的位置
			this.scrollInfo.scrollLeft = uni.$u.getPx(this.indicatorWidth) - uni.$u.getPx(this.indicatorBarWidth)
		},
		// #endif
		//
		scrollEvent(status) {
			this.$emit(status)
		},
		// 获取组件的宽度
		async getComponentWidth() {
			// 延时一定时间,以获取dom尺寸
			await uni.$u.sleep(30)
			// #ifndef APP-NVUE
			this.$uGetRect('.u-scroll-list').then(size => {
				this.scrollWidth = size.width
			})
			// #endif

			// #ifdef APP-NVUE
			const ref = this.$refs['u-scroll-list']
			ref && dom.getComponentRect(ref, (res) => {
				this.scrollWidth = res.size.width
			})
			// #endif
		},
	}
}
</script>

<style lang="scss" scoped>
@import "../../libs/css/components.scss";

.u-scroll-list {
	padding-bottom: 10px;

	&__scroll-view {
		@include flex;

		&__content {
			@include flex;
		}
	}

	&__indicator {
		@include flex;
		justify-content: center;
		margin-top: 15px;

		&__line {
			width: 60px;
			height: 4px;
			border-radius: 100px;
			overflow: hidden;

			&__bar {
				width: 20px;
				height: 4px;
				border-radius: 100px;
			}
		}
	}
}
</style>