Кнопочки с анимацией на css и js


Кнопка №1:


============================================

Кнопка №2:

Кнопка 2

============================================

Кнопка №3:

Click Me

Исполняемый код:

<div> <h2>Кнопка №1:</h2> <svg class="liquid-button" data-text="Yeah, I want that" data-force-factor="0.1" data-layer-1-viscosity="0.5" data-layer-2-viscosity="0.4" data-layer-1-mouse-force="400" data-layer-2-mouse-force="500" data-layer-1-force-limit="1" data-layer-2-force-limit="2" data-color1="#FFF500" data-color2="#20D8F9" data-color3="#20D8F9"> </svg> <style> .liquid-button margin: auto font-weight: bold path cursor: pointer </style> <script> const LiquidButton = class LiquidButton { constructor(svg) { const options = svg.dataset; this.id = this.constructor.id || (this.constructor.id = 1); this.constructor.id++; this.xmlns = "http://www.w3.org/2000/svg"; this.tension = options.tension * 1 || 0.4; this.width = options.width * 1 || 200; this.height = options.height * 1 || 50; this.margin = options.margin || 40; this.hoverFactor = options.hoverFactor || -0.1; this.gap = options.gap || 5; this.debug = options.debug || false; this.forceFactor = options.forceFactor || 0.2; this.color1 = options.color1 || "#36DFE7"; this.color2 = options.color2 || "#8F17E1"; this.color3 = options.color3 || "#BF09E6"; this.textColor = options.textColor || "#000000"; this.text = options.text || "Button"; this.svg = svg; this.layers = [ { points: [], viscosity: 0.5, mouseForce: 100, forceLimit: 2 }, { points: [], viscosity: 0.8, mouseForce: 150, forceLimit: 3 } ]; for (let layerIndex = 0; layerIndex < this.layers.length; layerIndex++) { const layer = this.layers[layerIndex]; layer.viscosity = options["layer-" + (layerIndex + 1) + "Viscosity"] * 1 || layer.viscosity; layer.mouseForce = options["layer-" + (layerIndex + 1) + "MouseForce"] * 1 || layer.mouseForce; layer.forceLimit = options["layer-" + (layerIndex + 1) + "ForceLimit"] * 1 || layer.forceLimit; layer.path = document.createElementNS(this.xmlns, "path"); this.svg.appendChild(layer.path); } this.wrapperElement = options.wrapperElement || document.body; if (!this.svg.parentElement) { this.wrapperElement.append(this.svg); } this.svgText = document.createElementNS(this.xmlns, "text"); this.svgText.setAttribute("x", "50%"); this.svgText.setAttribute("y", "50%"); this.svgText.setAttribute("dy", ~~(this.height / 8) + "px"); this.svgText.setAttribute("font-size", ~~(this.height / 3)); this.svgText.style.fontFamily = "sans-serif"; this.svgText.setAttribute("text-anchor", "middle"); this.svgText.setAttribute("pointer-events", "none"); this.svg.appendChild(this.svgText); this.svgDefs = document.createElementNS(this.xmlns, "defs"); this.svg.appendChild(this.svgDefs); this.touches = []; this.noise = options.noise || 0; document.body.addEventListener("touchstart", this.touchHandler); document.body.addEventListener("touchmove", this.touchHandler); document.body.addEventListener("touchend", this.clearHandler); document.body.addEventListener("touchcancel", this.clearHandler); this.svg.addEventListener("mousemove", this.mouseHandler); this.svg.addEventListener("mouseout", this.clearHandler); this.initOrigins(); this.animate(); } get mouseHandler() { return (e) => { this.touches = [ { x: e.offsetX, y: e.offsetY, force: 1 } ]; }; } get touchHandler() { return (e) => { this.touches = []; const rect = this.svg.getBoundingClientRect(); for ( let touchIndex = 0; touchIndex < e.changedTouches.length; touchIndex++ ) { const touch = e.changedTouches[touchIndex]; const x = touch.pageX - rect.left; const y = touch.pageY - rect.top; if (x > 0 && y > 0 && x < this.svgWidth && y < this.svgHeight) { this.touches.push({ x, y, force: touch.force || 1 }); } } e.preventDefault(); }; } get clearHandler() { return (e) => { this.touches = []; }; } get raf() { return ( this.__raf || (this.__raf = ( window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || function (callback) { setTimeout(callback, 10); } ).bind(window)) ); } distance(p1, p2) { return Math.sqrt(Math.pow(p1.x - p2.x, 2) + Math.pow(p1.y - p2.y, 2)); } update() { for (let layerIndex = 0; layerIndex < this.layers.length; layerIndex++) { const layer = this.layers[layerIndex]; const points = layer.points; for (let pointIndex = 0; pointIndex < points.length; pointIndex++) { const point = points[pointIndex]; const dx = point.ox - point.x + (Math.random() - 0.5) * this.noise; const dy = point.oy - point.y + (Math.random() - 0.5) * this.noise; const d = Math.sqrt(dx * dx + dy * dy); const f = d * this.forceFactor; point.vx += f * (dx / d || 0); point.vy += f * (dy / d || 0); for ( let touchIndex = 0; touchIndex < this.touches.length; touchIndex++ ) { const touch = this.touches[touchIndex]; let mouseForce = layer.mouseForce; if ( touch.x > this.margin && touch.x < this.margin + this.width && touch.y > this.margin && touch.y < this.margin + this.height ) { mouseForce *= -this.hoverFactor; } const mx = point.x - touch.x; const my = point.y - touch.y; const md = Math.sqrt(mx * mx + my * my); const mf = Math.max( -layer.forceLimit, Math.min(layer.forceLimit, (mouseForce * touch.force) / md) ); point.vx += mf * (mx / md || 0); point.vy += mf * (my / md || 0); } point.vx *= layer.viscosity; point.vy *= layer.viscosity; point.x += point.vx; point.y += point.vy; } for (let pointIndex = 0; pointIndex < points.length; pointIndex++) { const prev = points[(pointIndex + points.length - 1) % points.length]; const point = points[pointIndex]; const next = points[(pointIndex + points.length + 1) % points.length]; const dPrev = this.distance(point, prev); const dNext = this.distance(point, next); const line = { x: next.x - prev.x, y: next.y - prev.y }; const dLine = Math.sqrt(line.x * line.x + line.y * line.y); point.cPrev = { x: point.x - (line.x / dLine) * dPrev * this.tension, y: point.y - (line.y / dLine) * dPrev * this.tension }; point.cNext = { x: point.x + (line.x / dLine) * dNext * this.tension, y: point.y + (line.y / dLine) * dNext * this.tension }; } } } animate() { this.raf(() => { this.update(); this.draw(); this.animate(); }); } get svgWidth() { return this.width + this.margin * 2; } get svgHeight() { return this.height + this.margin * 2; } draw() { for (let layerIndex = 0; layerIndex < this.layers.length; layerIndex++) { const layer = this.layers[layerIndex]; if (layerIndex === 1) { if (this.touches.length > 0) { while (this.svgDefs.firstChild) { this.svgDefs.removeChild(this.svgDefs.firstChild); } for ( let touchIndex = 0; touchIndex < this.touches.length; touchIndex++ ) { const touch = this.touches[touchIndex]; const gradient = document.createElementNS( this.xmlns, "radialGradient" ); gradient.id = "liquid-gradient-" + this.id + "-" + touchIndex; const start = document.createElementNS(this.xmlns, "stop"); start.setAttribute("stop-color", this.color3); start.setAttribute("offset", "0%"); const stop = document.createElementNS(this.xmlns, "stop"); stop.setAttribute("stop-color", this.color2); stop.setAttribute("offset", "100%"); gradient.appendChild(start); gradient.appendChild(stop); this.svgDefs.appendChild(gradient); gradient.setAttribute("cx", touch.x / this.svgWidth); gradient.setAttribute("cy", touch.y / this.svgHeight); gradient.setAttribute("r", touch.force); layer.path.style.fill = "url(#" + gradient.id + ")"; } } else { layer.path.style.fill = this.color2; } } else { layer.path.style.fill = this.color1; } const points = layer.points; const commands = []; commands.push("M", points[0].x, points[0].y); for (let pointIndex = 1; pointIndex < points.length; pointIndex += 1) { commands.push( "C", points[(pointIndex + 0) % points.length].cNext.x, points[(pointIndex + 0) % points.length].cNext.y, points[(pointIndex + 1) % points.length].cPrev.x, points[(pointIndex + 1) % points.length].cPrev.y, points[(pointIndex + 1) % points.length].x, points[(pointIndex + 1) % points.length].y ); } commands.push("Z"); layer.path.setAttribute("d", commands.join(" ")); } this.svgText.textContent = this.text; this.svgText.style.fill = this.textColor; } createPoint(x, y) { return { x: x, y: y, ox: x, oy: y, vx: 0, vy: 0 }; } initOrigins() { this.svg.setAttribute("width", this.svgWidth); this.svg.setAttribute("height", this.svgHeight); for (let layerIndex = 0; layerIndex < this.layers.length; layerIndex++) { const layer = this.layers[layerIndex]; const points = []; for ( let x = ~~(this.height / 2); x < this.width - ~~(this.height / 2); x += this.gap ) { points.push(this.createPoint(x + this.margin, this.margin)); } for (let alpha = ~~(this.height * 1.25); alpha >= 0; alpha -= this.gap) { const angle = (Math.PI / ~~(this.height * 1.25)) * alpha; points.push( this.createPoint( (Math.sin(angle) * this.height) / 2 + this.margin + this.width - this.height / 2, (Math.cos(angle) * this.height) / 2 + this.margin + this.height / 2 ) ); } for ( let x = this.width - ~~(this.height / 2) - 1; x >= ~~(this.height / 2); x -= this.gap ) { points.push( this.createPoint(x + this.margin, this.margin + this.height) ); } for (let alpha = 0; alpha <= ~~(this.height * 1.25); alpha += this.gap) { const angle = (Math.PI / ~~(this.height * 1.25)) * alpha; points.push( this.createPoint( this.height - (Math.sin(angle) * this.height) / 2 + this.margin - this.height / 2, (Math.cos(angle) * this.height) / 2 + this.margin + this.height / 2 ) ); } layer.points = points; } } }; const redraw = () => { button.initOrigins(); }; const buttons = document.getElementsByClassName("liquid-button"); for (let buttonIndex = 0; buttonIndex < buttons.length; buttonIndex++) { const button = buttons[buttonIndex]; button.liquidButton = new LiquidButton(button); } </script> </div> <div><br>============================================<br></div> <div id="kn2"> <h2>Кнопка №2:</h2> <div class="okno-1">Кнопка 2</div> <style> .okno-1 {width:200px; height:40px; background: green; transition: all 0.2s ease-in-out; position: relative; overflow: hidden;} .okno-1:before { content: ""; background-color: rgba(255, 255, 255, 0.5); height: 250px; width: 40px; display: block; position: absolute; top: -38px; left: -50px; transform: skewX(-45deg) translateX(0); transition: none; animation: sheen 2s ease-in-out infinite; } @keyframes sheen { 0% { transform: skewX(-45deg) translate(-10%, 0); } 100% { transform: skewX(-45deg) translate(100%, 0); left:100%; } } </style> </div> <div><br>============================================<br></div> <div id="kn3"> <h2>Кнопка №3:</h2> <a href="#" class="knop3"><span>Click Me</span></a> <style> .knop3 { display: block; height: 50px; width: 186px; position: relative; overflow: hidden; text-decoration: none; text-transform: uppercase; letter-spacing: 2px; color: white; font-size: 14px; text-align: center; } .knop3:after { position: absolute; content: ""; display: inline-block; background: #3bade3; background: linear-gradient(45deg, #3bade3 0%, #576fe6 25%, #9844b7 51%, #ff357f 100%); height: 50px; width: 372px; z-index: -1; transform: translateX(-280px); transition: transform 400ms ease-in; } .knop3:hover:after { transform: translateX(-200px); } .knop3 span { position: relative; top: 16px; } </style> </div>