comic-plus
Version:
<p align="center"> <img width="200px" src="./logo.png"/> </p>
207 lines (206 loc) • 6.29 kB
JavaScript
import { defineComponent, ref, computed, watch, onMounted, provide, openBlock, createElementBlock, normalizeClass, unref, normalizeStyle, createElementVNode, renderSlot } from "vue";
import { useEventListener } from "@vueuse/core";
import "../style/anchor.css";
import { useGlobal } from "../../../utils/config.mjs";
import { debounce } from "../../../utils/tools.mjs";
import { isString } from "../../../utils/typescript.mjs";
import { animateScrollTo, elAnimation } from "../utils/scroll.mjs";
import { anchorProps, anchorEmits } from "./main.props.mjs";
import { ANCHOR_PROVIDE } from "./type.mjs";
const _sfc_main = /* @__PURE__ */ defineComponent({
...{
name: "CuAnchor"
},
__name: "main",
props: anchorProps,
emits: anchorEmits,
setup(__props, { emit: __emit }) {
const props = __props;
const emit = __emit;
const { globalSize } = useGlobal();
const currentHref = ref("");
const links = {};
const currentLinkEl = ref();
const containerEl = ref();
const anchorRef = ref();
var hashChangeFlag = false;
var rolling = false;
const debonceScroll = debounce(onScroll);
const anchorStyle = computed(() => {
return {
"--cu-anchor-line-weight": props.lineWeight ? props.lineWeight + "px" : void 0,
"--cu-anchor-color": props.color
};
});
const top = computed(() => {
var _a;
return ((_a = currentLinkEl.value) == null ? void 0 : _a.offsetTop) ?? 0;
});
const left = computed(() => {
var _a;
return ((_a = currentLinkEl.value) == null ? void 0 : _a.offsetLeft) ?? 0;
});
const lineStyle = computed(() => {
var _a;
if (props.direction === "vertical") {
return {
top: top.value + "px",
opacity: currentHref.value ? 1 : 0
};
} else if (props.direction === "horizontal") {
return {
left: left.value + 4 + "px",
width: ((_a = currentLinkEl.value) == null ? void 0 : _a.offsetWidth) - 8 + "px",
opacity: currentHref.value ? 1 : 0
};
} else {
return void 0;
}
});
function handleClick(href, animate) {
if (!href) return;
hashChangeFlag = true;
setCurrentAnchor(href);
scrollTo(href, animate);
}
function setCurrentAnchor(href) {
if (currentHref.value === href) return;
currentHref.value = href;
currentLinkEl.value = anchorRef.value.querySelector(`a[data-href="${href}"]`);
emit("change", href);
}
function onScroll() {
if (!rolling) {
let current = getCurrentAnchor();
currentHref.value = current;
currentLinkEl.value = anchorRef.value.querySelector(`a[data-href="${current}"]`);
}
rolling = false;
hashChangeFlag = false;
}
function getCurrentAnchor() {
if (!containerEl.value) return;
let eleTopList = Object.keys(links).map((v) => {
let ele = getElement(v);
if (ele) {
return {
href: v,
top: ele.getBoundingClientRect().top - getContainerOffsetTop()
};
}
}).filter(Boolean);
eleTopList.sort((prev, next) => prev.top - next.top);
for (let i = eleTopList.length - 1; i >= 0; i--) {
let item = eleTopList[i];
if (item.top - props.offset <= 0) {
return item.href;
}
}
return void 0;
}
let clearAnimate = null;
function scrollTo(id, animate) {
let el = document.querySelector(id);
if (el) {
if (clearAnimate) clearAnimate();
rolling = true;
let from = getContainerScrollTop();
let to = el.getBoundingClientRect().top - getContainerOffsetTop() + from - props.offset;
clearAnimate = animateScrollTo(containerEl.value, from, to, props.duration);
if (animate && props.anchorAnimation) {
elAnimation(el, props.duration);
}
}
}
function getContainerScrollTop() {
if (containerEl.value === window) {
return window.scrollY;
}
return containerEl.value.scrollTop;
}
function getElement(target) {
if (isString(target)) {
try {
return document.querySelector(target);
} catch {
return null;
}
}
return target;
}
function getContainer() {
const el = getElement(props.container);
if (!el || el === window) {
containerEl.value = window;
} else {
containerEl.value = el;
}
}
function getContainerOffsetTop() {
let pTop = 0;
try {
pTop = containerEl.value.getBoundingClientRect().top;
} catch {
}
return pTop;
}
function addLinkItem(item) {
if (!item.href) return;
links[item.href] = item;
}
function removeLinkItem(href) {
delete links[href];
if (currentHref.value === href) {
currentHref.value = "";
}
}
function hashChange() {
if (!hashChangeFlag) {
const hash = decodeURIComponent(window.location.hash);
handleClick(hash);
}
}
useEventListener(containerEl, "scroll", debonceScroll);
useEventListener(window, "hashchange", hashChange);
watch(
() => props.container,
() => {
getContainer();
}
);
onMounted(() => {
getContainer();
const hash = decodeURIComponent(window.location.hash);
let target = getElement(hash);
if (target) {
handleClick(hash);
} else {
onScroll();
}
});
provide(ANCHOR_PROVIDE, {
props,
currentHref,
addLinkItem,
removeLinkItem,
handleClick
});
return (_ctx, _cache) => {
return openBlock(), createElementBlock("ul", {
class: normalizeClass(["cu-anchor", [{ "show-line": _ctx.showLine }, _ctx.size ?? unref(globalSize), "cu-anchor--" + _ctx.direction]]),
ref_key: "anchorRef",
ref: anchorRef,
style: normalizeStyle(anchorStyle.value)
}, [
createElementVNode("span", {
class: "cu-anchor__line",
style: normalizeStyle(lineStyle.value)
}, null, 4),
renderSlot(_ctx.$slots, "default")
], 6);
};
}
});
export {
_sfc_main as default
};