<div class="wrapper">
<hgroup>
<h1>CSS Filter Glass</h1>
<h3>drag an image file from your desktop onto the glass</h3>
</hgroup>
<div class="window">
<div class="glass left img"></div>
<div class="handle"></div>
<div class="glass right filter-grayscale img"></div>
</div>
<ul class="filter-choice">
<li data-filter="grayscale">
<input type="radio" name="filter-choice-group" value="grayscale" class="filter-grayscale img" checked>
</li>
<li data-filter="sepia">
<input type="radio" name="filter-choice-group" value="sepia" class="filter-sepia img">
</li>
<li data-filter="blur">
<input type="radio" name="filter-choice-group" value="blur" class="filter-blur img">
</li>
<li data-filter="brightness">
<input type="radio" name="filter-choice-group" value="brightness" class="filter-brightness img">
</li>
<li data-filter="contrast">
<input type="radio" name="filter-choice-group" value="contrast" class="filter-contrast img">
</li>
<li data-filter="hue-rotate">
<input type="radio" name="filter-choice-group" value="hue-rotate" class="filter-hue-rotate img">
</li>
<li data-filter="invert">
<input type="radio" name="filter-choice-group" value="invert" class="filter-invert img">
</li>
<li data-filter="saturate">
<input type="radio" name="filter-choice-group" value="saturate" class="filter-saturate img">
</li>
</ul>
</div>
@import "compass/css3";
$window-width: 800px;
$window-height: $window-width * 9/16; /* keep 16:9 ratio */
$glass-width: $window-width / 2;
$handle-width: 2px;
.wrapper {
width: 940px;
margin: 20px auto;
position: relative;
color: #fff;
}
/*=== Title ===*/
hgroup {
text-align: center;
margin-bottom: 20px;
h1 {
font-size: 30px;
margin-bottom: 5px;
@include text-shadow(0px 1px 3px #999);
}
h3 {
font-weight: 300;
}
}
/*=== Main View ===*/
.window {
width: $window-width;
height: $window-height;
position: relative;
overflow: hidden;
float: left;
margin: 0 20px 20px 70px;
@include border-radius(5px);
@include box-shadow(0px 1px 2px 0px #999, inset 0px 1px 1px 0px white);
&.dragover {
animation: shake 0.5s infinite linear;
}
}
.glass {
width: $glass-width;
height: 100%;
position: absolute;
&.left {
left: 0px;
background-image: none;
background-size: $window-width;
background-position: top left;
@include border-top-left-radius(5px);
@include border-bottom-left-radius(5px);
}
&.right {
right: 0px;
background-image: none;
background-size: $window-width;
background-position: top right;
margin: -1px;
padding: 1px;
@include border-top-right-radius(8px); /* should be 5px, chrome overflow fix */
@include border-bottom-right-radius(5px);
}
}
.handle {
width: $handle-width;
height: 100%;
position: absolute;
background: gray;
z-index: 1;
cursor: pointer;
left: $glass-width - ($handle-width / 2);
&:after {
content: '‹ ›';
text-align: center;
font-family: serif;
font-size: 24px;
line-height: 26px;
display: block;
position: absolute;
width: 30px;
height: 30px;
border-radius: 100%;
top: 50%;
left: 50%;
margin: -15px 0 0 -15px;
background: #ddd;
@include box-shadow(0px 1px 1px 0px #222, inset 0px 1px 1px 0px white);
}
}
/*=== Filter Menu ===*/
.filter-choice {
float: left;
width: 50px;
height: $window-height;
position: relative;
> li {
width: 50px;
height: 28px;
margin-bottom: 10px;
position: relative;
@include border-radius(5px);
&.alt {
position: absolute;
bottom: 0;
text-align: center;
img:hover {
background: #999;
@include border-radius(100%);
@include box-shadow(0px 0px 1px 0px #999);
}
}
&:hover:after {
content: attr(data-filter);
display: block;
position: absolute;
width: 100px;
height: 100%;
line-height: 26px;
font-size: 12px;
top: 0;
left: 115%;
@include filter(none);
}
input {
display: block;
width: 50px;
height: 28px;
cursor: pointer;
margin: 0;
background-size: 100%;
background-repeat: no-repeat;
@include appearance(none);
@include border-radius(5px);
@include transition(box-shadow 0.2s);
@include box-shadow(0px 1px 2px 0px #999, inset 0px 1px 1px 0px white);
&:hover {
@include box-shadow(0px 1px 2px 1px #999, inset 0px 1px 1px 0px white);
}
&:active, &:checked {
@include box-shadow(inset 0px 1px 3px 1px #999, 0px 1px 1px 0px white);
}
}
}
}
/*=== Filter Classes ===*/
.filter-grayscale {
@include filter(grayscale(1));
}
.filter-sepia {
@include filter(sepia(1));
}
.filter-blur {
@include filter(blur(5px));
}
.filter-brightness {
@include filter(brightness(0.5));
}
.filter-contrast {
@include filter(contrast(10));
}
.filter-hue-rotate {
@include filter(hue-rotate(90deg));
}
.filter-invert {
@include filter(invert(1));
}
.filter-saturate {
@include filter(saturate(10));
}
input[type="radio"].filter-blur { /* blur filter fix for thumb */
@include filter(blur(1px));
}
/*=== Animations ===*/
@keyframes shake {
0% {
@include transform(rotate(0deg));
}
25% {
@include transform(rotate(2deg));
}
75% {
@include transform(rotate(-2deg));
}
100% {
@include transform(rotate(0deg));
}
}
body {
font-family: 'Open Sans', sans-serif;
background: #ddd;
background-image: url(https://cdn.pixabay.com/photo/2018/09/14/22/46/rail-3678287_1280.jpg);
}
View Compiled
var imageWindow = $('.window');
var leftGlass = $('.glass.left');
var rightGlass = $('.glass.right');
var initialWidth = leftGlass.width();
// Drag and Drop event listeners
imageWindow.on('dragover', onDragOver);
imageWindow.on('dragenter', onDragEnter);
imageWindow.on('dragleave', onDragLeave);
imageWindow.on('drop', onDrop);
// Glass handle drag behavior
$('.handle').draggable({axis: 'x',
containment: 'parent',
drag: function(event, ui) {
var handleOffset = ui.position.left - initialWidth;
leftGlass.width(initialWidth + handleOffset);
rightGlass.width(initialWidth - handleOffset);
}
});
// Filter chooser events
$('input[name="filter-choice-group"]').on('click', function() {
// remove any previous filter-* class from right glass
rightGlass.removeClass(function (index, css) {
return (css.match (/\bfilter-\S+/g) || []).join(' ');
});
// add the selected filter class to the right glass
rightGlass.addClass('filter-' + $(this).val());
});
setImage('https://images.unsplash.com/photo-1544390951-7b9e6547fb28?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=2250&q=80');
function setImage(uri) {
$('.img').css('background-image', 'url('+uri+')');
}
// Drag And Drop event handlers
function onDragOver(evt) {
evt.stopPropagation();
evt.preventDefault();
evt.originalEvent.dataTransfer.dropEffect = 'copy';
}
function onDragEnter(evt) {
evt.stopPropagation();
evt.preventDefault();
$('.window').addClass('dragover');
}
function onDragLeave(evt) {
evt.stopPropagation();
evt.preventDefault();
$('.window').removeClass('dragover');
}
// Thanks Eric Bidelman :)
function onDrop(evt) {
evt.stopPropagation();
evt.preventDefault();
$('.window').removeClass('dragover');
// Allowed file types
var allowedFileTypes = /image.*/;
// Get dropped file object
var file = evt.originalEvent.dataTransfer.files[0];
// Validate allowed type, or break
if (!file.type.match(allowedFileTypes)) {
return;
}
// Setup FileReader
var reader = new FileReader();
reader.onerror = function(evt) {
console.log('FileReader error: ' + e.target.error);
}
reader.onload = (function(aFile) {
return function(evt) {
// Set dropped file as current image
setImage(evt.target.result);
}
})(file);
reader.readAsDataURL(file);
}