UNPKG

vue-roller

Version:

Fluid and smooth rolling animation for Vue.js

278 lines (277 loc) 10.6 kB
import { ref, computed, watch, onMounted, nextTick, defineComponent, toRefs, openBlock, createElementBlock, normalizeStyle, unref, normalizeClass, createElementVNode, Fragment, renderList, toDisplayString, createBlock, TransitionGroup, withCtx } from "vue"; function useReloadAnimation(duration) { const isReady = ref(false); const isEnd = ref(false); let outerTimer; let innerTimer; let animationEnd = function() { }; return { reloadAnimation: () => { isReady.value = false; isEnd.value = false; clearTimeout(outerTimer); clearTimeout(innerTimer); outerTimer = setTimeout(() => { isReady.value = true; innerTimer = setTimeout(() => { isEnd.value = true; animationEnd(); }, duration.value); }, 100); }, isReady, isEnd, onAnimationEnd: (fn) => { animationEnd = fn; } }; } function useAnimationManager(char, defaultChar, charSet, duration) { const targetIdx = computed(() => charSet.value.indexOf(char.value)); const prevTargetIdx = ref(charSet.value.indexOf(defaultChar.value)); const { reloadAnimation, isReady, isEnd } = useReloadAnimation(duration); watch(char, (next, prev) => { prevTargetIdx.value = charSet.value.indexOf(prev); reloadAnimation(); }); reloadAnimation(); return { isReady, isEnd, targetIdx, prevTargetIdx }; } function useMeasureText(itemElement) { const width = ref(0); function updateWidth() { var _a; width.value = ((_a = itemElement.value) == null ? void 0 : _a.clientWidth) || 16; } onMounted(updateWidth); watch(itemElement, updateWidth); return { width }; } function useSelectElement(itemElements, targetIdx) { const itemElement = ref(null); function updateItemElement() { itemElement.value = itemElements.value[targetIdx.value]; } onMounted(updateItemElement); watch(targetIdx, () => { nextTick(updateItemElement); }); return { itemElement }; } var RollerItemMode = /* @__PURE__ */ ((RollerItemMode2) => { RollerItemMode2["SHORT"] = "short"; RollerItemMode2["LONG"] = "long"; return RollerItemMode2; })(RollerItemMode || {}); var RollerItemCharSet = /* @__PURE__ */ ((RollerItemCharSet2) => { RollerItemCharSet2["NUMBER"] = "number"; RollerItemCharSet2["ALPHABET"] = "alphabet"; return RollerItemCharSet2; })(RollerItemCharSet || {}); const RollerCharSet = { ["number"]: [" ", ...[...Array(10).keys()].map(String), ","], ["alphabet"]: [ " ", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "-" ] }; var RollerItem_vue_vue_type_style_index_0_scoped_true_lang = /* @__PURE__ */ (() => ".roller-item[data-v-5357a046]{position:relative;height:1em;transition:width .5s}.roller-item .roller-item__wrapper[data-v-5357a046]{position:relative;display:flex;align-items:center;width:100%;height:200%;overflow:hidden;top:-50%;mask-image:linear-gradient(0deg,rgba(255,255,255,0) 0%,rgb(0,0,0) 25%,rgb(0,0,0) 75%,rgba(255,255,255,0) 100%);-webkit-mask-image:linear-gradient(0deg,rgba(255,255,255,0) 0%,rgb(0,0,0) 25%,rgb(0,0,0) 75%,rgba(255,255,255,0) 100%);box-sizing:border-box}.roller-item .roller-item__wrapper.roller-item__wrapper--short[data-v-5357a046]{mask-image:linear-gradient(0deg,rgba(255,255,255,0) 22%,rgb(0,0,0) 30%,rgb(0,0,0) 70%,rgba(255,255,255,0) 78%);-webkit-mask-image:linear-gradient(0deg,rgba(255,255,255,0) 22%,rgb(0,0,0) 30%,rgb(0,0,0) 70%,rgba(255,255,255,0) 78%)}.roller-item .roller-item__wrapper .roller-item__wrapper__list[data-v-5357a046]{position:absolute;width:100%;display:flex;align-items:center;flex-direction:column;box-sizing:border-box;transition:.25s}.roller-item .roller-item__wrapper .roller-item__wrapper__list .roller-item__wrapper__list__item[data-v-5357a046]{display:flex;width:fit-content;height:1em;line-height:1;box-sizing:border-box;user-select:none}.roller-item .roller-item__wrapper .roller-item__wrapper__list .roller-item__wrapper__list__item--target[data-v-5357a046]{user-select:text}\n")(); var _export_sfc = (sfc, props) => { const target = sfc.__vccOpts || sfc; for (const [key, val] of props) { target[key] = val; } return target; }; const _hoisted_1 = { class: "roller-item__wrapper__list" }; const _sfc_main$1 = defineComponent({ name: "RollerItem", props: { char: { default: "" }, defaultChar: { default: "" }, duration: { default: 500 }, charSet: { default: () => RollerCharSet[RollerItemCharSet.NUMBER] }, mode: { default: RollerItemMode.SHORT } }, setup(__props) { const props = __props; const { char, defaultChar, charSet, duration } = toRefs(props); const { isReady, isEnd, targetIdx, prevTargetIdx } = useAnimationManager(char, defaultChar, charSet, duration); const itemElements = ref([]); const { itemElement } = useSelectElement(itemElements, targetIdx); const { width } = useMeasureText(itemElement); const top = computed(() => { if (!isReady.value) { if (prevTargetIdx.value !== -1) return `-${prevTargetIdx.value / charSet.value.length * 100}%`; return "0%"; } if (targetIdx.value === -1) return `-${1 / charSet.value.length * 100}%`; return `-${targetIdx.value / charSet.value.length * 100}%`; }); const shortCharSet = computed(() => { if (props.mode == RollerItemMode.SHORT) return [props.char]; if (targetIdx.value == -1) return ["", props.char, ""]; if (targetIdx.value == 0) return ["", props.char, props.charSet[targetIdx.value + 1]]; if (targetIdx.value == charSet.value.length - 1) return [props.charSet[targetIdx.value - 1], props.char, ""]; return props.charSet.slice(targetIdx.value - 1, targetIdx.value + 2); }); return (_ctx, _cache) => { return openBlock(), createElementBlock("div", { class: "roller-item", style: normalizeStyle({ width: `${unref(width)}px` }) }, [ unref(isEnd) ? (openBlock(), createElementBlock("div", { key: 0, class: normalizeClass(["roller-item__wrapper", { "roller-item__wrapper--short": __props.mode == unref(RollerItemMode).SHORT }]) }, [ createElementVNode("div", _hoisted_1, [ (openBlock(true), createElementBlock(Fragment, null, renderList(unref(shortCharSet), (item) => { return openBlock(), createElementBlock("div", { class: normalizeClass(["roller-item__wrapper__list__item", { "roller-item__wrapper__list__item--target": item == unref(char) }]), ref_for: true, ref_key: "itemElements", ref: itemElements }, toDisplayString(item), 3); }), 256)) ]) ], 2)) : (openBlock(), createElementBlock("div", { key: 1, class: normalizeClass(["roller-item__wrapper", { "roller-item__wrapper--short": __props.mode == unref(RollerItemMode).SHORT }]) }, [ createElementVNode("div", { class: "roller-item__wrapper__list", style: normalizeStyle({ top: "25%", transform: `translateY(${unref(top)})`, transition: `transform ${unref(duration)}ms` }) }, [ (openBlock(true), createElementBlock(Fragment, null, renderList(unref(charSet), (item) => { return openBlock(), createElementBlock("div", { class: normalizeClass(["roller-item__wrapper__list__item", { "roller-item__wrapper__list__item--target": item == unref(char) }]), ref_for: true, ref_key: "itemElements", ref: itemElements }, toDisplayString(item), 3); }), 256)) ], 4) ], 2)) ], 4); }; } }); var RollerItem = /* @__PURE__ */ _export_sfc(_sfc_main$1, [["__scopeId", "data-v-5357a046"]]); var Roller_vue_vue_type_style_index_0_scoped_true_lang = /* @__PURE__ */ (() => ".roller[data-v-4760d5f9]{display:flex;flex-wrap:wrap}.roller-list-enter-active[data-v-4760d5f9],.roller-list-leave-active[data-v-4760d5f9]{transition:.5s}.roller-list-enter-from[data-v-4760d5f9]{opacity:0}.roller-list-enter-to[data-v-4760d5f9],.roller-list-leave-from[data-v-4760d5f9]{opacity:1}.roller-list-leave-to[data-v-4760d5f9]{opacity:0;width:0px!important}\n")(); const _sfc_main = defineComponent({ name: "Roller", props: { value: { default: "123" }, defaultValue: { default: "" }, duration: { default: 500 }, charSet: null, mode: null }, emits: ["animation-end"], setup(__props, { emit }) { const props = __props; const { duration, value } = toRefs(props); const { reloadAnimation, onAnimationEnd } = useReloadAnimation(duration); watch([value], () => { reloadAnimation(); }); onAnimationEnd(() => emit("animation-end")); reloadAnimation(); const charArray = computed(() => [...props.value]); const defaultCharArray = computed(() => [...props.defaultValue]); const computedCharSet = computed(() => { if (Array.isArray(props.charSet)) return props.charSet; return RollerCharSet[props.charSet]; }); return (_ctx, _cache) => { return openBlock(), createBlock(TransitionGroup, { tag: "div", name: "roller-list", class: "roller" }, { default: withCtx(() => [ (openBlock(true), createElementBlock(Fragment, null, renderList(unref(charArray), (char, idx) => { return openBlock(), createBlock(RollerItem, { char, duration: unref(duration), charSet: unref(computedCharSet), defaultChar: unref(defaultCharArray)[idx], mode: __props.mode, key: idx }, null, 8, ["char", "duration", "charSet", "defaultChar", "mode"]); }), 128)) ]), _: 1 }); }; } }); var Roller = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-4760d5f9"]]); const install = (Vue) => { Vue.component(Roller.name, Roller); Vue.component(RollerItem.name, RollerItem); }; if (typeof window !== "undefined" && window.Vue) { install(window.Vue); } export { Roller, RollerItem, install };