小強包裝器 wrapper
被包裹的元素下方會探出兩根觸鬚... ( ・ิω・ิ)
技術關鍵字
| 名稱 | 描述 |
|---|---|
| SVG | 可在 DOM 中使用的圖形格式,適合複雜圖形和動畫,配合 Vue v-bind 可以實現基於資料的動態效果 |
| Pointer 事件 | 偵測滑鼠或觸控點移動、點擊、懸停等等事件,取得座標、目標等等資訊 |
| 向量計算 | 處理方向、加速度、速度等等數學運算 |
| CSS 動畫 | 基於 CSS transition 和 animation 實現 |
使用範例
基本用法
靠近就會躲起來
查看範例原始碼
vue
<template>
<div class="flex flex-col items-center gap-8 py-10">
<wrapper-roachie>
<base-btn
class="!border-orange-300 !rounded-2xl !from-orange-400 !to-red-500 !bg-gradient-to-br !px-14 !py-6 !text-white !shadow-lg !shadow-orange-300/40 dark:!border-orange-500 dark:!shadow-orange-900/40"
@click="handleBuy"
>
<span class="flex items-center gap-2 text-lg font-bold tracking-wide">
立即購買!
</span>
</base-btn>
</wrapper-roachie>
</div>
</template>
<script setup lang="ts">
import BaseBtn from '../../base-btn.vue'
import WrapperRoachie from '../wrapper-roachie.vue'
function handleBuy() {
window.open('https://codlin.me', '_blank')
}
</script>原理
以 useElementBounding 取得元素邊界,再以 useMouse 監聽滑鼠位置。
每次滑鼠移動時,用 Math.atan2 計算元素中心 → 滑鼠的角度,觸鬚則從反方向(angle + π)探出。接著以射線與矩形邊界的交點公式,找出觸鬚在元素邊緣的精確位置,最後以 CSS rotate 轉向、translateY 縮回。
滑鼠與元素邊框距離的計算:
dx = max(left - mouseX, 0, mouseX - right)
dy = max(top - mouseY, 0, mouseY - bottom)
distance = sqrt(dx² + dy²)距離小於 hideDistance 時,觸鬚立即縮回(transition: none);移開後以彈性動畫重新探出。
觸鬚物理
一開始觸鬚只是兩條直線,用正弦波讓它左右搖擺。
看起來像兩根鐵棒在那邊晃,完全不像生物 (´・ω・`)
研究了一下改成雙擺會更像,根段主動搖擺,末段帶有物理慣性被動跟隨,這樣尖端就會有自然的延遲與甩動感。
鱈魚:「看起來更像小強觸鬚了!◝( •ω• )◟」
路人:「可以不要這麼專業的做這種鬼東西嗎?╭(°A ,°`)╮」
根段用兩個不同頻率的正弦波疊加,製造不規則的擺動:
txt
θ = (sin(t × f₁ + φ₁) × A₁ + sin(t × f₂ + φ₂) × A₂) × ampMult兩個頻率(1.4 和 3.1 rad/s)不成整數比,所以不會出現規律的重複週期,看起來比較像真的在動、不像在跑動畫 (`・ω・´)
末段是一個阻尼驅動擺,由根段的角加速度(θ₁'')作為驅動力:
txt
φ'' = -ωₙ² φ - 2ζωₙ φ' - θ₁''φ是末段相對根段的偏轉角,φ = 0時兩段共線(伸直狀態)ωₙ = 2:自然頻率,越大末端越硬、反應越快ζ = 0.45:阻尼比,小於 1 是欠阻尼,末端甩出去之後會慢慢收斂回來
每幀用 Euler 積分更新速度與角度:
txt
φ' += φ'' × dt
φ += φ' × dt原始碼
API
Props
interface Props {
/** 觸發觸鬚縮回的距離,單位 px。@default 150 */
hideDistance?: number;
}Slots
interface Slots {
default?: () => unknown;
}