su-notice-bar.vue 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472
  1. <template>
  2. <view
  3. v-if="show"
  4. class="uni-noticebar"
  5. :style="{ backgroundColor: backgroundColor }"
  6. @click="onClick"
  7. >
  8. <slot name="icon">
  9. <uni-icons
  10. v-if="showIcon === true || showIcon === 'true'"
  11. class="uni-noticebar-icon"
  12. type="sound"
  13. :color="color"
  14. size="22"
  15. />
  16. </slot>
  17. <view
  18. ref="textBox"
  19. class="uni-noticebar__content-wrapper"
  20. :class="{
  21. 'uni-noticebar__content-wrapper--scrollable': scrollable,
  22. 'uni-noticebar__content-wrapper--single': !scrollable && (single || moreText),
  23. }"
  24. >
  25. <view
  26. :id="elIdBox"
  27. class="uni-noticebar__content"
  28. :class="{
  29. 'uni-noticebar__content--scrollable': scrollable,
  30. 'uni-noticebar__content--single': !scrollable && (single || moreText),
  31. }"
  32. >
  33. <text
  34. :id="elId"
  35. ref="animationEle"
  36. class="uni-noticebar__content-text"
  37. :class="{
  38. 'uni-noticebar__content-text--scrollable': scrollable,
  39. 'uni-noticebar__content-text--single': !scrollable && (single || showGetMore),
  40. }"
  41. :style="{
  42. color: color,
  43. width: wrapWidth + 'px',
  44. animationDuration: animationDuration,
  45. '-webkit-animationDuration': animationDuration,
  46. animationPlayState: webviewHide ? 'paused' : animationPlayState,
  47. '-webkit-animationPlayState': webviewHide ? 'paused' : animationPlayState,
  48. animationDelay: animationDelay,
  49. '-webkit-animationDelay': animationDelay,
  50. }"
  51. >
  52. {{ text }}
  53. </text>
  54. </view>
  55. </view>
  56. <view
  57. v-if="showGetMore === true || showGetMore === 'true'"
  58. class="uni-noticebar__more uni-cursor-point"
  59. @click="clickMore"
  60. >
  61. <text
  62. v-if="moreText.length > 0"
  63. :style="{ color: moreColor }"
  64. class="uni-noticebar__more-text"
  65. >
  66. {{ moreText }}
  67. </text>
  68. <uni-icons v-else type="right" :color="moreColor" size="16" />
  69. </view>
  70. <view
  71. class="uni-noticebar-close uni-cursor-point"
  72. v-if="
  73. (showClose === true || showClose === 'true') &&
  74. (showGetMore === false || showGetMore === 'false')
  75. "
  76. >
  77. <view @click="close">
  78. <slot name="close">
  79. <uni-icons type="closeempty" :color="color" size="16" />
  80. </slot>
  81. </view>
  82. </view>
  83. </view>
  84. </template>
  85. <script>
  86. import sheep from '@/sheep';
  87. // #ifdef APP-NVUE
  88. const dom = weex.requireModule('dom');
  89. const animation = weex.requireModule('animation');
  90. // #endif
  91. /**
  92. * NoticeBar 自定义导航栏
  93. * @description 通告栏组件
  94. * @tutorial https://ext.dcloud.net.cn/plugin?id=30
  95. * @property {Number} speed 文字滚动的速度,默认100px/秒
  96. * @property {String} text 显示文字
  97. * @property {String} backgroundColor 背景颜色
  98. * @property {String} color 文字颜色
  99. * @property {String} moreColor 查看更多文字的颜色
  100. * @property {String} moreText 设置“查看更多”的文本
  101. * @property {Boolean} single = [true|false] 是否单行
  102. * @property {Boolean} scrollable = [true|false] 是否滚动,为true时,NoticeBar为单行
  103. * @property {Boolean} showIcon = [true|false] 是否显示左侧喇叭图标
  104. * @property {Boolean} showClose = [true|false] 是否显示左侧关闭按钮
  105. * @property {Boolean} showGetMore = [true|false] 是否显示右侧查看更多图标,为true时,NoticeBar为单行
  106. * @event {Function} click 点击 NoticeBar 触发事件
  107. * @event {Function} close 关闭 NoticeBar 触发事件
  108. * @event {Function} getmore 点击”查看更多“时触发事件
  109. */
  110. export default {
  111. name: 'UniNoticeBar',
  112. emits: ['click', 'getmore', 'close'],
  113. props: {
  114. text: {
  115. type: String,
  116. default: '',
  117. },
  118. moreText: {
  119. type: String,
  120. default: '',
  121. },
  122. backgroundColor: {
  123. type: String,
  124. default: '',
  125. },
  126. speed: {
  127. // 默认1s滚动100px
  128. type: Number,
  129. default: 100,
  130. },
  131. color: {
  132. type: String,
  133. default: 'var(--ui-BG-Main)',
  134. },
  135. moreColor: {
  136. type: String,
  137. default: '#FF9A43',
  138. },
  139. single: {
  140. // 是否单行
  141. type: [Boolean, String],
  142. default: false,
  143. },
  144. scrollable: {
  145. // 是否滚动,添加后控制单行效果取消
  146. type: [Boolean, String],
  147. default: false,
  148. },
  149. showIcon: {
  150. // 是否显示左侧icon
  151. type: [Boolean, String],
  152. default: false,
  153. },
  154. showGetMore: {
  155. // 是否显示右侧查看更多
  156. type: [Boolean, String],
  157. default: false,
  158. },
  159. showClose: {
  160. // 是否显示左侧关闭按钮
  161. type: [Boolean, String],
  162. default: false,
  163. },
  164. },
  165. data() {
  166. const elId = `Uni_${Math.ceil(Math.random() * 10e5).toString(36)}`;
  167. const elIdBox = `Uni_${Math.ceil(Math.random() * 10e5).toString(36)}`;
  168. return {
  169. textWidth: 0,
  170. boxWidth: 0,
  171. wrapWidth: '',
  172. webviewHide: false,
  173. // #ifdef APP-NVUE
  174. stopAnimation: false,
  175. // #endif
  176. elId: elId,
  177. elIdBox: elIdBox,
  178. show: true,
  179. animationDuration: 'none',
  180. animationPlayState: 'paused',
  181. animationDelay: '0s',
  182. };
  183. },
  184. mounted() {
  185. // #ifdef APP-PLUS
  186. var pages = getCurrentPages();
  187. var page = pages[pages.length - 1];
  188. var currentWebview = page.$getAppWebview();
  189. currentWebview.addEventListener('hide', () => {
  190. this.webviewHide = true;
  191. });
  192. currentWebview.addEventListener('show', () => {
  193. this.webviewHide = false;
  194. });
  195. // #endif
  196. this.$nextTick(() => {
  197. this.initSize();
  198. });
  199. },
  200. // #ifdef APP-NVUE
  201. beforeDestroy() {
  202. this.stopAnimation = true;
  203. },
  204. // #endif
  205. methods: {
  206. initSize() {
  207. if (this.scrollable) {
  208. // #ifndef APP-NVUE
  209. let query = [],
  210. boxWidth = 0,
  211. textWidth = 0;
  212. let textQuery = new Promise((resolve, reject) => {
  213. uni.createSelectorQuery()
  214. // #ifndef MP-ALIPAY
  215. .in(this)
  216. // #endif
  217. .select(`#${this.elId}`)
  218. .boundingClientRect()
  219. .exec((ret) => {
  220. this.textWidth = ret[0].width;
  221. resolve();
  222. });
  223. });
  224. let boxQuery = new Promise((resolve, reject) => {
  225. uni.createSelectorQuery()
  226. // #ifndef MP-ALIPAY
  227. .in(this)
  228. // #endif
  229. .select(`#${this.elIdBox}`)
  230. .boundingClientRect()
  231. .exec((ret) => {
  232. this.boxWidth = ret[0].width;
  233. resolve();
  234. });
  235. });
  236. query.push(textQuery);
  237. query.push(boxQuery);
  238. Promise.all(query).then(() => {
  239. this.animationDuration = `${this.textWidth / this.speed}s`;
  240. this.animationDelay = `-${this.boxWidth / this.speed}s`;
  241. setTimeout(() => {
  242. this.animationPlayState = 'running';
  243. }, 1000);
  244. });
  245. // #endif
  246. // #ifdef APP-NVUE
  247. dom.getComponentRect(this.$refs['animationEle'], (res) => {
  248. let winWidth = sheep.$platform.device.windowWidth;
  249. this.textWidth = res.size.width;
  250. animation.transition(
  251. this.$refs['animationEle'],
  252. {
  253. styles: {
  254. transform: `translateX(-${winWidth}px)`,
  255. },
  256. duration: 0,
  257. timingFunction: 'linear',
  258. delay: 0,
  259. },
  260. () => {
  261. if (!this.stopAnimation) {
  262. animation.transition(
  263. this.$refs['animationEle'],
  264. {
  265. styles: {
  266. transform: `translateX(-${this.textWidth}px)`,
  267. },
  268. timingFunction: 'linear',
  269. duration: ((this.textWidth - winWidth) / this.speed) * 1000,
  270. delay: 1000,
  271. },
  272. () => {
  273. if (!this.stopAnimation) {
  274. this.loopAnimation();
  275. }
  276. },
  277. );
  278. }
  279. },
  280. );
  281. });
  282. // #endif
  283. }
  284. // #ifdef APP-NVUE
  285. if (!this.scrollable && (this.single || this.moreText)) {
  286. dom.getComponentRect(this.$refs['textBox'], (res) => {
  287. this.wrapWidth = res.size.width;
  288. });
  289. }
  290. // #endif
  291. },
  292. loopAnimation() {
  293. // #ifdef APP-NVUE
  294. animation.transition(
  295. this.$refs['animationEle'],
  296. {
  297. styles: {
  298. transform: `translateX(0px)`,
  299. },
  300. duration: 0,
  301. },
  302. () => {
  303. if (!this.stopAnimation) {
  304. animation.transition(
  305. this.$refs['animationEle'],
  306. {
  307. styles: {
  308. transform: `translateX(-${this.textWidth}px)`,
  309. },
  310. duration: (this.textWidth / this.speed) * 1000,
  311. timingFunction: 'linear',
  312. delay: 0,
  313. },
  314. () => {
  315. if (!this.stopAnimation) {
  316. this.loopAnimation();
  317. }
  318. },
  319. );
  320. }
  321. },
  322. );
  323. // #endif
  324. },
  325. clickMore() {
  326. this.$emit('getmore');
  327. },
  328. close() {
  329. this.show = false;
  330. this.$emit('close');
  331. },
  332. onClick() {
  333. this.$emit('click');
  334. },
  335. },
  336. };
  337. </script>
  338. <style lang="scss" scoped>
  339. .uni-noticebar {
  340. /* #ifndef APP-NVUE */
  341. display: flex;
  342. width: 100%;
  343. box-sizing: border-box;
  344. /* #endif */
  345. flex-direction: row;
  346. align-items: center;
  347. padding: 10px 12px;
  348. // margin-bottom: 10px;
  349. }
  350. .uni-cursor-point {
  351. /* #ifdef H5 */
  352. cursor: pointer;
  353. /* #endif */
  354. }
  355. .uni-noticebar-close {
  356. margin-left: 8px;
  357. margin-right: 5px;
  358. }
  359. .uni-noticebar-icon {
  360. margin-right: 5px;
  361. }
  362. .uni-noticebar__content-wrapper {
  363. flex: 1;
  364. flex-direction: column;
  365. overflow: hidden;
  366. }
  367. .uni-noticebar__content-wrapper--single {
  368. /* #ifndef APP-NVUE */
  369. line-height: 18px;
  370. /* #endif */
  371. }
  372. .uni-noticebar__content-wrapper--single,
  373. .uni-noticebar__content-wrapper--scrollable {
  374. flex-direction: row;
  375. }
  376. /* #ifndef APP-NVUE */
  377. .uni-noticebar__content-wrapper--scrollable {
  378. position: relative;
  379. height: 18px;
  380. }
  381. /* #endif */
  382. .uni-noticebar__content--scrollable {
  383. /* #ifdef APP-NVUE */
  384. flex: 0;
  385. /* #endif */
  386. /* #ifndef APP-NVUE */
  387. flex: 1;
  388. display: block;
  389. overflow: hidden;
  390. /* #endif */
  391. }
  392. .uni-noticebar__content--single {
  393. /* #ifndef APP-NVUE */
  394. display: flex;
  395. flex: none;
  396. width: 100%;
  397. justify-content: center;
  398. /* #endif */
  399. }
  400. .uni-noticebar__content-text {
  401. font-size: 14px;
  402. line-height: 18px;
  403. /* #ifndef APP-NVUE */
  404. word-break: break-all;
  405. /* #endif */
  406. }
  407. .uni-noticebar__content-text--single {
  408. /* #ifdef APP-NVUE */
  409. lines: 1;
  410. /* #endif */
  411. /* #ifndef APP-NVUE */
  412. display: block;
  413. width: 100%;
  414. white-space: nowrap;
  415. /* #endif */
  416. overflow: hidden;
  417. text-overflow: ellipsis;
  418. }
  419. .uni-noticebar__content-text--scrollable {
  420. /* #ifdef APP-NVUE */
  421. lines: 1;
  422. padding-left: 750rpx;
  423. /* #endif */
  424. /* #ifndef APP-NVUE */
  425. position: absolute;
  426. display: block;
  427. height: 18px;
  428. line-height: 18px;
  429. white-space: nowrap;
  430. padding-left: 100%;
  431. animation: notice 10s 0s linear infinite both;
  432. animation-play-state: paused;
  433. /* #endif */
  434. }
  435. .uni-noticebar__more {
  436. /* #ifndef APP-NVUE */
  437. display: inline-flex;
  438. /* #endif */
  439. flex-direction: row;
  440. flex-wrap: nowrap;
  441. align-items: center;
  442. padding-left: 5px;
  443. }
  444. .uni-noticebar__more-text {
  445. font-size: 14px;
  446. }
  447. @keyframes notice {
  448. 100% {
  449. transform: translate3d(-100%, 0, 0);
  450. }
  451. }
  452. </style>