guangchao.xu | 070005a | 2020-12-07 09:56:40 +0800 | [diff] [blame] | 1 | <template> |
| 2 | <view |
| 3 | v-if="show" |
| 4 | class="u-notice-bar" |
| 5 | :style="{ |
| 6 | background: computeBgColor, |
| 7 | padding: padding |
| 8 | }" |
| 9 | :class="[ |
| 10 | type ? `u-type-${type}-light-bg` : '' |
| 11 | ]" |
| 12 | > |
| 13 | <view class="u-direction-row"> |
| 14 | <view class="u-icon-wrap"> |
| 15 | <u-icon class="u-left-icon" v-if="volumeIcon" name="volume-fill" :size="volumeSize" :color="computeColor"></u-icon> |
| 16 | </view> |
| 17 | <view class="u-notice-box" id="u-notice-box"> |
| 18 | <view |
| 19 | class="u-notice-content" |
| 20 | id="u-notice-content" |
| 21 | :style="{ |
| 22 | animationDuration: animationDuration, |
| 23 | animationPlayState: animationPlayState, |
| 24 | }" |
| 25 | > |
| 26 | <text class="u-notice-text" @tap="click" :style="[textStyle]" |
| 27 | :class="['u-type-' + type]">{{showText}}</text> |
| 28 | </view> |
| 29 | </view> |
| 30 | <view class="u-icon-wrap"> |
| 31 | <u-icon @click="getMore" class="u-right-icon" v-if="moreIcon" name="arrow-right" :size="26" :color="computeColor"></u-icon> |
| 32 | <u-icon @click="close" class="u-right-icon" v-if="closeIcon" name="close" :size="24" :color="computeColor"></u-icon> |
| 33 | </view> |
| 34 | </view> |
| 35 | </view> |
| 36 | </template> |
| 37 | <script> |
| 38 | export default { |
| 39 | props: { |
| 40 | // 显示的内容,数组 |
| 41 | list: { |
| 42 | type: Array, |
| 43 | default() { |
| 44 | return []; |
| 45 | } |
| 46 | }, |
| 47 | // 显示的主题,success|error|primary|info|warning|none |
| 48 | // none主题默认为透明背景,黑色(contentColor)字体 |
| 49 | type: { |
| 50 | type: String, |
| 51 | default: 'warning' |
| 52 | }, |
| 53 | // 是否显示左侧的音量图标 |
| 54 | volumeIcon: { |
| 55 | type: Boolean, |
| 56 | default: true |
| 57 | }, |
| 58 | // 是否显示右侧的右箭头图标 |
| 59 | moreIcon: { |
| 60 | type: Boolean, |
| 61 | default: false |
| 62 | }, |
| 63 | // 是否显示右侧的关闭图标 |
| 64 | closeIcon: { |
| 65 | type: Boolean, |
| 66 | default: false |
| 67 | }, |
| 68 | // 是否自动播放 |
| 69 | autoplay: { |
| 70 | type: Boolean, |
| 71 | default: true |
| 72 | }, |
| 73 | // 文字颜色,各图标也会使用文字颜色 |
| 74 | color: { |
| 75 | type: String, |
| 76 | default: '' |
| 77 | }, |
| 78 | // 背景颜色 |
| 79 | bgColor: { |
| 80 | type: String, |
| 81 | default: '' |
| 82 | }, |
| 83 | // 是否显示 |
| 84 | show: { |
| 85 | type: Boolean, |
| 86 | default: true |
| 87 | }, |
| 88 | // 字体大小,单位rpx |
| 89 | fontSize: { |
| 90 | type: [Number, String], |
| 91 | default: 26 |
| 92 | }, |
| 93 | // 音量喇叭的大小 |
| 94 | volumeSize: { |
| 95 | type: [Number, String], |
| 96 | default: 34 |
| 97 | }, |
| 98 | // 水平滚动时的滚动速度,即每秒滚动多少rpx,这有利于控制文字无论多少时,都能有一个恒定的速度 |
| 99 | speed: { |
| 100 | type: [Number, String], |
| 101 | default: 160 |
| 102 | }, |
| 103 | // 播放状态,play-播放,paused-暂停 |
| 104 | playState: { |
| 105 | type: String, |
| 106 | default: 'play' |
| 107 | }, |
| 108 | // 通知的边距 |
| 109 | padding: { |
| 110 | type: [Number, String], |
| 111 | default: '18rpx 24rpx' |
| 112 | } |
| 113 | }, |
| 114 | data() { |
| 115 | return { |
| 116 | textWidth: 0, // 滚动的文字宽度 |
| 117 | boxWidth: 0, // 供文字滚动的父盒子的宽度,和前者一起为了计算滚动速度 |
| 118 | animationDuration: '10s', // 动画执行时间 |
| 119 | animationPlayState: 'paused', // 动画的开始和结束执行 |
| 120 | showText: '' // 显示的文本 |
| 121 | }; |
| 122 | }, |
| 123 | watch: { |
| 124 | list: { |
| 125 | immediate: true, |
| 126 | handler(val) { |
| 127 | this.showText = val.join(','); |
| 128 | this.$nextTick(() => { |
| 129 | this.initSize(); |
| 130 | }); |
| 131 | } |
| 132 | }, |
| 133 | playState(val) { |
| 134 | if(val == 'play') this.animationPlayState = 'running'; |
| 135 | else this.animationPlayState = 'paused'; |
| 136 | }, |
| 137 | speed(val) { |
| 138 | this.initSize(); |
| 139 | } |
| 140 | }, |
| 141 | computed: { |
| 142 | // 计算字体颜色,如果没有自定义的,就用uview主题颜色 |
| 143 | computeColor() { |
| 144 | if (this.color) return this.color; |
| 145 | // 如果是无主题,就默认使用content-color |
| 146 | else if(this.type == 'none') return '#606266'; |
| 147 | else return this.type; |
| 148 | }, |
| 149 | // 文字内容的样式 |
| 150 | textStyle() { |
| 151 | let style = {}; |
| 152 | if (this.color) style.color = this.color; |
| 153 | else if(this.type == 'none') style.color = '#606266'; |
| 154 | style.fontSize = this.fontSize + 'rpx'; |
| 155 | return style; |
| 156 | }, |
| 157 | // 计算背景颜色 |
| 158 | computeBgColor() { |
| 159 | if (this.bgColor) return this.bgColor; |
| 160 | else if(this.type == 'none') return 'transparent'; |
| 161 | } |
| 162 | }, |
| 163 | mounted() { |
| 164 | this.$nextTick(() => { |
| 165 | this.initSize(); |
| 166 | }); |
| 167 | }, |
| 168 | methods: { |
| 169 | initSize() { |
| 170 | let query = [], |
| 171 | boxWidth = 0, |
| 172 | textWidth = 0; |
| 173 | let textQuery = new Promise((resolve, reject) => { |
| 174 | uni.createSelectorQuery() |
| 175 | .in(this) |
| 176 | .select(`#u-notice-content`) |
| 177 | .boundingClientRect() |
| 178 | .exec(ret => { |
| 179 | this.textWidth = ret[0].width; |
| 180 | resolve(); |
| 181 | }); |
| 182 | }); |
| 183 | query.push(textQuery); |
| 184 | Promise.all(query).then(() => { |
| 185 | // 根据t=s/v(时间=路程/速度),这里为何不需要加上#u-notice-box的宽度,因为中设置了.u-notice-content样式中设置了padding-left: 100% |
| 186 | // 恰巧计算出来的结果中已经包含了#u-notice-box的宽度 |
| 187 | this.animationDuration = `${this.textWidth / uni.upx2px(this.speed)}s`; |
| 188 | // 这里必须这样开始动画,否则在APP上动画速度不会改变(HX版本2.4.6,IOS13) |
| 189 | this.animationPlayState = 'paused'; |
| 190 | setTimeout(() => { |
| 191 | if(this.playState == 'play' && this.autoplay) this.animationPlayState = 'running'; |
| 192 | }, 10); |
| 193 | }); |
| 194 | }, |
| 195 | // 点击通告栏 |
| 196 | click(index) { |
| 197 | this.$emit('click'); |
| 198 | }, |
| 199 | // 点击关闭按钮 |
| 200 | close() { |
| 201 | this.$emit('close'); |
| 202 | }, |
| 203 | // 点击更多箭头按钮 |
| 204 | getMore() { |
| 205 | this.$emit('getMore'); |
| 206 | } |
| 207 | } |
| 208 | }; |
| 209 | </script> |
| 210 | |
| 211 | <style lang="scss" scoped> |
| 212 | @import "../../libs/css/style.components.scss"; |
| 213 | |
| 214 | .u-notice-bar { |
| 215 | padding: 18rpx 24rpx; |
| 216 | overflow: hidden; |
| 217 | } |
| 218 | |
| 219 | .u-direction-row { |
| 220 | @include vue-flex; |
| 221 | align-items: center; |
| 222 | justify-content: space-between; |
| 223 | } |
| 224 | |
| 225 | .u-left-icon { |
| 226 | /* #ifndef APP-NVUE */ |
| 227 | display: inline-flex; |
| 228 | /* #endif */ |
| 229 | align-items: center; |
| 230 | } |
| 231 | |
| 232 | .u-notice-box { |
| 233 | flex: 1; |
| 234 | @include vue-flex; |
| 235 | overflow: hidden; |
| 236 | margin-left: 12rpx; |
| 237 | } |
| 238 | |
| 239 | .u-right-icon { |
| 240 | margin-left: 12rpx; |
| 241 | display: inline-flex; |
| 242 | align-items: center; |
| 243 | } |
| 244 | |
| 245 | .u-notice-content { |
| 246 | animation: u-loop-animation 10s linear infinite both; |
| 247 | text-align: right; |
| 248 | // 这一句很重要,为了能让滚动左右连接起来 |
| 249 | padding-left: 100%; |
| 250 | @include vue-flex; |
| 251 | flex-wrap: nowrap; |
| 252 | } |
| 253 | |
| 254 | .u-notice-text { |
| 255 | font-size: 26rpx; |
| 256 | word-break: keep-all; |
| 257 | white-space: nowrap |
| 258 | } |
| 259 | |
| 260 | @keyframes u-loop-animation { |
| 261 | 0% { |
| 262 | transform: translate3d(0, 0, 0); |
| 263 | } |
| 264 | |
| 265 | 100% { |
| 266 | transform: translate3d(-100%, 0, 0); |
| 267 | } |
| 268 | } |
| 269 | </style> |