| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418 |
- <template>
- <view :class="classes">
- <view
- v-show="showNoticebar"
- class="nut-noticebar__page"
- :class="{
- 'nut-noticebar__page--withicon': closeMode,
- 'nut-noticebar__page--close': closeMode,
- 'nut-noticebar__page--wrapable': wrapable
- }"
- :style="barStyle"
- @click="handleClick"
- v-if="direction == 'across'"
- >
- <view class="nut-noticebar__page-lefticon">
- <slot name="left-icon">
- <Notice size="16px" v-if="leftIcon"></Notice>
- </slot>
- </view>
- <view ref="wrap" class="nut-noticebar__page-wrap">
- <view
- ref="content"
- :class="wrapContentClass"
- :style="contentStyle"
- @animationend="onAnimationEnd"
- @webkitAnimationEnd="onAnimationEnd"
- ><slot>{{ text }}</slot>
- </view>
- </view>
- <view v-if="closeMode || $slots['right-icon']" class="nut-noticebar__page-righticon" @click.stop="onClickIcon">
- <slot name="right-icon" v-if="$slots['right-icon']"> </slot>
- <CircleClose v-else />
- </view>
- </view>
- <view
- class="nut-noticebar__vertical"
- v-if="scrollList.length > 0 && direction == 'vertical' && showNoticebar"
- :style="barStyle"
- >
- <template v-if="slots.default">
- <view class="nut-noticebar__vertical-list" :style="horseLampStyle">
- <ScrollItem
- v-for="(item, index) in scrollList"
- :key="index"
- :style="{ height: height + 'px', 'line-height': height + 'px' }"
- :item="item"
- ></ScrollItem>
- </view>
- </template>
- <template v-else>
- <ul class="nut-noticebar__vertical-list" :style="horseLampStyle">
- <li
- class="nut-noticebar__vertical-item"
- v-for="(item, index) in scrollList"
- :key="index"
- :style="{ height: pxCheck(height) }"
- @click="go(item)"
- >
- {{ item }}
- </li>
- </ul>
- </template>
- <view class="go" @click="!slots.rightIcon && handleClickIcon()">
- <slot name="right-icon">
- <CircleClose v-if="closeMode" :color="color" size="11px" />
- </slot>
- </view>
- </view>
- </view>
- </template>
- <script lang="ts">
- import {
- toRefs,
- onMounted,
- onUnmounted,
- reactive,
- computed,
- onActivated,
- onDeactivated,
- ref,
- watch,
- h,
- Slots
- } from 'vue';
- import { Notice, CircleClose } from '@nutui/icons-vue';
- import { createComponent } from '@/packages/utils/create';
- const { componentName, create } = createComponent('noticebar');
- import { pxCheck } from '@/packages/utils/pxCheck';
- interface StateProps {
- wrapWidth: number;
- firstRound: boolean;
- duration: number;
- offsetWidth: number;
- showNoticebar: boolean;
- animationClass: string;
- animate: boolean;
- scrollList: Slots[];
- distance: number;
- timer: null;
- keepAlive: boolean;
- isCanScroll: null | boolean;
- }
- export default create({
- props: {
- // 滚动方向 across 横向 vertical 纵向
- direction: {
- type: String,
- default: 'across'
- },
- list: {
- type: Array,
- default: () => {
- return [];
- }
- },
- standTime: {
- type: Number,
- default: 1000
- },
- complexAm: {
- type: Boolean,
- default: false
- },
- height: {
- type: Number,
- default: 40
- },
- text: {
- type: String,
- default: ''
- },
- closeMode: {
- type: Boolean,
- default: false
- },
- wrapable: {
- type: Boolean,
- default: false
- },
- leftIcon: { type: Boolean, default: true },
- color: {
- type: String,
- default: ''
- },
- background: {
- type: String,
- default: ''
- },
- delay: {
- type: [String, Number],
- default: 1
- },
- scrollable: {
- type: Boolean,
- default: null
- },
- speed: {
- type: Number,
- default: 50
- }
- },
- components: {
- ScrollItem: function (props) {
- props.item.props.style = props.style;
- props.item.key = props.key;
- return h(props.item);
- },
- Notice,
- CircleClose
- },
- emits: ['click', 'close'],
- setup(props, { emit, slots }) {
- const wrap = ref<null | HTMLElement>(null);
- const content = ref<null | HTMLElement>(null);
- const state = reactive<StateProps>({
- wrapWidth: 0,
- firstRound: true,
- duration: 0,
- offsetWidth: 0,
- showNoticebar: true,
- animationClass: '',
- animate: false,
- scrollList: [],
- distance: 0,
- timer: null,
- keepAlive: false,
- isCanScroll: null
- });
- const classes = computed(() => {
- const prefixCls = componentName;
- return {
- [prefixCls]: true
- };
- });
- const isEllipsis = computed(() => {
- if (state.isCanScroll == null) {
- return props.wrapable;
- } else {
- return !state.isCanScroll && !props.wrapable;
- }
- });
- const wrapContentClass = computed(() => {
- return {
- 'nut-noticebar__page-wrap-content': true,
- 'nut-ellipsis': isEllipsis.value,
- [state.animationClass]: true
- };
- });
- const barStyle = computed(() => {
- let style: {
- [props: string]: any;
- } = {};
- props.color && (style.color = props.color);
- props.background && (style.background = props.background);
- if (props.direction == 'vertical') {
- style.height = `${props.height}px`;
- }
- return style;
- });
- const contentStyle = computed(() => {
- return {
- animationDelay: (state.firstRound ? props.delay : 0) + 's',
- animationDuration: state.duration + 's',
- transform: `translateX(${state.firstRound ? 0 : state.wrapWidth + 'px'})`
- };
- });
- const horseLampStyle = computed(() => {
- let styles = {};
- if (props.complexAm) {
- styles = {
- transform: `translateY(${state.distance}px)`
- };
- } else {
- if (state.animate) {
- let a = ~~(props.height / props.speed / 4);
- styles = {
- transition: `all ${a == 0 ? ~~(props.height / props.speed) : a}s`,
- 'margin-top': `-${props.height}px`
- };
- }
- }
- return styles;
- });
- watch(
- () => props.text,
- (value) => {
- initScrollWrap(value);
- }
- );
- watch(
- () => props.list,
- (value) => {
- state.scrollList = [].concat(value as any);
- }
- );
- const initScrollWrap = (value: string) => {
- if (state.showNoticebar == false) {
- return;
- }
- setTimeout(() => {
- if (!wrap.value || !content.value) {
- return;
- }
- const wrapWidth = wrap.value.getBoundingClientRect().width;
- const offsetWidth = content.value.getBoundingClientRect().width;
- state.isCanScroll = props.scrollable == null ? offsetWidth > wrapWidth : props.scrollable;
- console.log(111, state.isCanScroll);
- if (state.isCanScroll) {
- state.wrapWidth = wrapWidth;
- state.offsetWidth = offsetWidth;
- state.duration = offsetWidth / props.speed;
- state.animationClass = 'play';
- } else {
- state.animationClass = '';
- }
- }, 0);
- };
- const handleClick = (event: Event) => {
- emit('click', event);
- };
- const onClickIcon = (event: Event) => {
- if (props.closeMode) {
- state.showNoticebar = !props.closeMode;
- }
- emit('close', event);
- };
- const onAnimationEnd = () => {
- state.firstRound = false;
- setTimeout(() => {
- state.duration = (state.offsetWidth + state.wrapWidth) / props.speed;
- state.animationClass = 'play-infinite';
- }, 0);
- };
- /**
- * 利益点滚动方式一
- */
- const startRollEasy = () => {
- showhorseLamp();
- (state.timer as any) = setInterval(showhorseLamp, ~~((props.height / props.speed / 4) * 1000) + props.standTime);
- };
- const showhorseLamp = () => {
- state.animate = true;
- setTimeout(() => {
- state.scrollList.push(state.scrollList[0]);
- state.scrollList.shift();
- state.animate = false;
- }, ~~((props.height / props.speed / 4) * 1000));
- };
- const startRoll = () => {
- (state.timer as any) = setInterval(() => {
- let chunk = 100;
- for (let i = 0; i < chunk; i++) {
- scroll(i, i < chunk - 1 ? false : true);
- }
- }, props.standTime + 100 * props.speed);
- };
- const scroll = (n: number, last: boolean) => {
- setTimeout(() => {
- state.distance -= props.height / 100;
- if (last) {
- state.scrollList.push(state.scrollList[0]);
- state.scrollList.shift();
- state.distance = 0;
- }
- }, n * props.speed);
- };
- /**
- * 点击滚动单元
- */
- const go = (item: any) => {
- emit('click', item);
- };
- const handleClickIcon = () => {
- if (props.closeMode) {
- state.showNoticebar = !props.closeMode;
- }
- emit('close', state.scrollList[0]);
- };
- onMounted(() => {
- if (props.direction == 'vertical') {
- if (slots.default) {
- state.scrollList = [].concat(slots.default()[0].children as any);
- } else {
- state.scrollList = [].concat(props.list as any);
- }
- setTimeout(() => {
- props.complexAm ? startRoll() : startRollEasy();
- }, props.standTime);
- } else {
- initScrollWrap(props.text);
- }
- });
- onActivated(() => {
- if (state.keepAlive) {
- state.keepAlive = false;
- }
- });
- onDeactivated(() => {
- state.keepAlive = true;
- clearInterval(state.timer as any);
- });
- onUnmounted(() => {
- clearInterval(state.timer as any);
- });
- return {
- ...toRefs(props),
- ...toRefs(state),
- isEllipsis,
- classes,
- barStyle,
- contentStyle,
- horseLampStyle,
- wrap,
- content,
- handleClick,
- onClickIcon,
- onAnimationEnd,
- go,
- handleClickIcon,
- slots,
- pxCheck,
- wrapContentClass
- };
- }
- });
- </script>
|