|                                                                                                                                                                                                                                                                                                                                                     |  | <template>	<view		class="uv-notice"		@tap="clickHandler"	>		<slot name="icon">			<view				class="uv-notice__left-icon"				v-if="icon"			>				<uv-icon					:name="icon"					:color="color"					size="19"				></uv-icon>			</view>		</slot>		<view			class="uv-notice__content"			ref="uv-notice__content"		>			<view				ref="uv-notice__content__text"				class="uv-notice__content__text"				:style="[animationStyle]"			>				<text					v-for="(item, index) in innerText"					:key="index"					:style="[textStyle]"				>{{item}}</text>			</view>		</view>		<view			class="uv-notice__right-icon"			v-if="['link', 'closable'].includes(mode)"		>			<uv-icon				v-if="mode === 'link'"				name="arrow-right"				:size="17"				:color="color"			></uv-icon>			<uv-icon				v-if="mode === 'closable'"				@click="close"				name="close"				:size="16"				:color="color"			></uv-icon>		</view>	</view></template><script>	import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js'	import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js'	import props from './props.js';	// #ifdef APP-NVUE
	const animation = uni.requireNativePlugin('animation')	const dom = uni.requireNativePlugin('dom')	// #endif
	/**	 * RowNotice 滚动通知中的水平滚动模式	 * @description 水平滚动	 * @tutorial https://www.uvui.cn/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: 'uv-row-notice',		emits: ['click','close'],		mixins: [mpMixin, 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(!this.$uv.test.string(newValue)) {						this.$uv.error('noticebar组件direction为row时,要求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.fontSize = this.$uv.addUnit(this.fontSize)				return style			},			animationStyle() {				let style = {}				style.animationDuration = this.animationDuration				style.animationPlayState = this.animationPlayState				return style			},			// 内部对用户传入的数据进一步分割,放到多个text标签循环,否则如果用户传入的字符串很长(100个字符以上)
			// 放在一个text标签中进行滚动,在低端安卓机上,动画可能会出现抖动现象,需要分割到多个text中可解决此问题
			innerText() {				let result = [],					// 每组text标签的字符长度
					len = 20				const textArr = this.text? this.text.split(''):[]				for (let i = 0; i < textArr.length; i += len) {					// 对拆分的后的text进行slice分割,得到的为数组再进行join拼接为字符串
					result.push(textArr.slice(i, i + len).join(''))				}				return result			}		},		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(!this.$uv.test.string(this.text)) {					this.$uv.error('noticebar组件direction为row时,要求text参数为字符串形式')				}			},			// vue版处理
			async vue() {				// #ifndef APP-NVUE
				let boxWidth = 0,					textWidth = 0				// 进行一定的延时
				await this.$uv.sleep()				// 查询盒子和文字的宽度
				textWidth = (await this.$uvGetRect('.uv-notice__content__text')).width				boxWidth = (await this.$uvGetRect('.uv-notice__content')).width				// 根据t=s/v(时间=路程/速度),这里为何不需要加上#uv-notice-box的宽度,因为中设置了.uv-notice-content样式中设置了padding-left: 100%
				// 恰巧计算出来的结果中已经包含了#uv-notice-box的宽度
				this.animationDuration = `${textWidth / this.$uv.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 this.$uv.sleep()				// 查询盒子和文字的宽度
				textWidth = (await this.getNvueRect('uv-notice__content__text')).width				boxWidth = (await this.getNvueRect('uv-notice__content')).width				// 将文字移动到盒子的右边沿,之所以需要这么做,是因为nvue不支持100%单位,否则可以通过css设置
				animation.transition(this.$refs['uv-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['uv-notice__content__text'], {					styles: {						// 目标移动终点为-textWidth,也即当文字的最右边贴到盒子的左边框的位置
						transform: `translateX(-${textWidth}px)`					},					// 滚动时间的计算为,时间 = 路程(boxWidth + textWidth) / 速度,最后转为毫秒
					duration: (boxWidth + textWidth) / this.$uv.getPx(this.speed) * 1000,					delay: 10				}, () => {					animation.transition(this.$refs['uv-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
		// #ifdef VUE2
		beforeDestroy() {			this.stopAnimation = true		},		// #endif
		// #ifdef VUE3
		unmounted() {			this.stopAnimation = true		}		// #endif
		// #endif
	};</script>
<style lang="scss" scoped>	@import '@/uni_modules/uv-ui-tools/libs/css/components.scss';	@import '@/uni_modules/uv-ui-tools/libs/css/color.scss';	.uv-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: $uv-warning;				/* #ifndef APP-NVUE */				// 这一句很重要,为了能让滚动左右连接起来
				padding-left: 100%;				word-break: keep-all;				white-space: nowrap;				animation: uv-loop-animation 10s linear infinite both;				/* #endif */				@include flex(row);			}		}
	}	/* #ifndef APP-NVUE */	@keyframes uv-loop-animation {		0% {			transform: translate3d(0, 0, 0);		}
		100% {			transform: translate3d(-100%, 0, 0);		}	}	/* #endif */</style>
 |