Files
react-animated-menus/src/components/Menu.js

258 lines
6.3 KiB
JavaScript

import React, { Component } from 'react';
import anime from 'animejs'
/*
* css
*/
import '../styles/Menu.css'
class App extends Component {
constructor(props) {
super(props);
/*
* Expected props:
* - width: Int in percentage
* - OR height: Int in percentage
* - color: String
* - food = {
* a: {
* value: "Button1",
* action: () => {
* alert("test1")
* }
* },
* b: {
* value: "Button2",
* action: () => {
* alert("test2")
* }
* },
* c: {
* value: "Button3",
* action: () => {
* alert("test3")
* }
* }
* }
*/
this.open = false;
this.id = this.getId();
}
componentDidMount() {
//Event listener for click
document.addEventListener("click", (event) => {
const id = this.id;
const open = this.open;
if(event.target.closest('#'+id) && !open) {
this.open = true;
animate();
}
if(!event.target.closest('#'+id) && open) {
this.open = false;
animate();
}
});
}
animate() {
const id = this.state.id;
//Targets that will be triggered
var targetDots = document.querySelectorAll('#'+id+' .Menu_dot');
var targetButton = document.querySelectorAll('#'+id+' .Menu_button');
var targetMenu = document.querySelector('#'+id+'.Menu');
if(this.open) {
var animation = anime.timeline();
//Display buttons
targetButton.forEach((element) => {
element.style.display = "block";
});
//Remove hover effect
targetMenu.classList.remove("Menu_closed");
animation
.add({
targets: targetDots,
translateX: ["-44.5%","0%"],
translateY: function(targetElements, i, l) {
if(i===0) return ["-44%","0%"];
if(i===1) return ["-44%","79%"];
if(i===2) return ["-44%","157%"];
},
scaleX: [0.1,1],
scaleY: [0.14,1],
borderRadius: {
value: 0,
easing: "easeOutCubic",
duration: 1000
},
duration: 2000,
backgroundColor: "rgb(255, 255, 255)"
})
.add({
targets: targetButton,
scaleX: [2.5,1],
opacity: 1,
duration: function(targetElements, i, l) {
return 400 + (i * 200);
},
easing: 'easeInOutCubic',
offset: '-=1700'
});
} else {
animation = anime.timeline();
const color = this.getColor();
//Remove hover effect
targetMenu.classList.add("Menu_closed");
animation
.add({
targets: targetButton,
scaleX: [1,2.5],
duration: function(targetElements, i, l) {
return 400 + (i * 200);
},
opacity: 0,
easing: 'easeInOutCubic'
})
.add({
targets: targetDots,
translateX: ["0%","-44.5%"],
translateY: function(targetElements, i, l) {
if(i===0) return ["0%","-44%"];
if(i===1) return ["79%","-44%"];
if(i===2) return ["157%","-44%"];
},
scaleX: [1,0.1],
scaleY: [1,0.14],
borderRadius: "100%",
duration: 1000,
backgroundColor: this.getColor(),
easing: "easeInOutCubic",
offset: '-=500'
});
//Remove menu after animation finished
animation.complete = function() {
targetButton.forEach((element) => {
element.style.display = "none";
});
};
}
}
getId() {
var random = "";
var possible = "abcdefghijklmnopqrstuvwxyz";
for (var i = 0; i < 5; i++)
random += possible.charAt(Math.floor(Math.random() * possible.length));
while(true) {
if(!document.getElementById(random)) {
return random;
}
}
}
getDimensions() {
//Input values
const inputWidth = this.props.width;
const inputHeight = this.props.height;
if(!(inputWidth || inputHeight) || !isNaN(inputWidth) || isNaN(inputHeight)) {
console.log("Please define a height OR width correctly! Only define one of both. It has to be an integer!");
return {
width: 0,
height: 0
}
} else {
//Units and values seperated
if(inputWidth !== undefined) {
//Width
const valueWidth = parseInt(inputWidth);
//Height
const valueHeight = valueWidth * 2.5;
return {
width: String(valueWidth) + 'px',
height: String(valueHeight) + 'px'
}
}
//Height
const valueHeight = parseInt(inputHeight);
//Width
const valueWidth = valueHeight / 2.5;
return {
width: String(valueWidth) + 'px',
height: String(valueHeight) + 'px'
}
}
}
getColor() {
return this.props.color ? this.props.color : "rgb(255, 255, 255)";
}
render() {
const style = {
width: this.getDimensions().width,
height: this.getDimensions().height
};
const styleDot = {
backgroundColor: this.getColor()
};
const styleFont = {fontSize: style.width};
return (
<div style={style} id={this.state.id} className="Menu Menu_closed">
<div className="Menu_dotBox">
<div style={styleDot} className="Menu_dot Menu_dotA">
<div className="Menu_button" style={styleFont} onClick={this.props.food.a.action}>
{this.props.food.a.value}
</div>
</div>
</div>
<div className="Menu_dotBox">
<div style={styleDot} className="Menu_dot Menu_dotB">
<div className="Menu_button" style={styleFont} onClick={this.props.food.b.action}>
{this.props.food.b.value}
</div>
</div>
</div>
<div className="Menu_dotBox">
<div style={styleDot} className="Menu_dot Menu_dotC">
<div className="Menu_button" style={styleFont} onClick={this.props.food.c.action}>
{this.props.food.c.value}
</div>
</div>
</div>
</div>
);
}
}
export default App;