221 lines
3.9 KiB
Vue
221 lines
3.9 KiB
Vue
<template>
|
|
<view
|
|
class="cl-noticebar"
|
|
:class="[
|
|
{
|
|
'is-scrollable': scrollable,
|
|
},
|
|
]"
|
|
:style="{
|
|
color,
|
|
backgroundColor,
|
|
}"
|
|
v-if="visible"
|
|
>
|
|
<cl-icon
|
|
name="close-border"
|
|
:size="34"
|
|
:margin="[0, 10, 0, 0]"
|
|
@tap="close"
|
|
v-if="closeable"
|
|
/>
|
|
|
|
<slot name="icon">
|
|
<cl-icon :class-name="icon" :size="34" :margin="[0, 10, 0, 0]" v-if="icon" />
|
|
</slot>
|
|
|
|
<view class="cl-noticebar__box">
|
|
<view
|
|
class="cl-noticebar__scroller"
|
|
:class="[`is-${direction}`]"
|
|
:style="{
|
|
height: parseRpx(height),
|
|
top: scroll.top + 'px',
|
|
left: scroll.left + 'px',
|
|
transition,
|
|
transform: `translateX(-${scroll.translateX}px)`,
|
|
}"
|
|
>
|
|
<view class="cl-noticebar__item" v-for="(item, index) in list" :key="index">
|
|
<text class="cl-noticebar__text">{{ item }}</text>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</template>
|
|
|
|
<script lang="ts">
|
|
import {
|
|
defineComponent,
|
|
onMounted,
|
|
onUnmounted,
|
|
reactive,
|
|
ref,
|
|
computed,
|
|
getCurrentInstance,
|
|
type PropType,
|
|
} from "vue";
|
|
import { isArray } from "lodash-es";
|
|
import { parseRpx } from "/@/cool/utils";
|
|
|
|
export default defineComponent({
|
|
name: "cl-noticebar",
|
|
|
|
props: {
|
|
// 文本内容
|
|
text: {
|
|
type: [String, Array],
|
|
default: "",
|
|
required: true,
|
|
},
|
|
// 方向
|
|
direction: {
|
|
type: String as PropType<"horizontal" | "vertical">,
|
|
default: "horizontal",
|
|
},
|
|
// 字体颜色
|
|
color: String,
|
|
// 背景颜色
|
|
backgroundColor: String,
|
|
// 能否滚动
|
|
scrollable: Boolean,
|
|
// 滚动时长
|
|
duration: {
|
|
type: Number,
|
|
default: 10,
|
|
},
|
|
// 能否关闭
|
|
closeable: Boolean,
|
|
// 左侧图标
|
|
icon: String,
|
|
// 高度
|
|
height: {
|
|
type: [String, Number],
|
|
default: 40,
|
|
},
|
|
},
|
|
|
|
emits: ["close"],
|
|
|
|
setup(props, { emit }) {
|
|
const { proxy }: any = getCurrentInstance();
|
|
|
|
// 是否可见
|
|
const visible = ref(true);
|
|
|
|
// 滚动配置
|
|
const scroll = reactive({
|
|
left: 0,
|
|
top: 0,
|
|
translateX: 0,
|
|
duration: 0,
|
|
});
|
|
|
|
// 计时器
|
|
let timer: any = null;
|
|
|
|
// 文案列表
|
|
const list = computed(() => {
|
|
return isArray(props.text) ? props.text : [props.text];
|
|
});
|
|
|
|
// 动画过度
|
|
const transition = computed(() => {
|
|
if (props.direction == "horizontal") {
|
|
return `transform ${scroll.duration}s linear`;
|
|
} else {
|
|
return `top 0.3s`;
|
|
}
|
|
});
|
|
|
|
// 刷新
|
|
function refresh() {
|
|
if (props.scrollable) {
|
|
// 清除定时器
|
|
clear();
|
|
|
|
// 获取盒子大小
|
|
uni.createSelectorQuery()
|
|
.in(proxy)
|
|
.select(`.cl-noticebar__box`)
|
|
.boundingClientRect((box: any) => {
|
|
// 获取文本大小
|
|
uni.createSelectorQuery()
|
|
.in(proxy)
|
|
.select(`.cl-noticebar__text`)
|
|
.boundingClientRect((text: any) => {
|
|
const duration = props.duration * 1000;
|
|
|
|
// 水平滑动
|
|
if (props.direction == "horizontal") {
|
|
const fn = () => {
|
|
scroll.duration = props.duration;
|
|
scroll.left = box.width;
|
|
scroll.translateX = text.width + scroll.left;
|
|
|
|
timer = setTimeout(() => {
|
|
scroll.translateX = 0;
|
|
scroll.duration = 0;
|
|
|
|
setTimeout(fn, 500);
|
|
}, duration);
|
|
};
|
|
|
|
fn();
|
|
}
|
|
// 垂直滑动
|
|
else {
|
|
timer = setInterval(() => {
|
|
if (
|
|
Math.abs(scroll.top) >=
|
|
box.height * (list.value.length - 1)
|
|
) {
|
|
scroll.top = 0;
|
|
} else {
|
|
scroll.top -= box.height;
|
|
}
|
|
}, duration);
|
|
}
|
|
})
|
|
.exec();
|
|
})
|
|
.exec();
|
|
}
|
|
}
|
|
|
|
// 关闭
|
|
function close() {
|
|
clear();
|
|
visible.value = false;
|
|
emit("close");
|
|
}
|
|
|
|
// 清空
|
|
function clear() {
|
|
clearInterval(timer);
|
|
clearTimeout(timer);
|
|
timer = null;
|
|
}
|
|
|
|
onMounted(() => {
|
|
refresh();
|
|
});
|
|
|
|
onUnmounted(() => {
|
|
clear();
|
|
});
|
|
|
|
return {
|
|
visible,
|
|
scroll,
|
|
list,
|
|
transition,
|
|
refresh,
|
|
close,
|
|
clear,
|
|
parseRpx,
|
|
};
|
|
},
|
|
});
|
|
</script>
|