commit | author | age
|
e3ba12
|
1 |
<template> |
Z |
2 |
<view |
|
3 |
class="u-notice" |
|
4 |
@tap="clickHandler" |
|
5 |
> |
|
6 |
<slot name="icon"> |
|
7 |
<view |
|
8 |
class="u-notice__left-icon" |
|
9 |
v-if="icon" |
|
10 |
> |
|
11 |
<u-icon |
|
12 |
:name="icon" |
|
13 |
:color="color" |
|
14 |
size="19" |
|
15 |
></u-icon> |
|
16 |
</view> |
|
17 |
</slot> |
|
18 |
<view |
|
19 |
class="u-notice__content" |
|
20 |
ref="u-notice__content" |
|
21 |
> |
|
22 |
<text |
|
23 |
ref="u-notice__content__text" |
|
24 |
class="u-notice__content__text" |
|
25 |
:style="[textStyle]" |
|
26 |
>{{text}}</text> |
|
27 |
</view> |
|
28 |
<view |
|
29 |
class="u-notice__right-icon" |
|
30 |
v-if="['link', 'closable'].includes(mode)" |
|
31 |
> |
|
32 |
<u-icon |
|
33 |
v-if="mode === 'link'" |
|
34 |
name="arrow-right" |
|
35 |
:size="17" |
|
36 |
:color="color" |
|
37 |
></u-icon> |
|
38 |
<u-icon |
|
39 |
v-if="mode === 'closable'" |
|
40 |
@click="close" |
|
41 |
name="close" |
|
42 |
:size="16" |
|
43 |
:color="color" |
|
44 |
></u-icon> |
|
45 |
</view> |
|
46 |
</view> |
|
47 |
</template> |
|
48 |
<script> |
|
49 |
import props from './props.js'; |
|
50 |
// #ifdef APP-NVUE |
|
51 |
const animation = uni.requireNativePlugin('animation') |
|
52 |
const dom = uni.requireNativePlugin('dom') |
|
53 |
// #endif |
|
54 |
/** |
|
55 |
* RowNotice 滚动通知中的水平滚动模式 |
|
56 |
* @description 水平滚动 |
|
57 |
* @tutorial https://www.uviewui.com/components/noticeBar.html |
|
58 |
* @property {String | Number} text 显示的内容,字符串 |
|
59 |
* @property {String} icon 是否显示左侧的音量图标 (默认 'volume' ) |
|
60 |
* @property {String} mode 通告模式,link-显示右箭头,closable-显示右侧关闭图标 |
|
61 |
* @property {String} color 文字颜色,各图标也会使用文字颜色 (默认 '#f9ae3d' ) |
|
62 |
* @property {String} bgColor 背景颜色 (默认 ''#fdf6ec' ) |
|
63 |
* @property {String | Number} fontSize 字体大小,单位px (默认 14 ) |
|
64 |
* @property {String | Number} speed 水平滚动时的滚动速度,即每秒滚动多少px(rpx),这有利于控制文字无论多少时,都能有一个恒定的速度 (默认 80 ) |
|
65 |
* |
|
66 |
* @event {Function} click 点击通告文字触发 |
|
67 |
* @event {Function} close 点击右侧关闭图标触发 |
|
68 |
* @example |
|
69 |
*/ |
|
70 |
export default { |
|
71 |
name: 'u-row-notice', |
|
72 |
mixins: [uni.$u.mpMixin, uni.$u.mixin,props], |
|
73 |
data() { |
|
74 |
return { |
|
75 |
animationDuration: '0', // 动画执行时间 |
|
76 |
animationPlayState: 'paused', // 动画的开始和结束执行 |
|
77 |
// nvue下,内容发生变化,导致滚动宽度也变化,需要标志为是否需要重新计算宽度 |
|
78 |
// 不能在内容变化时直接重新计算,因为nvue的animation模块上一次的滚动不是刚好结束,会有影响 |
|
79 |
nvueInit: true, |
|
80 |
show: true |
|
81 |
}; |
|
82 |
}, |
|
83 |
watch: { |
|
84 |
text: { |
|
85 |
immediate: true, |
|
86 |
handler(newValue, oldValue) { |
|
87 |
// #ifdef APP-NVUE |
|
88 |
this.nvueInit = true |
|
89 |
// #endif |
|
90 |
// #ifndef APP-NVUE |
|
91 |
this.vue() |
|
92 |
// #endif |
|
93 |
|
|
94 |
if(!uni.$u.test.string(newValue)) { |
|
95 |
uni.$u.error('noticebar组件direction为row时,要求text参数为字符串形式') |
|
96 |
} |
|
97 |
} |
|
98 |
}, |
|
99 |
fontSize() { |
|
100 |
// #ifdef APP-NVUE |
|
101 |
this.nvueInit = true |
|
102 |
// #endif |
|
103 |
// #ifndef APP-NVUE |
|
104 |
this.vue() |
|
105 |
// #endif |
|
106 |
}, |
|
107 |
speed() { |
|
108 |
// #ifdef APP-NVUE |
|
109 |
this.nvueInit = true |
|
110 |
// #endif |
|
111 |
// #ifndef APP-NVUE |
|
112 |
this.vue() |
|
113 |
// #endif |
|
114 |
} |
|
115 |
}, |
|
116 |
computed: { |
|
117 |
// 文字内容的样式 |
|
118 |
textStyle() { |
|
119 |
let style = {} |
|
120 |
style.color = this.color |
|
121 |
style.animationDuration = this.animationDuration |
|
122 |
style.animationPlayState = this.animationPlayState |
|
123 |
style.fontSize = uni.$u.addUnit(this.fontSize) |
|
124 |
return style |
|
125 |
}, |
|
126 |
}, |
|
127 |
mounted() { |
|
128 |
// #ifdef APP-PLUS |
|
129 |
// 在APP上(含nvue),监听当前webview是否处于隐藏状态(进入下一页时即为hide状态) |
|
130 |
// 如果webivew隐藏了,为了节省性能的损耗,应停止动画的执行,同时也是为了保持进入下一页返回后,滚动位置保持不变 |
|
131 |
var pages = getCurrentPages() |
|
132 |
var page = pages[pages.length - 1] |
|
133 |
var currentWebview = page.$getAppWebview() |
|
134 |
currentWebview.addEventListener('hide', () => { |
|
135 |
this.webviewHide = true |
|
136 |
}) |
|
137 |
currentWebview.addEventListener('show', () => { |
|
138 |
this.webviewHide = false |
|
139 |
}) |
|
140 |
// #endif |
|
141 |
|
|
142 |
this.init() |
|
143 |
}, |
|
144 |
methods: { |
|
145 |
init() { |
|
146 |
// #ifdef APP-NVUE |
|
147 |
this.nvue() |
|
148 |
// #endif |
|
149 |
|
|
150 |
// #ifndef APP-NVUE |
|
151 |
this.vue() |
|
152 |
// #endif |
|
153 |
|
|
154 |
if(!uni.$u.test.string(this.text)) { |
|
155 |
uni.$u.error('noticebar组件direction为row时,要求text参数为字符串形式') |
|
156 |
} |
|
157 |
}, |
|
158 |
// vue版处理 |
|
159 |
async vue() { |
|
160 |
// #ifndef APP-NVUE |
|
161 |
let boxWidth = 0, |
|
162 |
textWidth = 0 |
|
163 |
// 进行一定的延时 |
|
164 |
await uni.$u.sleep() |
|
165 |
// 查询盒子和文字的宽度 |
|
166 |
textWidth = (await this.$uGetRect('.u-notice__content__text')).width |
|
167 |
boxWidth = (await this.$uGetRect('.u-notice__content')).width |
|
168 |
// 根据t=s/v(时间=路程/速度),这里为何不需要加上#u-notice-box的宽度,因为中设置了.u-notice-content样式中设置了padding-left: 100% |
|
169 |
// 恰巧计算出来的结果中已经包含了#u-notice-box的宽度 |
|
170 |
this.animationDuration = `${textWidth / uni.$u.getPx(this.speed)}s` |
|
171 |
// 这里必须这样开始动画,否则在APP上动画速度不会改变 |
|
172 |
this.animationPlayState = 'paused' |
|
173 |
setTimeout(() => { |
|
174 |
this.animationPlayState = 'running' |
|
175 |
}, 10) |
|
176 |
// #endif |
|
177 |
}, |
|
178 |
// nvue版处理 |
|
179 |
async nvue() { |
|
180 |
// #ifdef APP-NVUE |
|
181 |
this.nvueInit = false |
|
182 |
let boxWidth = 0, |
|
183 |
textWidth = 0 |
|
184 |
// 进行一定的延时 |
|
185 |
await uni.$u.sleep() |
|
186 |
// 查询盒子和文字的宽度 |
|
187 |
textWidth = (await this.getNvueRect('u-notice__content__text')).width |
|
188 |
boxWidth = (await this.getNvueRect('u-notice__content')).width |
|
189 |
// 将文字移动到盒子的右边沿,之所以需要这么做,是因为nvue不支持100%单位,否则可以通过css设置 |
|
190 |
animation.transition(this.$refs['u-notice__content__text'], { |
|
191 |
styles: { |
|
192 |
transform: `translateX(${boxWidth}px)` |
|
193 |
}, |
|
194 |
}, () => { |
|
195 |
// 如果非禁止动画,则开始滚动 |
|
196 |
!this.stopAnimation && this.loopAnimation(textWidth, boxWidth) |
|
197 |
}); |
|
198 |
// #endif |
|
199 |
}, |
|
200 |
loopAnimation(textWidth, boxWidth) { |
|
201 |
// #ifdef APP-NVUE |
|
202 |
animation.transition(this.$refs['u-notice__content__text'], { |
|
203 |
styles: { |
|
204 |
// 目标移动终点为-textWidth,也即当文字的最右边贴到盒子的左边框的位置 |
|
205 |
transform: `translateX(-${textWidth}px)` |
|
206 |
}, |
|
207 |
// 滚动时间的计算为,时间 = 路程(boxWidth + textWidth) / 速度,最后转为毫秒 |
|
208 |
duration: (boxWidth + textWidth) / uni.$u.getPx(this.speed) * 1000, |
|
209 |
delay: 10 |
|
210 |
}, () => { |
|
211 |
animation.transition(this.$refs['u-notice__content__text'], { |
|
212 |
styles: { |
|
213 |
// 重新将文字移动到盒子的右边沿 |
|
214 |
transform: `translateX(${this.stopAnimation ? 0 : boxWidth}px)` |
|
215 |
}, |
|
216 |
}, () => { |
|
217 |
// 如果非禁止动画,则继续下一轮滚动 |
|
218 |
if (!this.stopAnimation) { |
|
219 |
// 判断是否需要初始化计算尺寸 |
|
220 |
if (this.nvueInit) { |
|
221 |
this.nvue() |
|
222 |
} else { |
|
223 |
this.loopAnimation(textWidth, boxWidth) |
|
224 |
} |
|
225 |
} |
|
226 |
}); |
|
227 |
}) |
|
228 |
// #endif |
|
229 |
}, |
|
230 |
getNvueRect(el) { |
|
231 |
// #ifdef APP-NVUE |
|
232 |
// 返回一个promise |
|
233 |
return new Promise(resolve => { |
|
234 |
dom.getComponentRect(this.$refs[el], (res) => { |
|
235 |
resolve(res.size) |
|
236 |
}) |
|
237 |
}) |
|
238 |
// #endif |
|
239 |
}, |
|
240 |
// 点击通告栏 |
|
241 |
clickHandler(index) { |
|
242 |
this.$emit('click') |
|
243 |
}, |
|
244 |
// 点击右侧按钮,需要判断点击的是关闭图标还是箭头图标 |
|
245 |
close() { |
|
246 |
this.$emit('close') |
|
247 |
} |
|
248 |
}, |
|
249 |
// #ifdef APP-NVUE |
|
250 |
beforeDestroy() { |
|
251 |
this.stopAnimation = true |
|
252 |
}, |
|
253 |
// #endif |
|
254 |
}; |
|
255 |
</script> |
|
256 |
|
|
257 |
<style lang="scss" scoped> |
|
258 |
@import "../../libs/css/components.scss"; |
|
259 |
|
|
260 |
.u-notice { |
|
261 |
@include flex; |
|
262 |
align-items: center; |
|
263 |
justify-content: space-between; |
|
264 |
|
|
265 |
&__left-icon { |
|
266 |
align-items: center; |
|
267 |
margin-right: 5px; |
|
268 |
} |
|
269 |
|
|
270 |
&__right-icon { |
|
271 |
margin-left: 5px; |
|
272 |
align-items: center; |
|
273 |
} |
|
274 |
|
|
275 |
&__content { |
|
276 |
text-align: right; |
|
277 |
flex: 1; |
|
278 |
@include flex; |
|
279 |
flex-wrap: nowrap; |
|
280 |
overflow: hidden; |
|
281 |
|
|
282 |
&__text { |
|
283 |
font-size: 14px; |
|
284 |
color: $u-warning; |
|
285 |
/* #ifndef APP-NVUE */ |
|
286 |
// 这一句很重要,为了能让滚动左右连接起来 |
|
287 |
padding-left: 100%; |
|
288 |
word-break: keep-all; |
|
289 |
white-space: nowrap; |
|
290 |
animation: u-loop-animation 10s linear infinite both; |
|
291 |
/* #endif */ |
|
292 |
} |
|
293 |
} |
|
294 |
|
|
295 |
} |
|
296 |
|
|
297 |
@keyframes u-loop-animation { |
|
298 |
0% { |
|
299 |
transform: translate3d(0, 0, 0); |
|
300 |
} |
|
301 |
|
|
302 |
100% { |
|
303 |
transform: translate3d(-100%, 0, 0); |
|
304 |
} |
|
305 |
} |
|
306 |
</style> |