Skip to content

noticebar.vue

新闻向左滚动

jsx
<template>
	<view v-if="show" class="u-notice-bar" :style="{
			background: computeBgColor,
			padding: padding
		}" :class="[
			type ? `u-type-${type}-light-bg` : ''
		]">
		<view class="u-direction-row">
			<view class="u-icon-wrap">
				<u-icon class="u-left-icon" v-if="volumeIcon" name="volume-fill" :size="volumeSize"
					:color="computeColor"></u-icon>
			</view>
			<view class="u-notice-box" id="u-notice-box">
				<view class="u-notice-content" id="u-notice-content" :style="{
						animationDuration: animationDuration,
						animationPlayState: animationPlayState,
					}">
					<text class="u-notice-text" @tap="click" :style="[textStyle]"
						:class="['u-type-' + type]">{{showText}}</text>
				</view>
			</view>
			<view class="u-icon-wrap">
				<u-icon @click="getMore" class="u-right-icon" v-if="moreIcon" name="arrow-right" :size="26"
					:color="computeColor"></u-icon>
				<u-icon @click="close" class="u-right-icon" v-if="closeIcon" name="close" :size="24"
					:color="computeColor"></u-icon>
			</view>
		</view>
	</view>
</template>
<script>
	export default {
		emits: ["close", "getMore"],
		props: {
			// 显示的内容,数组
			list: {
				type: Array,
				default () {
					return [];
				}
			},
			// 显示的主题,success|error|primary|info|warning|none
			// none主题默认为透明背景,黑色(contentColor)字体
			type: {
				type: String,
				default: 'warning'
			},
			// 是否显示左侧的音量图标
			volumeIcon: {
				type: Boolean,
				default: true
			},
			// 是否显示右侧的右箭头图标
			moreIcon: {
				type: Boolean,
				default: false
			},
			// 是否显示右侧的关闭图标
			closeIcon: {
				type: Boolean,
				default: false
			},
			// 是否自动播放
			autoplay: {
				type: Boolean,
				default: true
			},
			// 文字颜色,各图标也会使用文字颜色
			color: {
				type: String,
				default: ''
			},
			// 背景颜色
			bgColor: {
				type: String,
				default: ''
			},
			// 是否显示
			show: {
				type: Boolean,
				default: true
			},
			// 字体大小,单位rpx
			fontSize: {
				type: [Number, String],
				default: 26
			},
			// 音量喇叭的大小
			volumeSize: {
				type: [Number, String],
				default: 34
			},
			// 水平滚动时的滚动速度,即每秒滚动多少rpx,这有利于控制文字无论多少时,都能有一个恒定的速度
			speed: {
				type: [Number, String],
				default: 160
			},
			// 播放状态,play-播放,paused-暂停
			playState: {
				type: String,
				default: 'play'
			},
			// 通知的边距
			padding: {
				type: [Number, String],
				default: '18rpx 12rpx'
			}
		},
		data() {
			return {
				textWidth: 0, // 滚动的文字宽度
				boxWidth: 0, // 供文字滚动的父盒子的宽度,和前者一起为了计算滚动速度
				animationDuration: '10s', // 动画执行时间
				animationPlayState: 'paused', // 动画的开始和结束执行
				showText: '' // 显示的文本
			};
		},
		watch: {
			list: {
				immediate: true,
				handler(val) {
					this.showText = val.join('');
					this.$nextTick(() => {
						this.initSize();
					});
				}
			},
			playState(val) {
				if (val == 'play') this.animationPlayState = 'running';
				else this.animationPlayState = 'paused';
			},
			speed(val) {
				this.initSize();
			}
		},
		computed: {
			// 计算字体颜色,如果没有自定义的,就用uview主题颜色
			computeColor() {
				if (this.color) return this.color;
				// 如果是无主题,就默认使用content-color
				else if (this.type == 'none') return '#606266';
				else return this.type;
			},
			// 文字内容的样式
			textStyle() {
				let style = {};
				if (this.color) style.color = this.color;
				else if (this.type == 'none') style.color = '#606266';
				style.fontSize = this.fontSize + 'rpx';
				return style;
			},
			// 计算背景颜色
			computeBgColor() {
				if (this.bgColor) return this.bgColor;
				else if (this.type == 'none') return 'transparent';
			}
		},
		mounted() {
			this.$nextTick(() => {
				this.initSize();
			});
		},
		methods: {
			initSize() {
				let query = [],
					boxWidth = 0,
					textWidth = 0;
				let textQuery = new Promise((resolve, reject) => {
					uni.createSelectorQuery()
						.in(this)
						.select(`#u-notice-content`)
						.boundingClientRect()
						.exec(ret => {
							this.textWidth = ret[0].width;
							resolve();
						});
				});
				query.push(textQuery);
				Promise.all(query).then(() => {
					// 根据t=s/v(时间=路程/速度),这里为何不需要加上#u-notice-box的宽度,因为中设置了.u-notice-content样式中设置了padding-left: 100%
					// 恰巧计算出来的结果中已经包含了#u-notice-box的宽度
					this.animationDuration = `${this.textWidth / uni.upx2px(this.speed)}s`;
					// 这里必须这样开始动画,否则在APP上动画速度不会改变(HX版本2.4.6,IOS13)
					this.animationPlayState = 'paused';
					setTimeout(() => {
						if (this.playState == 'play' && this.autoplay) this.animationPlayState = 'running';
					}, 10);
				});
			},
			// 点击通告栏
			click(index) {
				this.$emit('click');
			},
			// 点击关闭按钮
			close() {
				this.$emit('close');
			},
			// 点击更多箭头按钮
			getMore() {
				this.$emit('getMore');
			}
		}
	};
</script>

<style lang="scss" scoped>
	@mixin vue-flex($direction: row) {
		/* #ifndef APP-NVUE */
		display: flex;
		flex-direction: $direction;
		/* #endif */
	}

	.u-notice-bar {
		padding: 18rpx 18rpx;
		overflow: hidden;
	}

	.u-direction-row {
		@include vue-flex;
		align-items: center;
		justify-content: space-between;
	}

	.u-left-icon {
		/* #ifndef APP-NVUE */
		display: inline-flex;
		/* #endif */
		align-items: center;
	}

	.u-notice-box {
		flex: 1;
		@include vue-flex;
		overflow: hidden;
		margin-left: 12rpx;
	}

	.u-right-icon {
		margin-left: 12rpx;
		display: inline-flex;
		align-items: center;
	}

	.u-notice-content {
		animation: u-loop-animation 10s linear infinite both;
		text-align: right;
		// 这一句很重要,为了能让滚动左右连接起来
		padding-left: 20%;
		@include vue-flex;
		flex-wrap: nowrap;
	}

	.u-notice-text {
		font-size: 26rpx;
		word-break: keep-all;
		white-space: nowrap
	}

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

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