0
I’d like to use this effect on one of my projects.
const ui = {
btn: document.querySelector('.c-magnetic-btn')
}
const state = {
bounds: ui.btn.getBoundingClientRect(),
threshold: parseInt(ui.btn.dataset.threshold),
ratio: parseInt(ui.btn.dataset.ratio),
isMagnetic: false,
mouse: {
x: 0,
y: 0
},
ease: {
x: 0,
y: 0,
scale: 1,
value: ui.btn.dataset.ease
},
transform: {
x: 0,
y: 0,
scale: 1,
max: ui.btn.dataset.max
},
width: window.innerWidth,
height: window.innerHeight,
history: false,
scale: ui.btn.dataset.scale
}
const mouseMove = ({ pageX, pageY }) => {
Object.assign(state, {
mouse: {
x: pageX,
y: pageY
},
isMagnetic: isMagnetic(pageX, pageY)
})
}
const resize = () => {
Object.assign(state, {
bounds: ui.btn.getBoundingClientRect(),
width: window.innerWidth,
height: window.innerHeight
})
}
const isMagnetic = (x, y) => {
const { bounds } = state
const centerX = bounds.left + (bounds.width / 2)
const centerY = bounds.top + (bounds.height / 2)
// use pythagorean theorem to calculate
// cursor distance from center of btn
// a^2 + b^2 = c^2
const a = Math.abs(centerX - x)
const b = Math.abs(centerY - y)
const c = Math.sqrt(a * a + b * b)
// true if cursor distance from center of btn is
// equal to btn radius + threshold
const isHover = c < (bounds.width / 2) + state.threshold
if (!state.history && isHover) {
ui.btn.classList.add('is-hover')
Object.assign(state, {
threshold: state.threshold * state.ratio,
history: true
})
} else if (state.history && !isHover) {
ui.btn.classList.remove('is-hover')
Object.assign(state, {
threshold: state.threshold / state.ratio,
history: false
})
}
return isHover
}
const run = () => {
requestAnimationFrame(run)
const { isMagnetic, transform, mouse, width, height, ease, max, scale } = state
transform.x = isMagnetic ? (mouse.x - width / 2) / width * transform.max : 0
transform.y = isMagnetic ? (mouse.y - height / 2) / height * transform.max : 0
transform.scale = isMagnetic ? scale : 1
// basic linear interpolation
// https://www.youtube.com/watch?v=yWhgniVHROw
ease.x += (transform.x - ease.x) * ease.value
ease.y += (transform.y - ease.y) * ease.value
ease.scale += (transform.scale - ease.scale) * ease.value
Object.assign(ui.btn.style, {
transform: `
translate(
${ease.x.toFixed(2)}px,
${ease.y.toFixed(2)}px
)
translateZ(0)
scale(
${(ease.scale).toFixed(2)}
)`
})
}
const init = () => {
document.addEventListener('mousemove', mouseMove)
window.addEventListener('resize', resize)
run()
}
init()
* {
box-sizing: border-box;
}
html,
body {
height: 100%;
margin: 0;
}
body {
display: flex;
justify-content: center;
align-items: center;
}
button {
border: none;
margin: 0;
padding: 0;
width: auto;
overflow: visible;
background: transparent;
color: inherit;
font: inherit;
line-height: normal;
text-align: center;
text-decoration: none;
outline: none;
cursor: pointer;
white-space: normal;
-webkit-font-smoothing: inherit;
-moz-osx-font-smoothing: inherit;
-webkit-appearance: none;
}
.u-fit {
width: 100%;
height: 100%;
}
.u-relative {
position: relative;
}
.u-absolute {
position: absolute;
}
.u-absolute.u-pos-tl {
top: 0;
left: 0;
}
.u-flex {
display: flex;
}
.u-flex.u-center-xy {
justify-content: center;
align-items: center;
}
.c-magnetic-btn {
width: 120px;
height: 120px;
border-radius: 50%;
}
.o-circle {
display: inline-block;
background-color: rgba(0, 0, 0, 0.015);
border: 2px solid rgba(0, 0, 0, 0.5);
border-radius: 50%;
z-index: -1;
opacity: 0.2;
transition: opacity 0.6s cubic-bezier(0.23, 1, 0.32, 1);
will-change: opacity;
}
.t-btn-label {
display: inline-block;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
font-family: 'Karla', sans-serif;
font-weight: 700;
letter-spacing: 1px;
line-height: 1;
color: rgba(0, 0, 0, 0.5);
font-size: 13px;
}
.is-hover .o-circle {
opacity: 0.4;
}
<button class="c-magnetic-btn u-relative"
data-threshold="60"
data-ratio="3.5"
data-max="100"
data-scale="1.1"
data-ease="0.225">
<span class="t-btn-label u-flex u-center-xy">Hover me.</span>
<span class="o-circle u-fit u-absolute u-pos-tl"></span>
</button>
I don’t understand much of javascript, and the effect itself is all done with javascript, I would need help to use this effect on as many buttons/ elements as I want, I believe I have how to do this. I believe it is just some change in javascript, could help me in this?
Thanks Cristiano! Solve my problem!
– Lucas Granvilla