@phucbm/magnetic-button
v1.1.0
Published
A lightweight TypeScript library that creates smooth magnetic attraction effects for HTML elements
Maintainers
Readme
🧲 Magnetic Button
A lightweight TypeScript library that creates smooth magnetic attraction effects for HTML elements. Elements with
the data-magnetic attribute will gently follow the mouse cursor when it's nearby, creating an engaging hover
interaction.
✨ Features
- Zero dependencies - Lightweight and fast
- TypeScript support - Full type definitions included
- Auto-initialization - Works with data attributes out of the box
- Customizable - Fine-tune distance, attraction, and animation speed
- Smooth animations - Uses linear interpolation for buttery smooth movement
- Touch device detection - Automatically disabled on touch devices by default
- Event callbacks - Hook into enter, exit, and update events
- API methods - Programmatically control instances with
destroy()andgetMagnetizedArea() - Modern browsers - Works in all modern browsers that support ES2022
🚀 Demo
Check out the live demo to see the magnetic effect in action!
📦 Installation
npm
npm install @phucbm/magnetic-buttonpnpm
pnpm add @phucbm/magnetic-buttonyarn
yarn add @phucbm/magnetic-buttonCDN
<script type="module">
import {MagneticButton} from 'https://unpkg.com/@phucbm/magnetic-button/dist/index.js'
new MagneticButton()
</script>
<script src="https://unpkg.com/@phucbm/magnetic-button/dist/magnetic-button.js"></script>
<script>
// MagneticButton is available on the window object
const magnetic = new window.MagneticButton()
</script>
🎯 Quick Start
HTML + Data Attributes (Easiest)
<!DOCTYPE html>
<html>
<head>
<style>
.magnetic-btn {
padding:1rem 2rem;
border:2px solid #333;
background:#1a1a1a;
color:white;
border-radius:8px;
cursor:pointer;
transition:border-color 0.3s ease;
}
.magnetic-btn.magnetizing {
border-color:#ff6b6b;
box-shadow:0 0 20px rgba(255, 107, 107, 0.3);
}
</style>
</head>
<body>
<!-- Basic usage -->
<button class="magnetic-btn" data-magnetic>
Hover me!
</button>
<!-- With custom parameters -->
<button
class="magnetic-btn"
data-magnetic
data-distance="150"
data-attraction="0.3"
data-speed="0.2">
Custom magnetic button
</button>
<script type="module">
import {MagneticButton} from '@phucbm/magnetic-button'
new MagneticButton() // Auto-initializes all elements with data-magnetic
</script>
</body>
</html>JavaScript/TypeScript
import {MagneticButton} from '@phucbm/magnetic-button'
// Auto-initialize all elements with data-magnetic attribute
new MagneticButton()
// Or target a specific element
const button = document.querySelector('.my-button')
const instance = new MagneticButton(button, {
distance: 200,
attraction: 0.5,
speed: 0.1,
disableOnTouch: true,
onEnter: (data) => console.log('Magnetized!', data),
onExit: (data) => console.log('Released!', data)
})
// Get magnetized area dimensions
const area = instance.getMagnetizedArea()
console.log(area) // { width: 300, height: 200 }
// Destroy instance and clean up
instance.destroy()⚙️ Configuration Options
Data Attributes
Add these attributes to your HTML elements to customize the magnetic effect:
| Attribute | Type | Default | Description |
|-------------------|----------|---------|-------------------------------------------------------------|
| data-magnetic | - | - | Required. Enables magnetic effect on the element |
| data-distance | number | 50 | Range from element edges where effect is active (pixels) |
| data-attraction | number | 0.3 | Strength of magnetic pull (0 = weak, 1 = strong) |
| data-speed | number | 0.1 | Speed of magnetic movement (0 = slow, 1 = instant) |
| data-max-x | number | - | Maximum horizontal movement in pixels (optional constraint) |
| data-max-y | number | - | Maximum vertical movement in pixels (optional constraint) |
JavaScript Options
When initializing with JavaScript, you can pass these options:
| Option | Type | Default | Description |
|------------------|------------|-----------------|----------------------------------------------------------------|
| activeClass | string | 'magnetizing' | CSS class added when magnetic effect is active |
| attraction | number | 0.3 | Strength of magnetic pull (0 = weak, 1 = strong) |
| distance | number | 50 | Range from element edges where effect is active (pixels) |
| speed | number | 0.1 | Speed of magnetic movement (0 = slow, 1 = instant) |
| maxX | number | - | Maximum horizontal movement in pixels (optional constraint) |
| maxY | number | - | Maximum vertical movement in pixels (optional constraint) |
| disableOnTouch | boolean | true | Disable magnetic effect on touch devices |
| onEnter | function | () => {} | Callback fired when mouse enters magnetic area |
| onExit | function | () => {} | Callback fired when mouse exits magnetic area |
| onUpdate | function | () => {} | Callback fired continuously while in magnetic area |
🎭 CSS Classes
The library automatically adds CSS classes that you can style:
| Class | When Applied | Description |
|------------------|--------------|-----------------------------------------------------------------------------------|
| .is-magnetized | Always | Added to all magnetic elements for identification |
| .magnetizing | On hover | Added when mouse is within magnetic range (customizable via activeClass option) |
📋 API Reference
Constructor
new MagneticButton(target ? : HTMLElement | null, options ? : MagneticButtonOptions)Parameters:
target- The HTML element to apply magnetic effect to. Ifnullor omitted, auto-initializes all elements withdata-magneticattributeoptions- Configuration options (see table above)
Methods
getMagnetizedArea()
Returns the total magnetized area dimensions including the distance parameter.
const area = instance.getMagnetizedArea()
console.log(area) // { width: 300, height: 250 }Returns: { width: number, height: number }
- For an element with dimensions 100×50px and distance=100px
- Total magnetized area will be: (100 + 100×2) × (50 + 100×2) = 300×250px
destroy()
Destroys the magnetic button instance and cleans up all event listeners.
instance.destroy()This method:
- Removes event listeners
- Cleans up CSS classes
- Resets element transform
- Removes instance from tracking
Event Data
Callback functions receive a MagneticData object with the following properties:
| Property | Type | Description |
|------------|----------|--------------------------------------------------|
| deltaX | number | Horizontal offset from element center |
| deltaY | number | Vertical offset from element center |
| distance | number | Distance from mouse to nearest element edge |
🎨 Examples
Strong Attraction
<button data-magnetic data-distance="120" data-attraction="0.2">
Strong Pull
</button>Subtle Effect
<button data-magnetic data-distance="80" data-attraction="0.8" data-speed="0.05">
Subtle Movement
</button>Large Detection Area
<button data-magnetic data-distance="200" data-attraction="0.4">
Wide Range
</button>With Event Callbacks
const instance = new MagneticButton(document.querySelector('.special-btn'), {
distance: 150,
attraction: 0.3,
onEnter: (data) => {
console.log('Entered magnetic field!')
// Play sound, trigger animation, etc.
},
onExit: (data) => {
console.log('Left magnetic field!')
// Reset state, stop animation, etc.
},
onUpdate: (data) => {
// Update UI based on mouse position
if (data.distance < 50) {
console.log('Very close to button!')
}
}
})Enable on Touch Devices
By default, the magnetic effect is disabled on touch devices. To enable it:
new MagneticButton(element, {
disableOnTouch: false // Enable on touch devices
})🎯 Tips and Best Practices
Performance
- The library is optimized and uses linear interpolation for smooth animations
- Avoid initializing too many magnetic elements simultaneously (recommended max: ~20-30)
- The effect is automatically disabled on touch devices by default for better performance
Distance Calculation
The distance parameter defines the range from the element's edges (not center):
- Element size: 100px × 50px
- Distance: 100px
- Total magnetized area: 300px × 250px (element + distance on all sides)
CSS Styling
/* Smooth transitions for non-magnetic properties */
.magnetic-btn {
transition:border-color 0.3s ease, box-shadow 0.3s ease;
}
/* Active state styling */
.magnetic-btn.magnetizing {
border-color:#ff6b6b;
box-shadow:0 0 20px rgba(255, 107, 107, 0.3);
}
/* Important: Avoid adding transition to transform */
.magnetic-btn {
/* DON'T: transition: transform 0.3s; */
/* This will conflict with the magnetic effect */
}Best Practice: Wrap Elements
For best results, wrap your buttons in a container and apply the magnetic effect to the wrapper:
<div class="magnetic-wrapper" data-magnetic>
<button class="my-button">
Hover Me
</button>
</div>This prevents CSS transitions on the button from interfering with the magnetic transform.
Accessibility
The magnetic effect doesn't interfere with keyboard navigation or screen readers, making it accessible by default. It's also automatically disabled on touch devices where the hover effect wouldn't work properly.
Framework Integration
React:
import {useEffect, useRef} from 'react'
import {MagneticButton} from '@phucbm/magnetic-button'
function MyButton(){
const buttonRef = useRef(null)
const instanceRef = useRef(null)
useEffect(() => {
instanceRef.current = new MagneticButton(buttonRef.current, {
distance: 150,
attraction: 0.3
})
// Cleanup on unmount
return () => {
instanceRef.current?.destroy()
}
}, [])
return <button ref={buttonRef}>Magnetic Button</button>
}Vue:
<template>
<button ref="buttonRef">Magnetic Button</button>
</template>
<script setup>
import {ref, onMounted, onUnmounted} from 'vue'
import {MagneticButton} from '@phucbm/magnetic-button'
const buttonRef = ref(null)
let instance = null
onMounted(() => {
instance = new MagneticButton(buttonRef.value, {
distance: 150,
attraction: 0.3
})
})
onUnmounted(() => {
instance?.destroy()
})
</script>🌐 Browser Support
- Chrome 63+
- Firefox 61+
- Safari 13.1+
- Edge 79+
📄 License
MIT © phucbm
🤝 Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
