求關注的按鈕 button
很會刷存在感的按鈕!◝( ゚ ∀。)◟
技術關鍵字
| 名稱 | 描述 |
|---|---|
| Pointer 事件 | 偵測滑鼠或觸控點移動、點擊、懸停等等事件,取得座標、目標等等資訊 |
| CSS 動畫 | 基於 CSS transition 和 animation 實現 |
| JS 動畫 | 基於 JavaScript 實現的動畫,達成更複雜、精準的動畫控制,常見套件有 GSAP、anime.js 等 |
| Anime.js | 輕量級 JavaScript 動畫函式庫 |
使用範例
基本用法
滑鼠靠近按鈕時會跑過來,若不在畫面內則會擠到邊緣湊熱鬧。
鱈魚 冷凍櫃長期住民
歡迎來到全球(自稱)最專業的鱈魚介紹網站。這裡沒有網美開箱,只有一條條 正在思考「自己適合被煎、被炸,還是被清蒸」的人生規劃中的鱈魚。
鱈魚,海洋界的鹹水奇才,上岸後搖身一變成炸魚薯條裡的夢幻男一號。 牠的夢想很單純——有朝一日能被做成外酥內嫩、筋道不柴的鱈魚排, 而不是永遠躺在冷凍櫃裡,當你逛賣場時視而不見的背景板。
身分證資料
學名:Gadus morhua
暱稱:鱈魚、炸魚薯條主角
居住地:冷水海域 & 冷凍櫃
個性特徵
- 味道溫柔,不搶戲
- 肉質細緻,沒刺好相處
- 適合配任何醬,交際手腕一流
人生目標
- 當一份被拍照打卡的主餐
- 進營養師的推薦清單
- 不要再被誤認成多利魚
查看範例原始碼
vue
<template>
<div class="w-full flex flex-col gap-4 border border-gray-200 rounded-xl p-6 shadow-sm">
<div class="flex flex-col gap-4">
<div class="flex items-center gap-2 text-xl font-bold">
<span>鱈魚</span>
<span class="border border-sky-100 rounded-full px-2 py-0.5 text-sm text-sky-600 font-normal">
冷凍櫃長期住民
</span>
</div>
<div class="opacity-80">
歡迎來到全球(自稱)最專業的鱈魚介紹網站。這裡沒有網美開箱,只有一條條
正在思考「自己適合被煎、被炸,還是被清蒸」的人生規劃中的鱈魚。
</div>
<div class="py-6 leading-relaxed opacity-80">
鱈魚,海洋界的鹹水奇才,上岸後搖身一變成炸魚薯條裡的夢幻男一號。
牠的夢想很單純——有朝一日能被做成<b>外酥內嫩、筋道不柴的鱈魚排</b>,
而不是永遠躺在冷凍櫃裡,當你逛賣場時視而不見的背景板。
</div>
<div class="grid grid-cols-1 gap-4 text-sm md:grid-cols-3">
<div class="border border-slate-100 rounded-lg p-4">
<div class="text-xs font-semibold opacity-80">
身分證資料
</div>
<div class="mt-1">
<div>學名:<span class="text-xs font-mono">Gadus morhua</span></div>
<div>暱稱:鱈魚、炸魚薯條主角</div>
<div>居住地:冷水海域 & 冷凍櫃</div>
</div>
</div>
<div class="border border-slate-100 rounded-lg p-4">
<div class="text-xs font-semibold opacity-80">
個性特徵
</div>
<ul class="mt-1 list-disc list-inside space-y-1">
<li>味道溫柔,不搶戲</li>
<li>肉質細緻,沒刺好相處</li>
<li>適合配任何醬,交際手腕一流</li>
</ul>
</div>
<div class="border border-slate-100 rounded-lg p-4">
<div class="text-xs font-semibold opacity-80">
人生目標
</div>
<ul class="mt-1 list-disc list-inside space-y-1">
<li>當一份被拍照打卡的主餐</li>
<li>進營養師的推薦清單</li>
<li>不要再被誤認成多利魚</li>
</ul>
</div>
</div>
</div>
<div class="mt-4 flex justify-center">
<btn-attention-seeker
label="點我購買"
:top-offset="topOffset"
z-index="19"
@click="handleClick"
/>
</div>
</div>
</template>
<script setup lang="ts">
import { useElementSize, useWindowSize } from '@vueuse/core'
import { computed, onMounted, reactive, ref } from 'vue'
import BtnAttentionSeeker from '../btn-attention-seeker.vue'
const windowSize = reactive(useWindowSize())
const navRef = ref<HTMLHeadElement>()
const localNavRef = ref<HTMLDivElement>()
onMounted(() => {
navRef.value = document.querySelector<HTMLHeadElement>('.VPNav') ?? undefined
localNavRef.value = document.querySelector<HTMLDivElement>('.VPLocalNav') ?? undefined
})
const navEl = reactive(useElementSize(navRef))
const localNavEl = reactive(useElementSize(localNavRef))
const topOffset = computed(() => {
if (windowSize.width < 960) {
return localNavEl.height ?? 0
}
return (navEl.height ?? 0) + (localNavEl.height ?? 0)
})
function handleClick() {
window.open('https://codlin.me', '_blank')
}
</script>原理
按鈕移動容器(carrierRef)會隨著滑鼠位置移動,並且在視窗邊界時會擠到邊緣湊熱鬧。
原始碼
API
Props
interface Info {
width: number;
height: number;
x: number;
y: number;
}
interface Props {
/** 按鈕內文字 */
label?: string;
/** 是否停用 */
disabled?: boolean;
/** 同 CSS z-index */
zIndex?: number | string;
/** 跟隨距離,當距離小於此值時開始跟隨 */
followDistance?: number | ((info: Info) => number);
/** 上方偏移量,避免被 header 遮擋 */
topOffset?: number;
/** 下方偏移量,避免被 footer 遮擋 */
bottomOffset?: number;
}Slots
defineSlots<{
/** 按鈕 */
default?: () => unknown;
}>()