Эластичный эффект для кнопки при наведении (CSS/JS)

Кнопка, при наведении на которую появляется эффект эластичности. Разберемся как можно реализовать такой эффект и использовать у себя на сайте.

Содержание
  1. HTML
  2. CSS (SCSS)
  3. JS

HTML

<a href="#" class="button">
	<canvas class="button__canvas"></canvas>
	<span class="button__text">Hover me I am liquid</span>
</a>

CSS (SCSS)

body {
  display: flex;
  justify-content: center;
  align-items: center;
  min-height: 100vh;
  background: #eef;
}
.button {
  position: relative;
  padding: 1.6em 2.8em;
  text-decoration: none;
  &__canvas {
    --offset: 32px;
    position: absolute;
    top: calc(var(--offset) * -1);
    left: calc(var(--offset) * -1);
    width: calc(100% + var(--offset) * 2);
    height: calc(100% + var(--offset) * 2);
    transition: opacity 2s ease;
  }
  &__text {
    position: relative;
    color: white;
    font-weight: bold;
    letter-spacing: .02em;
    pointer-events: none;
  }
  &:hover &__canvas {
    opacity: .85;
    transition: opacity .2s ease;
  }
  &:active &__canvas {
    opacity: 1;
    transition: none;
  }
}

JS

document.querySelectorAll('.button').forEach((elem) => {
	const canvas = elem.querySelector('.button__canvas')
	const ctx = canvas.getContext('2d')
	const offset = 32
	const background = '#eef'
	const foreground = '#7551e9'
	let points = []
	let position
	const distance = new Ola({
		value: offset
	})
	const size = () => {
		canvas.width = canvas.getBoundingClientRect().width
		canvas.height = canvas.getBoundingClientRect().height
		position = new Ola({
			x: canvas.width / 2,
			y: canvas.height / 2
		})
		const pixelsWidth = canvas.width - offset * 2
		const pixelsHeight = canvas.height - offset * 2
		const leftTop = [ offset, offset ]
		const rightTop = [ canvas.width - offset, offset ]
		const rightBottom = [ canvas.width - offset, canvas.height - offset ]
		const leftBottom = [ offset, canvas.height - offset ]
		points = []
		Array(pixelsWidth).fill().forEach((_, index) => {
			points.push([
				leftTop[0] + index,
				leftTop[1]
			])
		})
		Array(pixelsHeight).fill().forEach((_, index) => {
			points.push([
				rightTop[0],
				rightTop[1] + index
			])
		})
		Array(pixelsWidth).fill().forEach((_, index) => {
			points.push([
				rightBottom[0] - index,
				rightBottom[1]
			])
		})
		Array(pixelsHeight).fill().forEach((_, index) => {
			points.push([
				leftBottom[0],
				leftBottom[1] - index
			])
		})
	}
	size()
	const reset = () => {
		ctx.fillStyle = background
		ctx.fillRect(0, 0, canvas.width, canvas.height)
	}
	const draw = () => {
		ctx.fillStyle = foreground
		ctx.beginPath()
		points.forEach((point, index) => {
			const [ vx, vy ] = attract(point)
			if (index === 0) ctx.moveTo(vx, vy)
			else ctx.lineTo(vx, vy)
		})
		ctx.closePath()
		ctx.fill()
	}
	const attract = (point) => {
		const [ x, y ] = point
		const dx = x - position.x
		const dy = y - position.y
		const dist = Math.sqrt(dx * dx + dy * dy)
		const dist2 = Math.max(1, dist)
		const d = Math.min(dist2, Math.max(12, (dist2 / 4) - dist2))
		const D = dist2 * distance.value
		return [
			x + (d / D) * (position.x - x),
			y + (d / D) * (position.y - y)
		]
	}
	const loop = () => {
		reset()
		draw()
		requestAnimationFrame(loop)
	}
	window.onresize = size
	canvas.onmousemove = (e) => {
		position.set({
			x: e.clientX - e.target.getBoundingClientRect().left,
			y: e.clientY - e.target.getBoundingClientRect().top
		}, 200)
	}
	canvas.onmouseenter = () => {
		distance.set({
			value: 1
		}, 200)
	}
	canvas.onmouseleave = () => {
		position.set({
			x: canvas.width / 2,
			y: canvas.height / 2
		}, 2000)
		distance.set({
			value: offset
		}, 12000)
	}
	loop()
})

Источник
Оцените статью
( 1 оценка, среднее 5 из 5 )
Поделишься в соц. сетях? 🙂 Спасибо 🙏
Devtutor
Добавить комментарий