kui-vue
Version:
A lightweight desktop UI component library suitable for Vue.js 2.
194 lines (180 loc) • 4.97 kB
JSX
import Icon from "../icon";
import resize from "../directives/resize";
import { ChevronUp } from "kui-icons";
import { defineComponent, provide, ref, watch, onMounted, onBeforeUnmount, nextTick } from "vue";
import { withInstall } from '../utils/vue';
const Carousel = defineComponent({
name: "Carousel",
directives: { resize },
props: {
value: { type: Number, default: 0 },
loop: { type: Boolean, default: true },
autoplay: Boolean,
delay: { type: Number, default: 3000 },
vertical: Boolean,
dots: { type: Boolean, default: true },
},
setup(ps, { slots, emit, expose }) {
const currentIndex = ref(ps.value);
const posIndex = ref(ps.loop ? ps.value + 1 : ps.value);
const autoTimer = ref(null);
const width = ref(0);
const height = ref(0);
const animate = ref(ps.value > 0 ? false : true);
const playing = ref(false);
const carouselRef = ref(null);
provide("width", width);
provide("height", height);
watch(
() => ps.value,
(nv, ov) => {
currentIndex.value = nv;
}
);
const next = () => {
toSwitch("right");
};
const prev = () => {
toSwitch("left");
};
const goTo = (index) => {
clearInterval(autoTimer.value);
animate.value = true;
currentIndex.value = index;
posIndex.value = ps.loop ? index + 1 : index;
emit("update:value", index);
autoToPlay()
};
expose({ next, prev, goTo });
const autoToPlay = () => {
if (!ps.autoplay) return;
clearInterval(autoTimer.value);
autoTimer.value = setInterval(() => {
change("right");
}, parseInt(ps.delay));
};
let children = slots.default?.();
const toSwitch = (type) => {
clearInterval(autoTimer.value);
if (playing.value) return;
playing.value = true;
change(type);
};
const change = (type) => {
animate.value = true;
const total = ps.loop ? children?.length + 2 : children?.length;
let index = posIndex.value;
let nextCurrent = currentIndex.value;
if (type === "right") {
index = (index + 1) % total;
if (ps.loop) {
nextCurrent = (nextCurrent + 1) % children?.length;
} else {
nextCurrent = index;
}
} else if (type === "left") {
index = (index - 1 + total) % total;
if (ps.loop) {
nextCurrent = (nextCurrent - 1 + children?.length) % children?.length;
} else {
nextCurrent = index;
}
}
posIndex.value = index;
currentIndex.value = nextCurrent;
emit("update:value", currentIndex.value);
setTimeout(() => {
playing.value = false;
if (ps.loop) {
let count = ps.loop ? children?.length + 2 : children?.length;
if (posIndex.value === count - 1) {
animate.value = false;
posIndex.value = 1;
}
if (posIndex.value === 0) {
animate.value = false;
posIndex.value = count - 2;
}
}
}, 501);
autoToPlay()
};
const resize = () => {
animate.value = false;
width.value = carouselRef.value.offsetWidth;
height.value = carouselRef.value.offsetHeight;
};
onMounted(() => {
nextTick(() => {
resize();
autoToPlay();
});
});
onBeforeUnmount(() => {
clearInterval(autoTimer.value);
});
return () => {
let { vertical } = ps;
const first = children?.[0];
const last = children?.[children.length - 1];
const newChildren = ps.loop ? [last, ...children, first] : children;
let index = Math.min(children?.length - 1, currentIndex.value);
index = Math.max(0, index);
const classes = [
"k-carousel",
{
"k-carousel-vertical": vertical,
},
];
const dotsNode = (
<ul class="k-carousel-dots">
{children?.map((x, i) => (
<li class={{ "k-carousel-dots-active": index == i }} onClick={() => goTo(i)}></li>
))}
</ul>
);
let offsetX = 0,
offsetY = 0;
if (!vertical) {
offsetX = posIndex.value * width.value;
} else {
offsetY = posIndex.value * height.value;
}
const wrapperCls = {
class: "k-carousel-wrapper",
style: {
transform: `translate3d(-${offsetX}px,-${offsetY}px,0)`,
width: !vertical ? newChildren?.length * width.value + "px" : "",
height: vertical ? newChildren?.length * height.value + "px" : "",
transitionDuration: !animate.value ? "0s" : "",
},
};
const arrowLeft = (
<span class="k-carousel-arrow-left" onClick={() => toSwitch("left")}>
<Icon type={ChevronUp} />
</span>
);
const arrowRight = (
<span class="k-carousel-arrow-right" onClick={() => toSwitch("right")}>
<Icon type={ChevronUp} />
</span>
);
const props = {
class: classes,
ref: carouselRef,
onMouseEnter: () => clearInterval(autoTimer.value),
onMouseLeave: () => {
ps.autoplay && autoToPlay();
},
};
return (
<div v-resize={resize} {...props}>
<div {...wrapperCls}>{newChildren}</div>
{!vertical ? [arrowLeft, arrowRight] : null}
{ps.dots ? dotsNode : null}
</div>
);
};
},
});
export default withInstall(Carousel);