su-notice-bar.vue 12 KB

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