123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191 |
- <template>
- <div class="notice-bar" :style="{ background, height: `${height}px` }" v-show="!state.isMode">
- <div class="notice-bar-warp" :style="{ color, fontSize: `${size}px` }">
- <i v-if="leftIcon" class="notice-bar-warp-left-icon" :class="leftIcon"></i>
- <div class="notice-bar-warp-text-box" ref="noticeBarWarpRef">
- <div class="notice-bar-warp-text" ref="noticeBarTextRef" v-if="!scrollable">{{ text }}</div>
- <div class="notice-bar-warp-slot" v-else><slot /></div>
- </div>
- <SvgIcon :name="rightIcon" v-if="rightIcon" class="notice-bar-warp-right-icon" @click="onRightIconClick" />
- </div>
- </div>
- </template>
- <script setup lang="ts" name="noticeBar">
- import { reactive, ref, onMounted, nextTick } from 'vue';
- // 定义父组件传过来的值
- const props = defineProps({
- // 通知栏模式,可选值为 closeable link
- mode: {
- type: String,
- default: () => '',
- },
- // 通知文本内容
- text: {
- type: String,
- default: () => '',
- },
- // 通知文本颜色
- color: {
- type: String,
- default: () => 'var(--el-color-warning)',
- },
- // 通知背景色
- background: {
- type: String,
- default: () => 'var(--el-color-warning-light-9)',
- },
- // 字体大小,单位px
- size: {
- type: [Number, String],
- default: () => 14,
- },
- // 通知栏高度,单位px
- height: {
- type: Number,
- default: () => 40,
- },
- // 动画延迟时间 (s)
- delay: {
- type: Number,
- default: () => 1,
- },
- // 滚动速率 (px/s)
- speed: {
- type: Number,
- default: () => 100,
- },
- // 是否开启垂直滚动
- scrollable: {
- type: Boolean,
- default: () => false,
- },
- // 自定义左侧图标
- leftIcon: {
- type: String,
- default: () => '',
- },
- // 自定义右侧图标
- rightIcon: {
- type: String,
- default: () => '',
- },
- });
- // 定义子组件向父组件传值/事件
- const emit = defineEmits(['close', 'link']);
- // 定义变量内容
- const noticeBarWarpRef = ref();
- const noticeBarTextRef = ref();
- const state = reactive({
- order: 1,
- oneTime: 0,
- twoTime: 0,
- warpOWidth: 0,
- textOWidth: 0,
- isMode: false,
- });
- // 初始化 animation 各项参数
- const initAnimation = () => {
- nextTick(() => {
- state.warpOWidth = noticeBarWarpRef.value.offsetWidth;
- state.textOWidth = noticeBarTextRef.value.offsetWidth;
- document.styleSheets[0].insertRule(`@keyframes oneAnimation {0% {left: 0px;} 100% {left: -${state.textOWidth}px;}}`);
- document.styleSheets[0].insertRule(`@keyframes twoAnimation {0% {left: ${state.warpOWidth}px;} 100% {left: -${state.textOWidth}px;}}`);
- computeAnimationTime();
- setTimeout(() => {
- changeAnimation();
- }, props.delay * 1000);
- });
- };
- // 计算 animation 滚动时长
- const computeAnimationTime = () => {
- state.oneTime = state.textOWidth / props.speed;
- state.twoTime = (state.textOWidth + state.warpOWidth) / props.speed;
- };
- // 改变 animation 动画调用
- const changeAnimation = () => {
- if (state.order === 1) {
- noticeBarTextRef.value.style.cssText = `animation: oneAnimation ${state.oneTime}s linear; opactity: 1;}`;
- state.order = 2;
- } else {
- noticeBarTextRef.value.style.cssText = `animation: twoAnimation ${state.twoTime}s linear infinite; opacity: 1;`;
- }
- };
- // 监听 animation 动画的结束
- const listenerAnimationend = () => {
- noticeBarTextRef.value.addEventListener(
- 'animationend',
- () => {
- changeAnimation();
- },
- false
- );
- };
- // 右侧 icon 图标点击
- const onRightIconClick = () => {
- if (!props.mode) return false;
- if (props.mode === 'closeable') {
- state.isMode = true;
- emit('close');
- } else if (props.mode === 'link') {
- emit('link');
- }
- };
- // 页面加载时
- onMounted(() => {
- if (props.scrollable) return false;
- initAnimation();
- listenerAnimationend();
- });
- </script>
- <style scoped lang="scss">
- .notice-bar {
- padding: 0 15px;
- width: 100%;
- border-radius: 4px;
- .notice-bar-warp {
- display: flex;
- align-items: center;
- width: 100%;
- height: inherit;
- .notice-bar-warp-text-box {
- flex: 1;
- height: inherit;
- display: flex;
- align-items: center;
- overflow: hidden;
- position: relative;
- .notice-bar-warp-text {
- white-space: nowrap;
- position: absolute;
- left: 0;
- }
- .notice-bar-warp-slot {
- width: 100%;
- white-space: nowrap;
- :deep(.el-carousel__item) {
- display: flex;
- align-items: center;
- }
- }
- }
- .notice-bar-warp-left-icon {
- width: 24px;
- font-size: inherit !important;
- }
- .notice-bar-warp-right-icon {
- width: 24px;
- text-align: right;
- font-size: inherit !important;
- &:hover {
- cursor: pointer;
- }
- }
- }
- }
- </style>
|