游標小跟班
跟著游標跑的小跟班。(´ ・ω・`)ノ╰(・ิω・ิ )
TIP
此元件針對滑鼠設計,建議使用電腦或可以使用滑鼠的裝置瀏覽。
使用範例
基本用法
平時跟著游標跑,碰到特定元素會有特殊互動
查看範例原始碼
vue
<template>
<div class="w-full flex flex-col gap-4 border border-gray-300 p-6">
<cursor-sidekick v-if="enable" />
<base-checkbox
v-model="enable"
label="啟用小跟班"
class="border rounded bg-gray-100 p-4"
/>
<div class="flex flex-col gap-2">
<div class="text-2xl font-bold">
我是標題
</div>
<div class="">
我是一段文字(嘗試反白文字看看)
</div>
<hr>
<base-input
v-model="text"
label="文字輸入框"
/>
<div class="h-[20vh] w-1/2 border rounded">
<textarea
v-model="text"
placeholder="多行文字"
class="h-full w-full p-2"
/>
</div>
<div
contenteditable
class="w-2/3 border rounded p-2"
>
可編輯的 div
</div>
<hr>
<base-btn label="按鈕" />
<base-btn
disabled
label="不可以色色 (゚Д゚*)ノ"
/>
<hr>
<a
href="https://codlin.me/"
target="_blank"
>
鱈魚的魚缸
</a>
<img
src="/painting-codfish-bakery.webp"
alt="貪吃的鱈魚"
>
</div>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import BaseBtn from '../../base-btn.vue'
import BaseCheckbox from '../../base-checkbox.vue'
import BaseInput from '../../base-input.vue'
import CursorSidekick from '../cursor-sidekick.vue'
const enable = ref(false)
const text = ref('')
</script>
自定義內容
自行設計 Provider,產生各種奇奇怪怪的互動吧!ლ(´∀`ლ)
嘗試反白這段文字中有鱈魚的部分和沒有鱈魚的部分,看看有甚麼差別。
查看範例原始碼
vue
<template>
<div class="w-full flex flex-col gap-4 border border-gray-300 p-6">
<cursor-sidekick
v-if="enable"
color="#35abf0"
:hover-providers="hoverProviders"
:select-providers="selectProviders"
/>
<base-checkbox
v-model="enable"
label="啟用小跟班"
class="border rounded bg-gray-100 p-4"
/>
<div class="flex flex-col gap-2">
<div class="">
嘗試反白這段文字中有鱈魚的部分和沒有鱈魚的部分,看看有甚麼差別。
</div>
<hr>
<base-btn
disabled
label="不可以色色 ('◉◞⊖◟◉` )"
/>
<hr>
<img
src="/photography-ears-of-rice.jpg"
url="https://www.flickr.com/photos/coodfish/albums/"
>
</div>
</div>
</template>
<script setup lang="ts">
import type { ContentProvider } from '../use-content-provider'
import { ref } from 'vue'
import BaseBtn from '../../base-btn.vue'
import BaseCheckbox from '../../base-checkbox.vue'
import CursorSidekick from '../cursor-sidekick.vue'
const enable = ref(false)
const hoverProviders: ContentProvider[] = [
// hover 含有色色文字的按鈕時,提供色色傳送門
{
match(data) {
if ('rect' in data)
return false
if (
!(data instanceof HTMLButtonElement)
&& data?.getAttribute('role') !== 'button'
) {
return false
}
return data.innerHTML.includes('色色')
},
getContent: () => ({
btnList: [
{
label: '色色傳送門 (´,,•ω•,,)',
onClick() {
window.open(
'https://raw.githubusercontent.com/tpai/dogedeck/main/cards/%E6%8A%97%E8%89%B2%E8%89%B2%E8%97%A5.png',
'_blank',
)
},
},
],
}),
},
// 當圖片含有 url attr 時,提供開啟連結按鈕
{
match(data) {
if ('rect' in data)
return false
if (data instanceof HTMLImageElement) {
return true
}
return false
},
getContent(param) {
const { element } = param
const target = element?.value
if (!(target instanceof HTMLImageElement))
return
const url = target.getAttribute('url')
if (!url)
return
return {
btnList: [
{
label: '📷 查看更多精彩照片',
onClick() {
window.open(url ?? '', '_blank')
},
},
],
}
},
},
]
const selectProviders: ContentProvider[] = [
// 選取文字包含鱈魚時,提供額外選單
{
match(data) {
if (!('rect' in data))
return false
return data.text.includes('鱈魚')
},
getContent: () => ({
text: '被你發現惹 ᕕ( ゚ ∀。)ᕗ<br>歡迎來以下連結逛逛',
class: ' text-nowrap ',
btnList: [
{
label: '🎬 Youtube',
onClick() {
window.open('https://www.youtube.com/@codfish2140', '_blank')
},
},
{
label: '💡 CodePen',
onClick() {
window.open('https://codepen.io/Codfish2140', '_blank')
},
},
{
label: '✏️ 鱈魚的魚缸',
onClick() {
window.open('https://codlin.me/', '_blank')
},
},
],
}),
},
]
</script>
原理
這個小廢物元件應該是使用最多種瀏覽器 API 的元件了,以下是 API 與其應用範圍:
Document: activeElement property :取得目前 focus 的輸入框元素。
Document: elementFromPoint() method :取得目前被 hover 的元素。
Document: getSelection() method :取得選取文字與位置。
Element: getBoundingClientRect() method :取得目標元素尺寸與位置。
Web API: IntersectionObserver :判斷 tooltip 是否被遮擋。
小跟班的變形動畫是靠老朋友 anime.js 實現。
原始碼
API
Props
interface Props {
/** 單位 px */
size?: number;
/** \# 前綴之 HEX 格式
* @default '#515151'
*/
color?: string;
/** 最大速度。越慢小跟班越悠哉。單位 px/ms
* @default 1
*/
maxVelocity?: number;
/** @default 100 */
zIndex?: number;
/** 匹配 active element 的 provider。
*
* 通常用於可點擊或 focus 的元素。
*/
activeProviders?: ContentProvider[];
/** 匹配 hover element 的 provider
*
* 只要 hover 到符合條件的元素,即會觸發。
*/
hoverProviders?: ContentProvider[];
/** 匹配選取文字的 provider */
selectProviders?: ContentProvider[];
}
ContentProvider 定義如下:
interface BtnOption {
label: string;
onClick: (event: Event) => void;
}
interface Content {
/** 用於調整內容樣式 */
class?: string;
text?: string;
/** 按鈕清單 */
btnList?: BtnOption[];
}
export interface ContentProvider {
/** 判斷目前元素或文字是否符合 */
match: (
data: HTMLElement | SelectionState
) => boolean;
/** 取得小跟班顯示用內容 */
getContent: (
param: TargetParam
) => Content | undefined;
}