Import ol-ext to a React - Open Layers 6 project - reactjs
I'm trying to set up a React project where I want to use some of the tools provided by the ol-ext. I looked around and found some Codesandbox projects from the creator of the library but I was unable to make them work properly because of the way the library is imported in the projects causes an error.
Is there a problem with the syntax or incompatibility with ol-ext and the other Open Layers versions?
For anyone coming to the same problem I managed to make it work and I implemented the Interaction Transform Features example. Here is the code below:
JS:
// Import stylesheets
import './style.css';
import "ol/ol.css";
import "ol-ext/dist/ol-ext.css";
import Transform from "ol-ext/interaction/Transform";
import Stamen from 'ol/source/Stamen';
import { Map, View } from "ol";
import { defaults } from "ol/control";
import * as olEvents from 'ol/events';
import TileLayer from 'ol/layer/Tile';
import { Style, Fill, Text, Icon, Stroke, RegularShape } from "ol/style";
import {Polygon, LineString, Point, Circle} from 'ol/geom';
import VectorLayer from "ol/layer/Vector";
import VectorSource from "ol/source/Vector";
import Feature from "ol/Feature";
var interaction = new Transform ({
enableRotatedTransform: false,
/* Limit interaction inside bbox * /
condition: function(e, features) {
return ol.extent.containsXY([-465960, 5536486, 1001630, 6514880], e.coordinate[0], e.coordinate[1]);
},
/* */
addCondition: olEvents.condition,
// filter: function(f,l) { return f.getGeometry().getType()==='Polygon'; },
// layers: [vector],
hitTolerance: 2,
translateFeature: false,
scale: true,
rotate: true,
translate: true,
stretch: true
});
var circle = new RegularShape({
fill: new Fill({color:[255,255,255,0.01]}),
stroke: new Stroke({width:1, color:[0,0,0,0.01]}),
radius: 8,
points: 10
});
interaction.setStyle ('rotate',
new Style({
text: new Text ({
text:'\uf0e2',
font:"16px Fontawesome",
textAlign: "left",
fill:new Fill({color:'red'})
}),
image: circle
}));
// Center of rotation
interaction.setStyle ('rotate0',
new Style({
text: new Text ({
text:'\uf0e2',
font:"20px Fontawesome",
fill: new Fill({ color:[255,255,255,0.8] }),
stroke: new Stroke({ width:2, color:'red' })
}),
}));
// Style the move handle
interaction.setStyle('translate',
new Style({
text: new Text ({
text:'\uf047',
font:"20px Fontawesome",
fill: new Fill({ color:[255,255,255,0.8] }),
stroke: new Stroke({ width:2, color:'red' })
})
}));
// Layers
var layers = [
new TileLayer({
title:'terrain-background',
source: new Stamen({ layer: 'terrain' })
})
]
// The map
var map = new Map({
target: null,
view: new View({
zoom: 5,
center: [261720, 5951081]
}),
controls: defaults({ "attribution": false }),
layers: layers
});
// Style
function getStyle(feature) {
return [ new Style({
image: new RegularShape({
fill: new Fill({ color: [0,0,255,0.4]}),
stroke: new Stroke({color: [0,0,255,1],width: 1}),
radius: 10,
points: 3,
angle: feature.get('angle')||0
}),
fill: new Fill({color: [0,0,255,0.4]}),
stroke: new Stroke({color: [0,0,255,1],width: 1})
})];
}
// New vector layer
var vector = new VectorLayer({
name: 'Vecteur',
source: new VectorSource({ wrapX: false }),
style: getStyle
})
map.addLayer(vector);
vector.getSource().addFeature(new Feature(new Polygon([[[34243, 6305749], [-288626, 5757848], [210354, 5576845], [300000, 6000000], [34243, 6305749]]])));
vector.getSource().addFeature(new Feature(new LineString([[406033, 5664901], [689767, 5718712], [699551, 6149206], [425601, 6183449]])));
vector.getSource().addFeature(new Feature(new Point( [269914, 6248592])));
vector.getSource().addFeature(new Feature(new Circle( [500000, 6400000], 100000 )));
// Set cursor style
Transform.prototype.Cursors['rotate'] = 'url(\'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABcAAAAXAgMAAACdRDwzAAAAAXNSR0IArs4c6QAAAAlQTFRF////////AAAAjvTD7AAAAAF0Uk5TAEDm2GYAAAABYktHRACIBR1IAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH2wwSEgUFmXJDjQAAAEZJREFUCNdjYMAOuCCk6goQpbp0GpRSAFKcqdNmQKgIILUoNAxIMUWFhoKosNDQBKDgVAilCqcaQBogFFNoGNjsqSgUTgAAM3ES8k912EAAAAAASUVORK5CYII=\') 5 5, auto';
Transform.prototype.Cursors['rotate0'] = 'url(\'data:image/png;base64,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\') 5 5, auto';
map.addInteraction(interaction);
class App extends Component {
state = {
name: 'React',
styles: false,
scale: true,
stretch: true,
disableStretch: false,
rotate: true,
translate: true,
translateFeature: false,
forceMatching: false,
info: "Hello there"
};
setHandleStyleInput = (event) => {
this.setState({styles: event.target.checked})
if (!interaction instanceof Transform) return;
if (event.target.checked) {
// Style the rotate handle
var circle = new RegularShape({
fill: new Fill({color:[255,255,255,0.01]}),
stroke: new Stroke({width:1, color:[0,0,0,0.01]}),
radius: 8,
points: 10
});
interaction.setStyle ('rotate',
new Style({
text: new Text ({
text:'\uf0e2',
font:"16px Fontawesome",
textAlign: "left",
fill:new Fill({color:'yellow'})
}),
image: circle
}));
// Center of rotation
interaction.setStyle ('rotate0',
new Style({
text: new Text ({
text:'\uf0e2',
font:"20px Fontawesome",
fill: new Fill({ color:[255,255,255,0.8] }),
stroke: new Stroke({ width:2, color:'yellow' })
}),
}));
// Style the move handle
interaction.setStyle('translate',
new Style({
text: new Text ({
text:'\uf047',
font:"20px Fontawesome",
fill: new Fill({ color:[255,255,255,0.8] }),
stroke: new Stroke({ width:2, color:'yellow' })
})
}));
interaction.setStyle ('scaleh1',
new Style({
text: new Text ({
text:'\uf07d',
font:"bold 20px Fontawesome",
fill: new Fill({ color:[255,255,255,0.8] }),
stroke: new Stroke({ width:2, color:'yellow' })
})
}));
interaction.style.scaleh3 = interaction.style.scaleh1;
interaction.setStyle('scalev',
new Style({
text: new Text ({
text:'\uf07e',
font:"bold 20px Fontawesome",
fill: new Fill({ color:[255,255,255,0.8] }),
stroke: new Stroke({ width:2, color:'yellow' })
})
}));
interaction.style.scalev2 = interaction.style.scalev;
} else {
interaction.setDefaultStyle();
}
// Refresh
interaction.set('translate', interaction.get('translate'));
}
setPropertieScale = (p) =>{
this.setState({scale: p.target.checked})
if (p.target.checked) this.setState({ disableStretch: false});
else this.setState({ disableStretch: true});
}
setPropertieStretch = (p) =>{
this.setState({stretch: p.target.checked})
}
setPropertieRotate = (p) =>{
this.setState({rotate: p.target.checked})
}
setPropertieTranslate = (p) =>{
this.setState({translate: p.target.checked})
}
setPropertieTranslateFeature = (p) =>{
this.setState({translateFeature: p.target.checked})
}
componentDidMount() {
map.setTarget("map");
}
componentDidUpdate(prevState) {
/** Style the transform handles for the current interaction
*/
if(this.state.scale !== prevState.scale || this.state.stretch !== prevState.stretch || this.state.rotate !== prevState.rotate || this.state.translate !== prevState.translate || this.state.styles !== prevState.styles){
var interaction = new Transform ({
enableRotatedTransform: false,
/* Limit interaction inside bbox * /
condition: function(e, features) {
return ol.extent.containsXY([-465960, 5536486, 1001630, 6514880], e.coordinate[0], e.coordinate[1]);
},
/* */
addCondition: olEvents.condition,
// filter: function(f,l) { return f.getGeometry().getType()==='Polygon'; },
// layers: [vector],
hitTolerance: 2,
translateFeature: this.state.translateFeature,
scale: this.state.scale,
rotate: this.state.rotate,
translate: this.state.translate,
stretch: this.state.stretch
});
interaction.set('translate', interaction.get('translate'));
// Style handles
}
}
render() {
var info = "Heyyou"
// Events handlers
var startangle = 0;
var d=[0,0];
// Handle rotate on first point
var firstPoint = false;
interaction.on (['select'], function(e) {
if (firstPoint && e.features && e.features.getLength()) {
interaction.setCenter(e.features.getArray()[0].getGeometry().getFirstCoordinate());
}
});
interaction.on (['rotatestart','translatestart'], function(e){
// Rotation
startangle = e.feature.get('angle')||0;
// Translation
d=[0,0];
});
interaction.on('rotating', (e)=>{
info= "rotate: "+((e.angle*180/Math.PI -180)%360+180).toFixed(2)
// Set angle attribute to be used on style !
e.feature.set('angle', startangle - e.angle);
});
interaction.on('translating', (e) =>{
d[0]+=e.delta[0];
d[1]+=e.delta[1];
info= "translate: "+d[0].toFixed(2)+","+d[1].toFixed(2)
if (firstPoint) {
interaction.setCenter(e.features.getArray()[0].getGeometry().getFirstCoordinate());
}
});
interaction.on('scaling', (e) => {
info= "scale: "+e.scale[0].toFixed(2)+","+e.scale[1].toFixed(2)
if (firstPoint) {
interaction.setCenter(e.features.getArray()[0].getGeometry().getFirstCoordinate());
}
});
interaction.on(['rotateend', 'translateend', 'scaleend'], (e) =>{
this.setState({info: info})
info= ""
});
return (
<div>
<div id="map" style={{width:"500px", height:"400px"}}></div>
<div className="options" >
<h2>Options:</h2>
<ul><li>
<input
className="style"
type="checkbox"
checked={this.state.styles}
onChange={this.setHandleStyleInput} />
<label> styles transform handles (using fontawesome)</label>
</li><li>
<input
className="scale"
type="checkbox"
checked={this.state.scale}
onChange={this.setPropertieScale} /><label> enable scale</label>
</li><li>
<input
className="stretch"
type="checkbox"
checked={this.state.stretch}
onChange={this.setPropertieStretch} disabled={this.state.disableStretch} />
<label> enable stretch</label>
</li><li>
<input
className="rotate"
type="checkbox"
checked={this.state.rotate}
onChange={this.setPropertieRotate} />
<label> enable rotate</label>
</li><li>
<input
className="translate"
type="checkbox"
checked={this.state.translate}
onChange={this.setPropertieTranslate} />
<label> enable translate</label>
</li><li>
<input
className="translateFeature"
type="checkbox"
checked={this.state.translateFeature}
onChange={this.setPropertieTranslateFeature} />
<label> translate when click on feature</label>
</li><li>
SetRotateCenter:
<button onClick={()=>{firstPoint=false; interaction.setCenter()}}>objects</button>
<button onClick={()=>{firstPoint=false; interaction.setCenter(map.getView().getCenter())}}>view center</button>
<button onClick={()=>{firstPoint=true;}}>first point</button>
</li><li>
<hr/>
Use <i>Shift</i> to add object to tranform
<hr/>
Use <i>Shift</i> key to preserve proportions when scaling (see keepAspectRatio).
<br />
Use <i>Ctrl</i> key to modify the center when scaling.
</li></ul>
<div style={{background:"white", padding:"0 0.45em"}}><span id="info"></span>{this.state.info}</div>
</div>
</div>
);
}
}
HTML:
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
<div id="root"></div>
CSS:
h1, p {
font-family: Lato;
}
a.icss-github-corner,
a.icss-github-corner-left {
font-size: 2.5em;
position: fixed;
top:0;
right: 0;
z-index: 1000;
color: #fff;
background-color: #333;
padding: .5em 2em 0;
text-align: center;
-webkit-transform: rotate(45deg);
-ms-transform: rotate(45deg);
transform: rotate(45deg);
-webkit-transform-origin: 3.9em 3em;
-ms-transform-origin: 3.9em 3em;
transform-origin: 3.9em 3em;
overflow: hidden;
}
a.icss-github-corner-left {
left: 0;
right: auto;
-webkit-transform: rotate(-45deg);
-ms-transform: rotate(-45deg);
transform: rotate(-45deg);
-webkit-transform-origin: 1em 3.1em;
-ms-transform-origin: 1em 3.1em;
transform-origin: 1em 3.1em;
}
a.icss-github-corner:hover i,
a.icss-github-corner-left:hover i{
-webkit-animation: vertical 2s ease;
animation: vertical 2s ease;
}
a.icss-github-corner i,
a.icss-github-corner-left i,
i.icss-github-corner {
color: #fff;
position: relative;
display:inline-block;
font-style: normal;
background-color:currentColor;
-webkit-box-sizing: border-box;
box-sizing: border-box;
vertical-align: middle;
width: .8em;
height: .6em;
-webkit-border-radius: 45% 45% 45% 45% / 50%;
border-radius: 45% 45% 45% 45% / 50%;
background-color: currentColor;
-webkit-box-shadow: 0 .35em 0 -.2em,
0 .38em 0 -.2em,
0 .41em 0 -.2em,
0 .44em 0 -.2em,
0 .47em 0 -.2em;
box-shadow: 0 .35em 0 -.2em,
0 .38em 0 -.2em,
0 .41em 0 -.2em,
0 .44em 0 -.2em,
0 .47em 0 -.2em;
margin: .12em .1em .23em;
}
a.icss-github-corner i:before,
a.icss-github-corner-left i:before,
i.icss-github-corner:before {
content: "";
border-width: 0;
position: absolute;
-webkit-box-sizing: border-box;
box-sizing: border-box;
border-width: .15em .15em;
border-style: solid;
-webkit-border-radius: 0.02em 60% 100% 80%;
border-radius: 0.02em 60% 100% 80%;
left: 0;
top: -.07em;
-webkit-transform: rotate(20deg);
-ms-transform: rotate(20deg);
transform: rotate(20deg);
}
a.icss-github-corner i:after,
a.icss-github-corner-left i:after,
i.icss-github-corner:after {
content: "";
border-width: 0;
position: absolute;
-webkit-box-sizing: border-box;
box-sizing: border-box;
border-width: .15em .15em;
border-style: solid;
-webkit-border-radius: 0.02em 80% 100% 60%;
border-radius: 0.02em 80% 100% 60%;
left: .5em;
top: -.07em;
-webkit-transform: rotate(65deg);
-ms-transform: rotate(65deg);
transform: rotate(65deg);
}
#-webkit-keyframes vertical {
0%{-webkit-transform:translate(0,-3px);transform:translate(0,-3px)}
4%{-webkit-transform:translate(0,3px);transform:translate(0,3px)}
8%{-webkit-transform:translate(0,-3px);transform:translate(0,-3px)}
12%{-webkit-transform:translate(0,3px);transform:translate(0,3px)}
16%{-webkit-transform:translate(0,-3px);transform:translate(0,-3px)}
20%{-webkit-transform:translate(0,3px);transform:translate(0,3px)}
22%,100%{-webkit-transform:translate(0,0);transform:translate(0,0)}
}
#keyframes vertical {
0%{-webkit-transform:translate(0,-3px);transform:translate(0,-3px)}
4%{-webkit-transform:translate(0,3px);transform:translate(0,3px)}
8%{-webkit-transform:translate(0,-3px);transform:translate(0,-3px)}
12%{-webkit-transform:translate(0,3px);transform:translate(0,3px)}
16%{-webkit-transform:translate(0,-3px);transform:translate(0,-3px)}
20%{-webkit-transform:translate(0,3px);transform:translate(0,3px)}
22%,100%{-webkit-transform:translate(0,0);transform:translate(0,0)}
}
/**/
body {
font-family: 'Lucida Grande',Verdana,Geneva,Lucida,Arial,Helvetica,sans-serif;
font-size: 16px;
}
a, i, b {
color: #337ab7;
text-decoration: none;
}
button i {
color: #fff;
}
.ol-control.ol-bar .ol-control button i {
color: #fff;
}
a:hover {
text-decoration: underline;
}
a.title {
text-decoration: none;
}
h1 {
background: #1f6b75 url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAACE1BMVEX///8A//8AgICA//8AVVVAQID///8rVVVJtttgv98nTmJ2xNgkW1ttyNsmWWZmzNZYxM4gWGgeU2JmzNNr0N1Rwc0eU2VXxdEhV2JqytQeVmMhVmNoydUfVGUgVGQfVGQfVmVqy9hqy9dWw9AfVWRpydVry9YhVmMgVGNUw9BrytchVWRexdGw294gVWQgVmUhVWPd4N6HoaZsy9cfVmQgVGRrytZsy9cgVWQgVWMgVWRsy9YfVWNsy9YgVWVty9YgVWVry9UgVWRsy9Zsy9UfVWRsy9YgVWVty9YgVWRty9Vsy9aM09sgVWRTws/AzM0gVWRtzNYgVWRuy9Zsy9cgVWRGcHxty9bb5ORbxdEgVWRty9bn6OZTws9mydRfxtLX3Nva5eRix9NFcXxOd4JPeINQeIMiVmVUws9Vws9Vw9BXw9BYxNBaxNBbxNBcxdJexdElWWgmWmhjyNRlx9IqXGtoipNpytVqytVryNNrytZsjZUuX210k5t1y9R2zNR3y9V4lp57zth9zdaAnKOGoaeK0NiNpquV09mesrag1tuitbmj1tuj19uktrqr2d2svcCu2d2xwMO63N+7x8nA3uDC3uDFz9DK4eHL4eLN4eIyYnDX5OM5Z3Tb397e4uDf4uHf5uXi5ePi5+Xj5+Xk5+Xm5+Xm6OY6aHXQ19fT4+NfhI1Ww89gx9Nhx9Nsy9ZWw9Dpj2abAAAAWnRSTlMAAQICAwQEBgcIDQ0ODhQZGiAiIyYpKywvNTs+QklPUlNUWWJjaGt0dnd+hIWFh4mNjZCSm6CpsbW2t7nDzNDT1dje5efr7PHy9PT29/j4+Pn5+vr8/f39/f6DPtKwAAABTklEQVR4Xr3QVWPbMBSAUTVFZmZmhhSXMjNvkhwqMzMzMzPDeD+xASvObKePPa+ffHVl8PlsnE0+qPpBuQjVJjno6pZpSKXYl7/bZyFaQxhf98hHDKEppwdWIW1frFnrxSOWHFfWesSEWC6R/P4zOFrix3TzDFLlXRTR8c0fEEJ1/itpo7SVO9Jdr1DVxZ0USyjZsEY5vZfiiAC0UoTGOrm9PZLuRl8X+Dq1HQtoFbJZbv61i+Poblh/97TC7n0neCcK0ETNUrz1/xPHf+DNAW9Ac6t8O8WH3Vp98f5lCaYKAOFZMLyHL4Y0fe319idMNgMMp+zWVSybUed/+/h7I4wRAG1W6XDy4XmjR9HnzvDRZXUAYDFOhC1S/Hh+fIXxen+eO+AKqbs+wAo30zDTDvDxKoJN88sjUzDFAvBzEUGFsnADoIvAJzoh2BZ8sner+Ke/vwECuQAAAABJRU5ErkJggg==") no-repeat scroll 10px center;
color: #fff;
font-size: 1.5em;
padding: 0.5em 50px;
margin:0;
}
h2 {
color: #337ab7;
font-size:1.1em;
margin: 0.5em 0;
}
.info {
background:#f5f5f5;
padding:0.5em;
margin: 1em 0;
}
.info ul {
margin:0;
}
#map {
float:left;
margin-right:1em;
background:#ddd;
}
.ol-attribution img {
vertical-align:middle;
}
.layerSwitcher {
display:inline-block;
background:#cdf;
padding:0.5em;
}
.btn {
color:#fff;
background:#369;
padding:0.5em;
text-decoration:none;
cursor:pointer;
display:inline-block;
margin-right:0.5em;
}
.block,
.options {
display: table;
margin: 0.5em;
position: relative;
z-index: 1;
margin:1em;
}
.options {
background: #def;
padding: 0.5em;
}
.options ul {
list-style: none;
padding-left: 0.5em;
}
i[class*="icss-"] {
position: relative;
display:inline-block;
font-style: normal;
background-color:currentColor;
box-sizing: border-box;
vertical-align: middle;
font-size: 1.5em;
}
i[class*="icss-"]:before,
i[class*="icss-"]:after {
content: "";
border-width: 0;
position: absolute;
}
i.icss-book {
width:1em;
height:.8em;
background-color: transparent;
margin: 0 .03em .08em 0;
}
i.icss-book:before {
height: .8em;
width: 0.7em;
box-shadow: inset 0 0 0 0.15em,
inset 0 -.48em,
.07em .07em;
border: 0.07em solid transparent;
border-width: 0 0.07em .07em 0;
border-radius: .05em .15em .1em .1em / .05em .05em .1em .05em;
transform: skewX(-20deg);
left: 0.15em;
}
i.icss-book:after {
width: .2em;
height: .2em;
background-color: transparent;
border: 0.05em solid currentColor;
border-color: currentColor transparent transparent currentColor;
border-radius: 50%;
transform: rotate(-45deg);
top: 0.67em;
left: .018em;
box-shadow: .13em -.15em 0 -.05em,
.51em -.33em 0 -.05em;
}
.experimental {
color:#fff;
background: #f91;
padding:.2em .5em;
display: inline-block;
-webkit-transform: rotate(-5deg);
transform: rotate(-5deg);
margin: -1em 0;
}
.ol-attribution ul {
font-size: .8em;
}
Also here is a working example: StackBlitz
Related
I am trying to render a image and as the errorCount increases show an different image everything works but image not displaying i had it but broke it
I am trying to figure out why the image will not display. I am trying to A different object everytime errorCount increases. Everything works but image will not display. I had it at one point but now I cannot figure out why it will not display. All the functionality works except displaying the image I want to display. strike-image-data.js import './images/hangmanScoreImg1.png'; import './images/hangmanScoreImg2.png'; import './images/hangmanScoreImg3.png'; import './images/hangmanScoreImg4.png'; import './images/hangmanScoreImg5.png'; import './images/hangmanScoreImg6.png'; import './images/hangmanScoreImg7.png'; const strikeImageData = [ { id: 1, image: "./images/hangmanScoreImg1.png" , name: 'start stage', phrase: 'You have 6 more wrong guesses left', }, { id: 2, image:'./images/hangmanScoreImg2.png', name: 'strike one stage', phrase:'You have 5 more wrong guesses left', }, { id: 3, image:'./images/hangmanScoreImg3.png', name:'strike two stage', phrase:'You have 4 more wrong guesses left', }, { id: 4, image:'./images/hangmanScoreImg4.png', name:'strike three stage', phrase:'You have 3 more wrong guesses left', }, { id: 5, image:'./images/hangmanScoreImg5.png', name:'strike four stage', phrase:'You have 2 more wrong guesses left', }, { id: 6, image:'./images/hangmanScoreImg6.png', name:'strike five stage', phrase:'You have 1 more wrong guess left', }, { id: 7, image:'./images/hangmanScoreImg7.png', name:'gameover stage', phrase:'Game Over', } ]; export default strikeImageData; gameboard.js import React, { useState, useEffect } from 'react'; import LetterGrid from './letter-grid'; import ButtonGrid from './button-grid'; import strikeImageData from './strike-image-data'; import './app.css'; // eslint-disable-next-line export default function({secretWord, maxErrors, isShown}) { const [guessedLetters, setGuessedLetters] = useState([]); const [errorCount, setErrorCount] = useState(0); const [strikeStages] = useState(strikeImageData); const [index, setIndex] = React.useState(0); const letterGuessedHandler = function(letter) { let val = letter.toLowerCase(); if (secretWord.toLowerCase().indexOf(val) === -1) { setErrorCount(errorCount + 1); setIndex(index + 1); } setGuessedLetters(prev => [...prev, val]); } useEffect(() => { const lastIndex = strikeStages.length - 1; if(index < 0) { setIndex(lastIndex); } if(index > lastIndex) { setIndex(0); } }, [index, strikeStages]); return ( <div className={isShown ? '' : 'hidden'}> <section className='section'> <div className='section-center'> {strikeStages.map((strikeStage, strikeStageIndex) => { const { id, image, name, phrase } = strikeStage; let position = 'nextSlide'; if (strikeStageIndex === index) { position = 'activeSlide'; } if (strikeStageIndex === index - 1 || (index === 0 && strikeStageIndex === strikeStages.length - 1)) { position = 'lastSlide'; } return ( <article className={position} key={id} > <img src={image} alt={name} /> <h4>{name}</h4> <p className="text">{phrase}</p> </article> ); })} </div> </section> <div> Mistakes Left: {maxErrors - errorCount} </div> <LetterGrid secretWord={secretWord} guessedLetters={guessedLetters} /> <ButtonGrid letterGuessed={letterGuessedHandler} isShown={errorCount < maxErrors} /> </div> ) } app.js import {useState} from 'react'; import './app.css'; import GameBoard from './game-board'; import WordSelect from './word-select'; export default function App() { const [secretWord, setSecretWord] = useState(''); return ( <div className="app-container"> <h1>Welcome to Hangman</h1> <p>Do you want to play the Hangman Game?</p> <div> <WordSelect isShown={!secretWord} wordSelected={val => setSecretWord(val)} /> <GameBoard secretWord={secretWord} maxErrors={6} isShown={secretWord} /> </div> </div> ); } app.css /*Global Styles */ :root { /* dark shades of primary color*/ --clr-primary-1: hsl(21, 91%, 17%); --clr-primary-2: hsl(21, 84%, 25%); --clr-primary-3: hsl(21, 81%, 29%); --clr-primary-4: hsl(21, 77%, 34%); --clr-primary-5: hsl(21, 62%, 45%); --clr-primary-6: hsl(21, 57%, 50%); --clr-primary-7: hsl(21, 65%, 59%); --clr-primary-8: hsl(21, 80%, 74%); --clr-primary-9: hsl(21, 94%, 87%); --clr-primary-10: hsl(21, 100%, 94%); /* darkest grey - used for headings */ --clr-grey-1: hsl(209, 61%, 16%); --clr-grey-2: hsl(211, 39%, 23%); --clr-grey-3: hsl(209, 34%, 30%); --clr-grey-4: hsl(209, 28%, 39%); /* grey used for paragraphs */ --clr-grey-5: hsl(210, 22%, 49%); --clr-grey-6: hsl(209, 23%, 60%); --clr-grey-7: hsl(211, 27%, 70%); --clr-grey-8: hsl(210, 31%, 80%); --clr-grey-9: hsl(212, 33%, 89%); --clr-grey-10: hsl(210, 36%, 96%); --clr-white: #fff; --clr-red-dark: hsl(360, 67%, 44%); --clr-red-light: hsl(360, 71%, 66%); --clr-green-dark: hsl(125, 67%, 44%); --clr-green-light: hsl(125, 71%, 66%); --clr-black: #222; --transition: all 0.3s linear; --spacing: 0.1rem; --radius: 0.25rem; --light-shadow: 0 5px 15px rgba(0, 0, 0, 0.1); --dark-shadow: 0 5px 15px rgba(0, 0, 0, 0.2); --max-width: 1170px; --fixed-width: 620px; } body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif; background: var(--clr-grey-10); color: var(--clr-grey-1); line-height: 1.5; font-size: 0.875rem; } ul { list-style-type: none; } img { min-width: 150px; min-height: 150px; } a { text-decoration: none; } h1, h2, h3, h4 { letter-spacing: var(--spacing); text-transform: capitalize; line-height: 1.25; margin-bottom: 0.75rem; } h1 { font-size: 3rem; } h2 { font-size: 2rem; } h3 { font-size: 1.25rem; } h4 { font-size: 0.875rem; } p { margin-bottom: 1.25rem; color: var(--clr-grey-5); } #media screen and (min-width: 800px) { h1 { font-size: 3rem; } h2 { font-size: 2.5rem; } h3 { font-size: 1.75rem; } h4 { font-size: 1rem; } body { font-size: 1rem; } h1, h2, h3, h4 { line-height: 1; } } .app-container { width: 550px; } .flex { display: flex; } .flex-wrap { flex-wrap: wrap; } .mt-10 { margin-top: 10px; } .letter { border: 1px solid gray; font-size: 24px; font-weight: bold; line-height: 34px; height: 34px; width: 34px; text-align: center; } .button { font-size: 24px; font-weight: bold; line-height: 34px; height: 34px; width: 34px; text-align: center; margin-right: 4px; margin-bottom: 4px; } .guessed { display: none; } .hidden { display: none; } /* Global Classes */ /* section */ .section { width: 90vw; margin: 5rem auto; max-width: var(--max-width); } #media screen and (min-width: 992px) { .section { width: 95vw; } } .section-center { margin: 0 auto; margin-top: 4rem; width: 80vw; height: 450px; max-width: 800px; text-align: center; position: relative; display: flex; overflow: hidden ; } article { position: absolute; top: 0; left: 0; width: 100%; height: 100%; opacity: 0; transition: var(--transition); } article.activeSlide { opacity: 1; transform: translateX(0); } article.lastSlide { transform: translateX(-100%); } article.nextSlide { transform: translateX(100%); }
How to build a slider in react js or javascript without using material ui slider
I want to create a slider in react js but don't know how to implement this without using any third-party plugins. Something similar to this https://mui.com/components/slider/
You can try Something like this: class Slider extends React.Component { constructor() { super(); this.state = { value: 0 }; this.changeValue = this.changeValue.bind(this); } changeValue() { this.setState({ value: this.refs.input.value * this.props.max }); } render() { const sliderStyle = { position: "relative", width: "284px", height: "28px" }; const rangeStyle = { webkitAppearance: "none", appearance: "none", touchAction: "pan-y", position: "absolute", margin: "0", padding: "0", width: "284px", backgroundColor: "transparent" }; const progressBarStyle = { webkitAppearance: "none", appearance: "none", position: "absolute", display: "block", margin: "0", top: "13px", left: "13px", width: "256px", height: "3px", zIndex: "-1", backgroundColor: "#D7D7D7" }; return ( <div style={sliderStyle} aria-valuemin={this.props.min} aria-valuemax={this.props.max} aria-valuenow={this.state.value} aria-valuetext={this.state.value} > <input ref="input" type="range" onChange={this.changeValue} defaultValue={this.props.min} min={this.props.min} max={this.props.max} step={this.props.max / 100} style={rangeStyle} /> <progress value={this.state.value} min={this.props.min} max={this.props.max} style={progressBarStyle} ></progress> </div> ); } } ReactDOM.render(<Slider min={0} max={1} />, document.querySelector("#root")); :root { --thumb-shadow: 0 3px 8px rgba(0, 0, 0, .15), 0 1px 1px rgba(0, 0, 0, .16), 0 3px 1px rgba(0, 0, 0, .10) } body { margin: 0; width: 100vw; height: 100vh; display: -webkit-flex; display: flex; justify-content: center; align-items: center; background-color: #EFEFF4 } * { outline: none } progress::-webkit-progress-value { background-color: #007AFF } progress::-webkit-progress-bar { background-color: #B6B6B6; border-radius: 1.5px; overflow: hidden } input::-webkit-slider-thumb { -webkit-appearance: none; appearance: none; height: 28px; width: 28px; border: none; border-radius: 50%; background-color: white; z-index: 2; box-shadow: var(--thumb-shadow) } <script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script> <div id=root> </div>
Why is git pages rendering my page different then in local?
I'm rather new to whole react thing, going through courses on FCC. My problem come from CSS/SCSS itself. I tried ordinary CSS and SCSS. My page is done in React. The page is also hosted on codepen. All three versions are the same, codepen, git and local and yet CSS is somewhat different between them. My previous projects who are also hosted on these platforms and in local have also the same CSS but they look identical. Difference between local and codepen's version isn't that drastic, but between local>codepen>github is more than noticeable as you descend. Do GitHub ignores/renders? or what, React differently? This question should also apply to codepen. My codepen My git page My repoToProject Image of localhost: html from codepen: <div id="root"></div> scss from codepen: $position: absolute; $percentage: 100%; $color: whitesmoke; $car: auto; #import url("//netdna.bootstrapcdn.com/bootstrap/3.0.0/css/bootstrap-glyphicons.css"); .btn1 { position: $position; bottom: -30px; left: 51.3%; z-index: 999; background-color: $color; border: 1px solid gray; color: aqua; } #root { width: $percentage; } body, html { height: $percentage; } body { background-color: $color; background-image: url("../images/react1.png"); background-repeat: no-repeat; background-size: $car; background-position: center bottom; overflow: hidden; } .grid-container { display: grid; grid-template-columns: 50% 50%; background-color: $color; padding: 10px; grid-gap: 15px; width: $percentage; grid-template-rows: 30px 25px 98%; } .grid-item { text-align: center; } #inner2 { padding-left: 20px; padding-right: 25px; border-bottom: 15px solid #d6e9c6; padding-bottom: 55px; background-color: #fcf8e3; width: $percentage; margin-left: $car; margin-right: $car; position: $position; top: 0px; left: 0px; height: $percentage; min-height: 20%; max-height: $percentage; } #editor { width: $percentage; background-color: white; resize: none; color: #495057; border: 1px solid #ced4da; border-radius: 0.25rem; overflow-y: $car; max-height: $percentage; min-height: 40px; margin-bottom: 40px; } #preview { width: 98.9%; background-color: white; border-radius: 0.25rem; border: 1px solid #ced4da; overflow-y: $car; max-height: $percentage; min-height: 40px; margin-bottom: 40px; } #item1, #item2 { font-family: "Russo One"; font-style: oblique; font-weight: bold; font-size: 2em; margin-bottom: 10px; padding-bottom: 0px; width: $percentage; background-color: #fcf8e3; min-height: 50px; border-bottom: none; padding-top: 10px; } .insignia { letter-spacing: 5px; -webkit-transition: letter-spacing 1s; transition: letter-spacing 1s; } .insignia:hover { letter-spacing: 13px; cursor: pointer; } .ui-resizable-s { cursor: row-resize; } textarea:focus, input:focus { outline: none; } #arrow { background-color: #dff0d8; width: $percentage; height: 15px; position: $position; bottom: -12px; padding-left: 0px; padding-right: 0px; font-size: 1.5em; border-bottom: 1px solid #d6e9c6; text-align: center; } .glyphicon { top: -4px; left: 4px; color: gray; -ms-transform: scale(1, 0.6); /* IE 9 */ -webkit-transform: scale(1, 0.6); /* Safari */ transform: scale(1, 0.6); } #media screen and (max-height: 600px) { #inner2 { height: 90vh !important; } } #eraser { text-align: center; grid-column: 1 / 3; z-index: 2; line-height: 10px; margin-left: $car; margin-right: $car; } /*Additional styling*/ td, th { border: 2px solid #224b4b; padding-left: 5px; padding-right: 5px; } .label { position: $position; top: -10px; left: 0px; min-width: $percentage; z-index: 999; } .preview-editor { position: fixed; top: 55px; left: 0px; min-width: $percentage; height: $percentage; z-index: 999; } h1 { border-bottom: 2px solid #224b4b; } h2 { border-bottom: 1px solid #224b4b; } code { background-color: #d6e9c6; color: #e83e8c !important; } blockquote { border-left: 2px solid black; padding-left: 5px; margin-left: 25px; } #media only screen and (max-width: 768px) { img { width: 100%; } #ggED { text-align: left; } #ggPrev { text-align: right; } .insignia, .insignia:hover { letter-spacing: 0px; font-size: 1em; } } js from codepen: var renderer = new marked.Renderer(); renderer.link = function(href, title, text) { return ( '<a target="_blank" href="' + href + '" title="' + title + '">' + text + "</a>" ); }; marked.setOptions({ breaks: true, renderer: renderer, sanitize: true }); class DisplayMessages extends React.Component { constructor(props) { super(props); this.state = { markdown: defaultMarkdown, erase: false, goFull: false, headViewKlasa: "grid-item", headEdKlasa: "grid-item", editorKlasa: "", previewKlasa: "", stilPreview: {}, stilEditor: {}, attr: "Click on me for fullscreen", inner2H: "", h2Inner: false }; this.handleChange = this.handleChange.bind(this); this.eraseFields = this.eraseFields.bind(this); this.inner2Height = this.inner2Height.bind(this); } eraseFields() { this.setState({ erase: true }); if (this.state.erase === false) { this.setState({ markdown: "" }); } if (this.state.erase === true) { this.setState({ markdown: defaultMarkdown, erase: !this.state.erase }); } } componentDidMount() { this.node = ReactDOM.findDOMNode(this); $(this.node).resizable({ handles: "s", minHeight: 170 }); document .querySelector(".ui-resizable-handle.ui-resizable-s") .setAttribute( "title", "Double click on me or pull me down to full height" ); } inner2Height() { if (this.state.h2Inner === false) { this.setState({ inner2H: "100%", h2Inner: true }); } if (this.state.h2Inner === true) { this.setState({ inner2H: "", h2Inner: false }); } } fullScreen(clicked_id) { if (clicked_id === "ggEd" && this.state.goFull === false) { this.setState({ headEdKlasa: this.state.headEdKlasa + " label", attr: "Click again to go back!", editorKlasa: "preview-editor", stilPreview: { display: "none" }, stilEditor: { paddingTop: "0px" }, goFull: true }); } if (clicked_id === "ggEd" && this.state.goFull === true) { this.setState({ headEdKlasa: this.state.headEdKlasa.substr(0, 9), attr: "Click on me for fullscreen", editorKlasa: "", stilPreview: { display: "block" }, stilEditor: { paddingTop: "10px" }, goFull: !this.state.goFull }); } if (clicked_id === "ggPrev" && this.state.goFull === false) { this.setState({ headViewKlasa: this.state.headViewKlasa + " label", attr: "Click again to go back!", previewKlasa: "preview-editor", stilEditor: { display: "none" }, stilPreview: { paddingTop: "0px" }, goFull: true }); } if (clicked_id === "ggPrev" && this.state.goFull === true) { this.setState({ headViewKlasa: this.state.headViewKlasa.substr(0, 9), attr: "Click on me for fullscreen", previewKlasa: "", stilEditor: { display: "block" }, stilPreview: { paddingTop: "10px" }, goFull: !this.state.goFull }); } } handleChange(event) { this.setState({ markdown: event.target.value }); } render() { const btnText = this.state.erase ? "Populate" : "Erase"; const handleClick = e => this.fullScreen(e.target.id); return ( <div id="inner2" className="grid-container animated zoomIn" style={{ height: this.state.inner2H }} onDoubleClick={this.inner2Height} > <EditorHead id={"item1"} style={this.state.stilEditor} className={this.state.headEdKlasa} onClick={handleClick} title={this.state.attr} /> <PreviewHead id={"item2"} style={this.state.stilPreview} className={this.state.headViewKlasa} onClick={handleClick} title={this.state.attr} /> <BtnEraser id={"eraser"} onClick={this.eraseFields} type={"button"} className={"btn btn-danger btn-lg"} title={"Erase & populate both fields"} value={btnText} /> <Editor id={"editor"} onChange={this.handleChange} className={this.state.editorKlasa} value={this.state.markdown} placeholder={"Enter ... some kind a text!? ..."} title={ "This is rather obvious isn't it? It's editor window Sherlock :D" } /> <Preview id={"preview"} className={this.state.previewKlasa} dangerouslySetInnerHTML={{ __html: marked(this.state.markdown, { renderer: renderer }) }} title={"It's a preview window, Sherlock ;)"} /> <Arrow id={"arrow"} /> </div> ); } } /*class Inner2 extends React.Component{ render(){ return ( <div id={this.props.id} className={this.props.className} style={this.props.style} onDoubleClick={this.props.onDoubleClick}>Editor:</div> ); } }*/ class EditorHead extends React.Component { render() { return ( <h1 id={this.props.id} style={this.props.style} className={this.props.className} onClick={this.props.onClick} > <span className="insignia" title={this.props.title} id="ggEd"> Editor: </span> </h1> ); } } class PreviewHead extends React.Component { render() { return ( <h1 id={this.props.id} style={this.props.style} className={this.props.className} onClick={this.props.onClick} > <span className="insignia" title={this.props.title} id="ggPrev"> Previewer: </span> </h1> ); } } class BtnEraser extends React.Component { render() { return ( <button id={this.props.id} onClick={this.props.onClick} type={this.props.type} className={this.props.className} title={this.props.title} > {this.props.value} </button> ); } } class Editor extends React.Component { render() { return ( <textarea id={this.props.id} onChange={this.props.onChange} className={this.props.className} value={this.props.value} placeholder={this.props.placeholder} title={this.props.title} /> ); } } class Preview extends React.Component { render() { return ( <div id={this.props.id} className={this.props.className} dangerouslySetInnerHTML={this.props.dangerouslySetInnerHTML} title={this.props.title} /> ); } } class Arrow extends React.Component { render() { return ( <div id={this.props.id}> <Glyph className={"glyphicon glyphicon-align-justify"} /> </div> ); } } class Glyph extends React.Component { render() { return <span className={this.props.className} />; } } ReactDOM.render(<DisplayMessages />, document.getElementById("root"));
ReactCSSTransitionGroup enter animation not working, exit is working fine
I am creating a reusable modal, and everything is working but when I actually call the component, the enter transition is not working. Exit works perfectly. I get the error: warning.js:45 Warning: Failed propType: transitionLeaveTimeout wasn't supplied to ReactCSSTransitionGroup: this can cause unreliable animations and won't be supported in a future version of React.Check the render method of Modal I have supplied enter and leave timeouts as instructed and still no luck. import React from 'react'; import { render } from 'react-dom'; import ReactCSSTransitionGroup from 'react-addons-css-transition-group'; import '../../modal.css'; const Modal = React.createClass({ render(){ if(this.props.isOpen){ return ( <ReactCSSTransitionGroup transitionName={this.props.transitionName} transitionEnterTimeout={400} transitionLeaveTimeout={400}> <div className="ui-modal" key={this.props.transitionName} {...this.props}> {this.props.children} </div> </ReactCSSTransitionGroup> ); } else { return <ReactCSSTransitionGroup transitionName={this.props.transitionName} />; } } }); const UiModal = React.createClass({ getInitialState(){ return { isModalOpen: false }; }, openModal() { this.setState({ isModalOpen: true }); }, closeModal() { this.setState({ isModalOpen: false }); }, setModalSize() { this.setState({ isModalLarge: false }); }, render() { const { openBtnText, header, subHeader, body, footer, optionalFooterText, closeBtnText, size } = this.props; const modalSize = size === 'large' ? 'ui-modal-large' : 'ui-modal-small'; return ( <div className="ui-modal-trigger-container"> <h1>Modal small enter from bottom</h1> <div className="button" onClick={this.openModal}>{ this.props.openBtnText }</div> <Modal isOpen={this.state.isModalOpen} transitionName="modal-anim" id={modalSize}> <h1 className="ui-modal-header">{header}</h1> <div className="ui-modal-subheader">{subHeader}</div> <div className="ui-modal-body"> {body} </div> <div className="ui-modal-footer"> <div className="ui-modal-footer-button-group"> <div className="ui-modal-footer-button button" onClick={this.closeModal}>{closeBtnText}</div> <div className="ui-modal-optional-footer-text" onClick={this.closeModal}>{optionalFooterText}</div> </div> </div> </Modal> </div> ); } }); export default UiModal; The only information I get back in the console is: warning.js:45 Warning: Failed propType: transitionLeaveTimeout wasn't supplied to ReactCSSTransitionGroup: this can cause unreliable animations and won't be supported in a future version of React.Check the render method of Modal I am unsure how to fix since I have provided enter and leave timeouts already and it does not fix the issue. The CSS for the modal is below: .ui-modal-trigger-container { width: 500px; margin: 0 auto; padding: 20px; border: 1px solid #ccc; border-radius: 4px; z-index: 0; margin-top: 300px; } .ui-modal { width: 450px; margin: 0 auto; top: 70%; left: 35%; padding: 20px; background-color: green; position: absolute; z-index: 1; border: 1px solid grey; box-shadow: 0 0 5px 2px #fff; background: white; } #ui-modal-small { width: 450px; margin: 0 auto; top: 70%; left: 35%; padding: 20px; background-color: green; position: absolute; z-index: 1; border: 1px solid grey; box-shadow: 0 0 5px 2px #fff; background: white; } #ui-modal-large { width: 100%; height: 100%; position: absolute; z-index: 1; top: 8%; left:0%; border: 1px solid #ccc; background: white; } .ui-modal-header { font-family: 'Flexo'; font-size: 28px; border-bottom: 2px solid black; width: 90%; margin: 0 auto; } .ui-modal-subheader { font-family: 'Flexo'; font-size: 13px; width: 90%; margin: 0 auto; } .ui-modal-body { margin: 0 auto; width: 90%; padding: 10px; } .ui-modal-footer { border-top: 2px solid black; margin: 0 auto; width: 90%; } .ui-modal-footer-button-group { padding-top: 10px; } .ui-modal-footer-button { float: right; } .ui-modal-optional-footer-text { float: left; color: #4099D4; font-style: italic; } .modal-anim-enter { opacity: 0.00; transform: translateY(100%); transition: all .5s; } .modal-anim-enter.modal-anim-enter-active { opacity: 1; transform: scale(1); transition: all .5s; } .modal-anim-leave { opacity: 1; transform: translateY(100%); transition: all .5s; } .modal-anim-leave.modal-anim-leave-active { opacity: 0.00; transform: translateY(100%); }
Animating movement of other items in ng-repeat with ngAnimate
Sorry for the vague title, but I couldn't find a fitting short description. So, I have a list of different items which I'm using ng-repeat to show. Some of them might be removed or added to that list. I'm using ng-enter etc to animate the transition of the items entering and exiting the view. The problem is, the rest of the items, the ones that are not being added or removed, look like they hop and skip around instead of flowing naturally when items between them are being removed or added. I made a codepen: If you click any of the players, you remove them. Click reset to bring them back in. If you remove any of the players in the middle, you can see they jump to the left instantly after the removed item is done with its animation. I would like it to slide over to it's new position instead. http://codepen.io/utrolig/pen/oxQVVZ HTML: <div ng-app="test"> <div ng-controller="AnimateController as vm"> <div class="player" ng-repeat="player in vm.items" ng-click="vm.toggleActiveState(player)" ng-if="player.active"> <div class="imgcont"> <img ng-src="{{player.img}}" /> <div class="jerseyno"> <span>#{{::player.jerseyNumber}}</span> </div> </div> <div class="playername"> <span ng-bind="::player.name"></span> </div> </div> <div class="resetbutton" ng-click="vm.resetActiveState(vm.players)"> <span>Reset</span> </div> </div> </div> JS: (function(){ 'use strict'; angular .module('test', ['ngAnimate']) .controller('AnimateController', AnimateController); function AnimateController(){ var vm = this; vm.toggleActiveState = toggleActiveState; vm.resetActiveState = resetActiveState; vm.items = [ { name: "Pavel Bure", active: true, team: "Vancouver Canucks", jerseyNumber: 10, img: "https://nhl.bamcontent.com/images/headshots/current/168x168/8455738.jpg" },{ name: "Wayne Gretzky", jerseyNumber: 99, active: true, team: "Vancouver Canucks", img: "https://nhl.bamcontent.com/images/headshots/current/168x168/8447400.jpg" },{ name: "Jaromir Jagr", jerseyNumber: 68, active: true, team: "Florida Panthers", img: "https://nhl.bamcontent.com/images/headshots/current/168x168/8448208.jpg" },{ name: "Mats Zuccarello", jerseyNumber: 36, active: true, team: "New York Rangers", img:"https://nhl.bamcontent.com/images/headshots/current/168x168/8475692.jpg" } ]; ////////////// function toggleActiveState(item){ item.active = item.active ? false : true; } function resetActiveState(arr){ for (var i = 0; i < vm.items.length; i++){ vm.items[i].active = true; } } } })(); CSS: body, html { min-height: 100vh; position: relative; font-family: 'Arial', sans-serif; background: #1d1f20; color: rgba(255,255,255,0.7) } .imgcont { border-radius: 8px; } .imgcont img { border-radius: 8px; } .jerseyno { position: absolute; top: 6px; right: 6px; padding: 4px; background: rgba(0,0,0,.75); border-radius: 4px; } .resetbutton { display: block; float: left; border-radius: 4px; margin: 24px; padding: 24px; background: rgba(0,0,0,.5); cursor: pointer; } .playername { position: absolute; bottom: 6px; left: 6px; background: rgba(0,0,0,.75); padding: 4px; border-radius: 4px; } .player { float: left; margin-right: 12px; position: relative; cursor: hand; cursor: pointer; transition: .5s all; } .player.ng-enter { opacity: 0; transform: translateY(-50%); } .player.ng-enter-active { opacity: 1; transform: translateY(0%); } .player.ng-leave-active { opacity: 0; transform: translateY(-50%); } Thanks!