审查视图

node_modules/uview-ui/components/u-row-notice/u-row-notice.vue 8.2 KB
韩昌 authored
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 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306
<template>
	<view
		class="u-notice"
		@tap="clickHandler"
	>
		<slot name="icon">
			<view
				class="u-notice__left-icon"
				v-if="icon"
			>
				<u-icon
					:name="icon"
					:color="color"
					size="19"
				></u-icon>
			</view>
		</slot>
		<view
			class="u-notice__content"
			ref="u-notice__content"
		>
			<text
				ref="u-notice__content__text"
				class="u-notice__content__text"
				:style="[textStyle]"
			>{{text}}</text>
		</view>
		<view
			class="u-notice__right-icon"
			v-if="['link', 'closable'].includes(mode)"
		>
			<u-icon
				v-if="mode === 'link'"
				name="arrow-right"
				:size="17"
				:color="color"
			></u-icon>
			<u-icon
				v-if="mode === 'closable'"
				@click="close"
				name="close"
				:size="16"
				:color="color"
			></u-icon>
		</view>
	</view>
</template>
<script>
	import props from './props.js';
	// #ifdef APP-NVUE
	const animation = uni.requireNativePlugin('animation')
	const dom = uni.requireNativePlugin('dom')
	// #endif
	/**
	 * RowNotice 滚动通知中的水平滚动模式
	 * @description 水平滚动
	 * @tutorial https://www.uviewui.com/components/noticeBar.html
	 * @property {String | Number}	text			显示的内容,字符串
	 * @property {String}			icon			是否显示左侧的音量图标 (默认 'volume' )
	 * @property {String}			mode			通告模式,link-显示右箭头,closable-显示右侧关闭图标
	 * @property {String}			color			文字颜色,各图标也会使用文字颜色 (默认 '#f9ae3d' )
	 * @property {String}			bgColor			背景颜色 (默认 ''#fdf6ec' )
	 * @property {String | Number}	fontSize		字体大小,单位px (默认 14 )
	 * @property {String | Number}	speed			水平滚动时的滚动速度,即每秒滚动多少px(rpx),这有利于控制文字无论多少时,都能有一个恒定的速度  (默认 80 )
	 * 
	 * @event {Function} click 点击通告文字触发
	 * @event {Function} close 点击右侧关闭图标触发
	 * @example 
	 */
	export default {
		name: 'u-row-notice',
		mixins: [uni.$u.mpMixin, uni.$u.mixin,props],
		data() {
			return {
				animationDuration: '0', // 动画执行时间
				animationPlayState: 'paused', // 动画的开始和结束执行
				// nvue下,内容发生变化,导致滚动宽度也变化,需要标志为是否需要重新计算宽度
				// 不能在内容变化时直接重新计算,因为nvue的animation模块上一次的滚动不是刚好结束,会有影响
				nvueInit: true,
				show: true
			};
		},
		watch: {
			text: {
				immediate: true,
				handler(newValue, oldValue) {
					// #ifdef APP-NVUE
					this.nvueInit = true
					// #endif
					// #ifndef APP-NVUE
					this.vue()
					// #endif
					
					if(!uni.$u.test.string(newValue)) {
						uni.$u.error('noticebar组件directionrow时,要求text参数为字符串形式')
					}
				}
			},
			fontSize() {
				// #ifdef APP-NVUE
				this.nvueInit = true
				// #endif
				// #ifndef APP-NVUE
				this.vue()
				// #endif
			},
			speed() {
				// #ifdef APP-NVUE
				this.nvueInit = true
				// #endif
				// #ifndef APP-NVUE
				this.vue()
				// #endif
			}
		},
		computed: {
			// 文字内容的样式
			textStyle() {
				let style = {}
				style.color = this.color
				style.animationDuration = this.animationDuration
				style.animationPlayState = this.animationPlayState
				style.fontSize = uni.$u.addUnit(this.fontSize)
				return style
			},
		},
		mounted() {
			// #ifdef APP-PLUS
			// 在APP上(含nvue),监听当前webview是否处于隐藏状态(进入下一页时即为hide状态)
			// 如果webivew隐藏了,为了节省性能的损耗,应停止动画的执行,同时也是为了保持进入下一页返回后,滚动位置保持不变
			var pages = getCurrentPages()
			var page = pages[pages.length - 1]
			var currentWebview = page.$getAppWebview()
			currentWebview.addEventListener('hide', () => {
				this.webviewHide = true
			})
			currentWebview.addEventListener('show', () => {
				this.webviewHide = false
			})
			// #endif

			this.init()
		},
		methods: {
			init() {
				// #ifdef APP-NVUE
				this.nvue()
				// #endif

				// #ifndef APP-NVUE
				this.vue()
				// #endif
				
				if(!uni.$u.test.string(this.text)) {
					uni.$u.error('noticebar组件directionrow时,要求text参数为字符串形式')
				}
			},
			// vue版处理
			async vue() {
				// #ifndef APP-NVUE
				let boxWidth = 0,
					textWidth = 0
				// 进行一定的延时
				await uni.$u.sleep()
				// 查询盒子和文字的宽度
				textWidth = (await this.$uGetRect('.u-notice__content__text')).width
				boxWidth = (await this.$uGetRect('.u-notice__content')).width
				// 根据t=s/v(时间=路程/速度),这里为何不需要加上#u-notice-box的宽度,因为中设置了.u-notice-content样式中设置了padding-left: 100%
				// 恰巧计算出来的结果中已经包含了#u-notice-box的宽度
				this.animationDuration = `${textWidth / uni.$u.getPx(this.speed)}s`
				// 这里必须这样开始动画,否则在APP上动画速度不会改变
				this.animationPlayState = 'paused'
				setTimeout(() => {
					this.animationPlayState = 'running'
				}, 10)
				// #endif
			},
			// nvue版处理
			async nvue() {
				// #ifdef APP-NVUE
				this.nvueInit = false
				let boxWidth = 0,
					textWidth = 0
				// 进行一定的延时
				await uni.$u.sleep()
				// 查询盒子和文字的宽度
				textWidth = (await this.getNvueRect('u-notice__content__text')).width
				boxWidth = (await this.getNvueRect('u-notice__content')).width
				// 将文字移动到盒子的右边沿,之所以需要这么做,是因为nvue不支持100%单位,否则可以通过css设置
				animation.transition(this.$refs['u-notice__content__text'], {
					styles: {
						transform: `translateX(${boxWidth}px)`
					},
				}, () => {
					// 如果非禁止动画,则开始滚动
					!this.stopAnimation && this.loopAnimation(textWidth, boxWidth)
				});
				// #endif
			},
			loopAnimation(textWidth, boxWidth) {
				// #ifdef APP-NVUE
				animation.transition(this.$refs['u-notice__content__text'], {
					styles: {
						// 目标移动终点为-textWidth,也即当文字的最右边贴到盒子的左边框的位置
						transform: `translateX(-${textWidth}px)`
					},
					// 滚动时间的计算为,时间 = 路程(boxWidth + textWidth) / 速度,最后转为毫秒
					duration: (boxWidth + textWidth) / uni.$u.getPx(this.speed) * 1000,
					delay: 10
				}, () => {
					animation.transition(this.$refs['u-notice__content__text'], {
						styles: {
							// 重新将文字移动到盒子的右边沿
							transform: `translateX(${this.stopAnimation ? 0 : boxWidth}px)`
						},
					}, () => {
						// 如果非禁止动画,则继续下一轮滚动
						if (!this.stopAnimation) {
							// 判断是否需要初始化计算尺寸
							if (this.nvueInit) {
								this.nvue()
							} else {
								this.loopAnimation(textWidth, boxWidth)
							}
						}
					});
				})
				// #endif
			},
			getNvueRect(el) {
				// #ifdef APP-NVUE
				// 返回一个promise
				return new Promise(resolve => {
					dom.getComponentRect(this.$refs[el], (res) => {
						resolve(res.size)
					})
				})
				// #endif
			},
			// 点击通告栏
			clickHandler(index) {
				this.$emit('click')
			},
			// 点击右侧按钮,需要判断点击的是关闭图标还是箭头图标
			close() {
				this.$emit('close')
			}
		},
		// #ifdef APP-NVUE
		beforeDestroy() {
			this.stopAnimation = true
		},
		// #endif
	};
</script>

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

	.u-notice {
		@include flex;
		align-items: center;
		justify-content: space-between;

		&__left-icon {
			align-items: center;
			margin-right: 5px;
		}

		&__right-icon {
			margin-left: 5px;
			align-items: center;
		}

		&__content {
			text-align: right;
			flex: 1;
			@include flex;
			flex-wrap: nowrap;
			overflow: hidden;

			&__text {
				font-size: 14px;
				color: $u-warning;
				/* #ifndef APP-NVUE */
				// 这一句很重要,为了能让滚动左右连接起来
				padding-left: 100%;
				word-break: keep-all;
				white-space: nowrap;
				animation: u-loop-animation 10s linear infinite both;
				/* #endif */
			}
		}

	}

	@keyframes u-loop-animation {
		0% {
			transform: translate3d(0, 0, 0);
		}

		100% {
			transform: translate3d(-100%, 0, 0);
		}
	}
</style>