<div id="app"></div>
// Fonts
@import 'https://fonts.googleapis.com/css?family=Lato:100,300,400,700,900';
// Colors
$white: #FFFFFF;
$sunsetorange: #F05D5E;
$irresistable: #AF3B6E;
$yankeesblue: #252D3F;
$mediumspringgreen: #21FA90;
// Mixins
@mixin mq($width) {
@media screen and (max-width: $width) {
@content;
}
}
// Styles
.App {
align-items: center;
font-family: 'Lato', sans-serif;
display: flex;
font-weight: 300;
width: 100vw;
height: 100vh;
justify-content: center;
background: $yankeesblue;
overflow: hidden;
position: absolute;
top: 0;
left: 0;
}
.ImageBG {
position: absolute;
top: -5vh;
left: -5vw;
width: 110vw;
height: 110vh;
background-size: cover;
filter: blur(15px);
opacity: 1;
mix-blend-mode: multiply;
}
.Settings {
width: 90vw;
height: 90vh;
position: relative;
z-index: 2;
}
.MainWrapper {
display: flex;
width: 100%;
height: 85%;
.Sidebar {
width: 30%;
max-width: 350px;
height: 100%;
background: rgba(lighten($yankeesblue, 5), .85);
@include mq(880px) {
display: none;
}
.Title {
width: 100%;
padding: 20px;
box-sizing: border-box;
border-bottom: 1px solid rgba($white, .1);
color: $white;
text-transform: uppercase;
font-weight: 600;
letter-spacing: .2em;
font-size: 12px;
}
.Setting {
padding: 0 20px;
margin-top: 20px;
border-bottom: 1px solid rgba($white, .05);
label {
font-weight: 400;
text-transform: uppercase;
color: $white;
display: flex;
justify-content: space-between;
margin-bottom: 5px;
font-size: 12px;
letter-spacing: .2em;
opacity: .5;
}
input {
width: 100%;
margin-bottom: 20px;
}
}
}
.ImageContainer {
width: 50%;
background: rgba(darken($yankeesblue, 5), .5);
display: flex;
flex: 1 0 auto;
justify-content: center;
align-items: center;
.Image {
height: 90%;
width: 90%;
background-size: cover;
box-shadow: 0 10px 20px rgba(black, .5);
background-position: center;
position: relative;
transition: filter .125s ease;
&::before {
content: '';
width: 100%;
height: 100%;
position: absolute;
top: 0;
left: 0;
// background: blue;
// mix-blend-mode: color;
opacity: .3;
}
&::after {
width: 100%;
height: 100%;
position: absolute;
top: 0;
mix-blend-mode: overlay;
left: 0;
z-index: 3;
content: '';
// background: -moz-radial-gradient(center, ellipse cover, rgba(0,0,0,0) 0%, rgba(0,0,0,0.65) 100%); /* FF3.6-15 */
// background: -webkit-radial-gradient(center, ellipse cover, rgba(0,0,0,0) 0%,rgba(0,0,0,0.65) 100%); /* Chrome10-25,Safari5.1-6 */
// background: radial-gradient(ellipse at center, rgba(0,0,0,0) 0%,rgba(0,0,0,0.65) 100%); /* W3C, IE10+, FF16+, Chrome26+, Opera12+, Safari7+ */
// filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#00000000', endColorstr='#a6000000',GradientType=1 ); /* IE6-9 fallback on horizontal gradient */
}
}
}
}
.FilterList {
width: 100%;
height: calc(15% - 10px);
background: rgba(lighten($yankeesblue, 5), .45);
box-sizing: border-box;
padding: 20px;
display: flex;
overflow-x: auto;
.Filter {
// min-width: 150px;
// width: 150px;
width: 25%;
height: 100%;
position: relative;
flex: 1 0 1;
margin-right: 20px;
background: darken($yankeesblue, 2);
box-shadow: 0 10px 20px 0 rgba(black, .25);
transition: box-shadow .25s ease, transform .25s ease;
&:hover {
cursor:pointer;
box-shadow: 0 10px 20px -10px rgba(black, 1);
transform: scale(1.1);
}
&:last-child {
margin-right: 0px;
}
// FilterImage
.Image {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-size: cover;
background-position: center;
transition: filter .25s ease .25s;
}
}
}
View Compiled
// Data
var data = {
image: 'https://unsplash.it/1080/800',
settings: [
{
name: 'contrast',
value: '100%',
},
{
name: 'hue',
value: '0deg'
},
{
name: 'brightness',
value: '100%'
},
{
name: 'saturate',
value: '100%'
},
{
name: 'sepia',
value: '0%'
}
],
filters: [
{
id: 0,
name: 'Noir',
settings: [
{
name: 'contrast',
value: '138%',
},
{
name: 'hue',
value: '0deg'
},
{
name: 'brightness',
value: '122%'
},
{
name: 'saturate',
value: '0%'
},
{
name: 'sepia',
value: '0%'
}
]
},
{
id: 1,
name: 'Aged',
settings: [
{
name: 'contrast',
value: '94%',
},
{
name: 'hue',
value: '-54deg'
},
{
name: 'brightness',
value: '92%'
},
{
name: 'saturate',
value: '100%'
},
{
name: 'sepia',
value: '44%'
}
]
},
{
id: 2,
name: 'Whiteout',
settings: [
{
name: 'contrast',
value: '32%',
},
{
name: 'hue',
value: '0deg'
},
{
name: 'brightness',
value: '173%'
},
{
name: 'saturate',
value: '0%'
},
{
name: 'sepia',
value: '0%'
}
]
},
{
id: 3,
name: 'Vintage',
settings: [
{
name: 'contrast',
value: '164%',
},
{
name: 'hue',
value: '0deg'
},
{
name: 'brightness',
value: '47%'
},
{
name: 'saturate',
value: '0%'
},
{
name: 'sepia',
value: '100%'
}
]
}
]
};
// App Container
var App = React.createClass({
getDefaultProps: function() {
return(data);
},
handleChange: function(e) {
var value = e.target.value;
var name = e.target.id;
switch (name) {
case 'contrast':
this.props.settings[0].value = value + '%';
break;
case 'hue':
this.props.settings[1].value = value + 'deg';
break;
case 'brightness':
this.props.settings[2].value = value + '%';
break;
case 'saturate':
this.props.settings[3].value = value + '%';
break;
case 'sepia':
this.props.settings[4].value = value + '%';
break;
}
this.forceUpdate();
},
handleClick: function(e) {
var index = e.target.id.replace('filter-','');
// console.log(this.props.filters[index].settings);
this.props.settings[0].value = this.props.filters[index].settings[0].value;
this.props.settings[1].value = this.props.filters[index].settings[1].value;
this.props.settings[2].value = this.props.filters[index].settings[2].value;
this.props.settings[3].value = this.props.filters[index].settings[3].value;
this.props.settings[4].value = this.props.filters[index].settings[4].value;
this.forceUpdate();
},
render: function() {
return(
<div className="App">
<ImageBG image={this.props.image} />
<Settings onClick={this.handleClick} onChange={this.handleChange} data={this.props} />
</div>
)
}
});
// Image Background
var ImageBG = React.createClass({
render: function() {
return (
<div className="ImageBG" style={{backgroundImage: 'url('+ this.props.image + ')'}}></div>
)
}
});
// Settings Container
var Settings = React.createClass({
render: function() {
return (
<div className="Settings">
<div className="MainWrapper">
<Sidebar onChange={this.props.onChange} settings={this.props.data.settings} />
<ImageContainer settings={this.props.data.settings} image={this.props.data.image} />
</div>
<FilterList onClick={this.props.onClick} filters={this.props.data.filters} image={this.props.data.image} />
</div>
)
}
});
// Sidebar
var Sidebar = React.createClass({
render: function() {
var onChange = this.props.onChange;
var settings = this.props.settings.map(function(setting, i) {
return <Setting onChange={onChange} name={setting.name} value={setting.value} />;
});
return (
<div className="Sidebar">
<div className="Title">Reactagram v1.0</div>
{settings}
</div>
);
}
});
var Setting = React.createClass({
render: function() {
if(this.props.name == 'hue') {
var value = this.props.value.replace('deg','');
return (
<div className="Setting">
<label><div>{this.props.name}</div><div>{value}</div></label>
<input refs={this.props.name} min="-360" max="360" step="1" onChange={this.props.onChange} id={this.props.name} type="range" defaultValue={this.props.value} />
</div>
);
} else if(this.props.name == 'contrast' || this.props.name == 'brightness') {
var value = this.props.value.replace('%','');
return (
<div className="Setting">
<label><div>{this.props.name}</div><div>{value}</div></label>
<input refs={this.props.name} min="0" max="200" step="1" onChange={this.props.onChange} id={this.props.name} type="range" defaultValue={this.props.value} />
</div>
);
} else {
var value = this.props.value.replace('%','');
return (
<div className="Setting">
<label><div>{this.props.name}</div><div>{value}</div></label>
<input refs={this.props.name} min="0" max="100" step="1" onChange={this.props.onChange} id={this.props.name} type="range" defaultValue={this.props.value} />
</div>
);
}
}
});
// Image Container
var ImageContainer = React.createClass({
render: function() {
return (
<div className="ImageContainer">
<Image settings={this.props.settings} image={this.props.image} />
</div>
);
}
});
// Image
var Image = React.createClass({
render: function() {
if(!this.props.settings == []) {
var filterString = "";
var filters = this.props.settings.map(function(filter, i) {
if(filter.name == 'hue') {
filterString = filterString + 'hue-rotate(' + filter.value + ') ';
} else {
filterString = filterString + filter.name + '(' + filter.value + ') ';
}
return filterString;
});
}
var style = {
backgroundImage: 'url(' + this.props.image + ')',
webkitFilter: filterString
};
if(!this.props.id) {
var id = 'filter-image';
} else {
var id = this.props.id;
}
return (
<div id={id} className="Image" style={style}></div>
);
}
});
// FilterList
var FilterList = React.createClass({
render: function() {
var image = this.props.image;
var onClick = this.props.onClick;
var filters = this.props.filters.map(function(filter, i) {
return <Filter onClick={onClick} id={filter.id} image={image} settings={filter.settings} />
});
return (
<div className="FilterList">
{filters}
</div>
);
}
});
// Filter
var Filter = React.createClass({
render: function() {
return (
<div className="Filter" onClick={this.props.onClick}>
<Image id={'filter-' + this.props.id} settings={this.props.settings} image={this.props.image} />
</div>
);
}
});
// Render
ReactDOM.render(
<App />,
document.getElementById('app')
);
View Compiled
This Pen doesn't use any external CSS resources.