@hardanonymous/marquee-selector
v0.0.6
Published
A framework-agnostic marquee selection library with drag & text selection protection
Maintainers
Readme
Marquee Selector
🎮 Demo
體驗線上 Demo:Marquee Selector Demo
✨ 特性
- 🎯 框架無關:純 Vanilla JavaScript 實現,可在任何前端框架中使用
- 🎨 多目標支援:可同時追蹤多組不同的選取目標,各有獨立的回調
- 🛡️ 智能保護機制:
- 自動保護可拖拽元素(
draggable="true") - 保護文字選取區域(input、textarea、contenteditable 等)
- 自動保護可拖拽元素(
- 🔄 完整生命週期:提供
onSelectionStart、onSelectionChange、onSelectionEnd、onClearClick回調 - 📍 精確碰撞檢測:支援滾動容器,使用 AABB 算法進行精確的碰撞檢測
- 💾 記憶體優化:使用 WeakMap 快取選取狀態,避免記憶體洩漏
- 🎨 CSS 可定制:通過 CSS 變數輕鬆定制外觀
📦 安裝
npm install @hardanonymous/marquee-selector或使用 yarn:
yarn add @hardanonymous/marquee-selector🚀 快速開始
import { MarqueeSelector } from "@hardanonymous/marquee-selector";
import "@hardanonymous/marquee-selector/style.css";
// 1. 初始化
const marquee = new MarqueeSelector({
container: document.body,
});
// 2. 添加目標配置
marquee.addTarget({
selector: ".item",
onSelectionChange: (elements) => {
console.log("選取變化:", elements);
elements.forEach((el) => el.classList.add("selected"));
},
onClearClick: (elements) => {
console.log("清除選取");
elements.forEach((el) => el.classList.remove("selected"));
},
});
// 3. 啟用框選
marquee.enable();📚 使用範例
Vue 3
<script setup lang="ts">
import { ref, onMounted, onUnmounted } from "vue";
import { MarqueeSelector } from "@hardanonymous/marquee-selector";
import "@hardanonymous/marquee-selector/style.css";
const containerRef = ref<HTMLElement>();
const selectedIds = ref<number[]>([]);
let marquee: MarqueeSelector | null = null;
onMounted(() => {
if (containerRef.value) {
marquee = new MarqueeSelector({
container: containerRef.value,
});
marquee.addTarget({
selector: ".item",
onSelectionChange: (elements) => {
selectedIds.value = elements.map((el) =>
Number(el.getAttribute("data-id"))
);
},
});
marquee.enable();
}
});
onUnmounted(() => {
marquee?.destroy();
});
</script>
<template>
<div ref="containerRef">
<div
v-for="item in items"
:key="item.id"
:data-id="item.id"
class="item"
:class="{ selected: selectedIds.includes(item.id) }"
>
{{ item.name }}
</div>
</div>
</template>React
import { useEffect, useRef, useState } from "react";
import { MarqueeSelector } from "@hardanonymous/marquee-selector";
import "@hardanonymous/marquee-selector/style.css";
function App() {
const containerRef = useRef<HTMLDivElement>(null);
const [selectedIds, setSelectedIds] = useState<number[]>([]);
useEffect(() => {
if (!containerRef.current) return;
const marquee = new MarqueeSelector({
container: containerRef.current,
});
marquee.addTarget({
selector: ".item",
onSelectionChange: (elements) => {
const ids = elements.map((el) => Number(el.getAttribute("data-id")));
setSelectedIds(ids);
},
});
marquee.enable();
return () => marquee.destroy();
}, []);
return (
<div ref={containerRef}>
{items.map((item) => (
<div
key={item.id}
data-id={item.id}
className={`item ${selectedIds.includes(item.id) ? "selected" : ""}`}
>
{item.name}
</div>
))}
</div>
);
}📖 完整文檔
查看 完整文檔 了解:
- 詳細的 API 參考
- 進階使用方式
- 與拖拽功能整合
- 工作原理說明
- 效能優化建議
- 常見問題解答
🎨 自定義樣式
通過 CSS 變數自定義框選框外觀:
:root {
--marquee-border-width: 2px;
--marquee-border-style: dashed;
--marquee-border-color: #ff0000;
--marquee-bg-color: rgba(255, 0, 0, 0.15);
--marquee-z-index: 10000;
}🤝 貢獻
歡迎提交 Issue 和 Pull Request!
📄 授權
MIT License - 詳見 LICENSE 文件
