Skip to content

每條游來游去的小魚,都是正在瀏覽這一頁的訪客,有一條是你自己 ( ´ ▽ ` )ノ 覺得干擾閱讀的話,可以把小魚關掉。

小強包裝器 wrapper ​

被包裹的元素下方會探出兩根觸鬚... ( ・ิω・ิ)

技術關鍵字 ​

名稱描述
SVG可在 DOM 中使用的圖形格式,適合複雜圖形和動畫,配合 Vue v-bind 可以實現基於資料的動態效果
Pointer 事件偵測滑鼠或觸控點移動、點擊、懸停等等事件,取得座標、目標等等資訊
向量計算處理方向、加速度、速度等等數學運算
CSS 動畫基於 CSS transition 和 animation 實現
彈簧阻尼系統以勁度與阻尼常數模擬彈簧的震盪與回彈,常用於自然的 UI 動態回饋
JS 動畫基於 JavaScript 實現的動畫,達成更複雜、精準的動畫控制,常見套件有 GSAP、anime.js 等

使用範例 ​

基本用法 ​

靠近就會躲起來

查看範例原始碼
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">
          {{ t('buyNow') }}
        </span>
      </base-btn>
    </wrapper-roachie>
  </div>
</template>

<script setup lang="ts">
import { useI18n } from 'vue-i18n'
import BaseBtn from '../../base-btn.vue'
import WrapperRoachie from '../wrapper-roachie.vue'

const { t } = useI18n()

function handleBuy() {
  window.open('https://codlin.me', '_blank')
}
</script>

原理 ​

以 useElementBounding 取得元素邊界,再以 useMouse 偵測滑鼠位置。

座標與角度計算 ​

  1. 中心點定位:計算元素中心相對於視窗的座標。
  2. 角度計算:以 Math.atan2 計算元素中心 → 滑鼠的角度 θmouse 。
  3. 反向探出:觸鬚設定在反方向( θmouse+π )探出。
  4. 邊緣交點:透過射線與矩形邊界的交點公式,算出觸鬚在元素邊緣的精確位置。這能確保觸鬚無論從哪個角度探動,根部都能完美貼合元素邊緣。

距離與隱藏邏輯 ​

計算滑鼠與元素邊框最近點的歐幾里得距離(Euclidean distance):

ts
// 簡化後的計算邏輯
const dx = Math.max(left - mouseX, 0, mouseX - right)
const dy = Math.max(top - mouseY, 0, mouseY - bottom)
const distance = Math.sqrt(dx * dx + dy * dy)

當距離小於 hideDistance 時,isHiding 變為 true,觸鬚收回。

觸控體驗 ​

在觸控裝置上,手指離開後 useMouse 的座標通常會停留在最後的位置。若該位置就在元素旁,觸鬚將永遠縮回。

為了解決此問題,加入了 Touch Idle 機制:

  • 偵測 touchstart 時重置閒置狀態。
  • 偵測 touchend 後啟動 2 秒的 debounce 計時器。
  • 若 2 秒內無新觸控,則強制進入閒置狀態,讓 isHiding 失效並重新彈出觸鬚。

響應式縮放 ​

觸鬚尺寸會根據包裹元素的最短邊自動縮放(基準為 80px),確保在大按鈕或小標籤上都能維持視覺和諧:

ts
antennaScale = max(0.5, min(width, height) / 80)

觸鬚物理 ​

一開始觸鬚只是兩條直線,用正弦波讓它左右搖擺。

看起來像兩根鐵棒在那邊晃,完全不像生物 (´・ω・`)

研究了一下,在擺動角度不大時,用雙擺模擬會更像。

根段主動搖擺,末段帶有物理慣性被動跟隨,這樣尖端就會有自然的延遲與甩動感。


鱈魚:「看起來更像小強觸鬚了!◝( •ω• )◟」

路人:「可以不要這麼專業的做這種鬼東西嗎?╭(°A ,°`)╮」


這部分實作於 roach-antenna.vue 中。

1. 根段 (Root Segment) ​

根段用兩個不同頻率的正弦波疊加,製造不規則的擺動:

ts
θ = (sin(t × 1.4 + φ₁) × 0.05 + sin(t × 3.1 + φ₂) × 0.02) × ampMult

使用 1.4 rad/s 與 3.1 rad/s 兩個不成整數比的頻率,能避免出現明顯的規律週期,讓動態更像真實生物。

2. 末段 (Tip Segment) ​

末段是阻尼驅動擺,由根段的角加速度( θroot″ )作為驅動力:

txt
φ'' = -ωₙ² φ - 2ζωₙ φ' - θ_{root}''
  • 自然頻率 ( ωn=2 ):決定末端的硬度與反應速度。
  • 阻尼比 ( ζ=0.45 ):欠阻尼狀態,使末端甩動後具有自然的收斂感。

原始碼 ​

API ​

Props ​

interface Props {
  /** 觸發觸鬚縮回的距離,單位 px。@default 150 */
  hideDistance?: number;
}

Slots ​

interface Slots {
  default?: () => unknown;
}

v0.93.1