Skip to content
歡迎來票選你最喜歡的元件! 也可以告訴我任何你想說的話喔!(*´∀`)~♥

小強包裝器 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;
}

v0.60.0