Skip to content
Welcome to vote for your favorite component! You can also tell me anything you want to say! (*´∀`)~♥

Roachie Wrapper wrapper

Two antennae peek out from under the wrapped element... ( ・ิω・ิ)

Usage Examples

Basic Usage

Get close and they'll hide.

View example source code
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>

How It Works

useElementBounding tracks the element's bounding box, and useMouse watches the cursor position.

On every mouse move, Math.atan2 calculates the angle from the element center → cursor, and the antennae emerge from the opposite direction (angle + π). A ray-rectangle intersection formula then finds the exact point on the element's edge. CSS rotate handles the direction and translateY slides the antenna in and out.

Distance from cursor to element border:

dx = max(left - mouseX, 0, mouseX - right)
dy = max(top - mouseY, 0, mouseY - bottom)
distance = sqrt(dx² + dy²)

When distance drops below hideDistance, the antenna snaps back; when the cursor moves away, it slides out again with a smooth animation.

Antenna Physics

At first the antennae were just two straight lines oscillating with a sine wave.

They looked like two iron rods swinging back and forth — nothing like a living creature (´・ω・`)

After some research, a double pendulum turned out to be a much better fit. The root segment actively oscillates, and the tip segment follows passively with physical inertia, giving the tip a natural delay and swing.


Codlin: "It actually looks like cockroach antennae now! ◝( •ω• )◟"

Passerby: "Did you really spend this much effort on this? ╭(°A ,°`)╮"


The root segment uses two sine waves at different frequencies to create irregular motion:

txt
θ = (sin(t × f₁ + φ₁) × A₁ + sin(t × f₂ + φ₂) × A₂) × ampMult

The two frequencies (1.4 and 3.1 rad/s) are not integer multiples of each other, so no repeating cycle appears — it looks genuinely alive rather than looping (`・ω・´)

The tip segment is a damped driven pendulum, driven by the root segment's angular acceleration (θ₁''):

txt
φ'' = -ωₙ² φ - 2ζωₙ φ' - θ₁''
  • φ is the tip's deflection angle relative to the root; φ = 0 means both segments are collinear (straight)
  • ωₙ = 2: natural frequency — higher means a stiffer, faster-reacting tip
  • ζ = 0.45: damping ratio — less than 1 is underdamped, so the tip overshoots and gradually settles

Velocity and angle are updated each frame with Euler integration:

txt
φ' += φ'' × dt
φ  += φ'  × dt

Source Code

API

Props

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

Slots

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

v0.60.0