index.vue 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191
  1. <template>
  2. <div class="notice-bar" :style="{ background, height: `${height}px` }" v-show="!state.isMode">
  3. <div class="notice-bar-warp" :style="{ color, fontSize: `${size}px` }">
  4. <i v-if="leftIcon" class="notice-bar-warp-left-icon" :class="leftIcon"></i>
  5. <div class="notice-bar-warp-text-box" ref="noticeBarWarpRef">
  6. <div class="notice-bar-warp-text" ref="noticeBarTextRef" v-if="!scrollable">{{ text }}</div>
  7. <div class="notice-bar-warp-slot" v-else><slot /></div>
  8. </div>
  9. <SvgIcon :name="rightIcon" v-if="rightIcon" class="notice-bar-warp-right-icon" @click="onRightIconClick" />
  10. </div>
  11. </div>
  12. </template>
  13. <script setup lang="ts" name="noticeBar">
  14. import { reactive, ref, onMounted, nextTick } from 'vue';
  15. // 定义父组件传过来的值
  16. const props = defineProps({
  17. // 通知栏模式,可选值为 closeable link
  18. mode: {
  19. type: String,
  20. default: () => '',
  21. },
  22. // 通知文本内容
  23. text: {
  24. type: String,
  25. default: () => '',
  26. },
  27. // 通知文本颜色
  28. color: {
  29. type: String,
  30. default: () => 'var(--el-color-warning)',
  31. },
  32. // 通知背景色
  33. background: {
  34. type: String,
  35. default: () => 'var(--el-color-warning-light-9)',
  36. },
  37. // 字体大小,单位px
  38. size: {
  39. type: [Number, String],
  40. default: () => 14,
  41. },
  42. // 通知栏高度,单位px
  43. height: {
  44. type: Number,
  45. default: () => 40,
  46. },
  47. // 动画延迟时间 (s)
  48. delay: {
  49. type: Number,
  50. default: () => 1,
  51. },
  52. // 滚动速率 (px/s)
  53. speed: {
  54. type: Number,
  55. default: () => 100,
  56. },
  57. // 是否开启垂直滚动
  58. scrollable: {
  59. type: Boolean,
  60. default: () => false,
  61. },
  62. // 自定义左侧图标
  63. leftIcon: {
  64. type: String,
  65. default: () => '',
  66. },
  67. // 自定义右侧图标
  68. rightIcon: {
  69. type: String,
  70. default: () => '',
  71. },
  72. });
  73. // 定义子组件向父组件传值/事件
  74. const emit = defineEmits(['close', 'link']);
  75. // 定义变量内容
  76. const noticeBarWarpRef = ref();
  77. const noticeBarTextRef = ref();
  78. const state = reactive({
  79. order: 1,
  80. oneTime: 0,
  81. twoTime: 0,
  82. warpOWidth: 0,
  83. textOWidth: 0,
  84. isMode: false,
  85. });
  86. // 初始化 animation 各项参数
  87. const initAnimation = () => {
  88. nextTick(() => {
  89. state.warpOWidth = noticeBarWarpRef.value.offsetWidth;
  90. state.textOWidth = noticeBarTextRef.value.offsetWidth;
  91. document.styleSheets[0].insertRule(`@keyframes oneAnimation {0% {left: 0px;} 100% {left: -${state.textOWidth}px;}}`);
  92. document.styleSheets[0].insertRule(`@keyframes twoAnimation {0% {left: ${state.warpOWidth}px;} 100% {left: -${state.textOWidth}px;}}`);
  93. computeAnimationTime();
  94. setTimeout(() => {
  95. changeAnimation();
  96. }, props.delay * 1000);
  97. });
  98. };
  99. // 计算 animation 滚动时长
  100. const computeAnimationTime = () => {
  101. state.oneTime = state.textOWidth / props.speed;
  102. state.twoTime = (state.textOWidth + state.warpOWidth) / props.speed;
  103. };
  104. // 改变 animation 动画调用
  105. const changeAnimation = () => {
  106. if (state.order === 1) {
  107. noticeBarTextRef.value.style.cssText = `animation: oneAnimation ${state.oneTime}s linear; opactity: 1;}`;
  108. state.order = 2;
  109. } else {
  110. noticeBarTextRef.value.style.cssText = `animation: twoAnimation ${state.twoTime}s linear infinite; opacity: 1;`;
  111. }
  112. };
  113. // 监听 animation 动画的结束
  114. const listenerAnimationend = () => {
  115. noticeBarTextRef.value.addEventListener(
  116. 'animationend',
  117. () => {
  118. changeAnimation();
  119. },
  120. false
  121. );
  122. };
  123. // 右侧 icon 图标点击
  124. const onRightIconClick = () => {
  125. if (!props.mode) return false;
  126. if (props.mode === 'closeable') {
  127. state.isMode = true;
  128. emit('close');
  129. } else if (props.mode === 'link') {
  130. emit('link');
  131. }
  132. };
  133. // 页面加载时
  134. onMounted(() => {
  135. if (props.scrollable) return false;
  136. initAnimation();
  137. listenerAnimationend();
  138. });
  139. </script>
  140. <style scoped lang="scss">
  141. .notice-bar {
  142. padding: 0 15px;
  143. width: 100%;
  144. border-radius: 4px;
  145. .notice-bar-warp {
  146. display: flex;
  147. align-items: center;
  148. width: 100%;
  149. height: inherit;
  150. .notice-bar-warp-text-box {
  151. flex: 1;
  152. height: inherit;
  153. display: flex;
  154. align-items: center;
  155. overflow: hidden;
  156. position: relative;
  157. .notice-bar-warp-text {
  158. white-space: nowrap;
  159. position: absolute;
  160. left: 0;
  161. }
  162. .notice-bar-warp-slot {
  163. width: 100%;
  164. white-space: nowrap;
  165. :deep(.el-carousel__item) {
  166. display: flex;
  167. align-items: center;
  168. }
  169. }
  170. }
  171. .notice-bar-warp-left-icon {
  172. width: 24px;
  173. font-size: inherit !important;
  174. }
  175. .notice-bar-warp-right-icon {
  176. width: 24px;
  177. text-align: right;
  178. font-size: inherit !important;
  179. &:hover {
  180. cursor: pointer;
  181. }
  182. }
  183. }
  184. }
  185. </style>