<h1>(remix colors with sliders down below)</h1>
<script src="https://gist.github.com/lonekorean/8a6a3c508b7c71deb4070d3314900b1f.js"></script>
<div id="app">
<textarea class="filter-css" v-model="cssFilterDisplay"></textarea>
<div class="filters">
<filter-control
label="Hue Rotate" filter-key="hueRotate" min="0" max="360"
:initial-value="hueRotate" @update="updateValue">
</filter-control>
<filter-control
label="Brightness" filter-key="brightness" min="0" max="200"
:initial-value="brightness" @update="updateValue">
</filter-control>
<filter-control
label="Contrast" filter-key="contrast" min="0" max="200"
:initial-value="contrast" @update="updateValue">
</filter-control>
<filter-control
label="Saturate" filter-key="saturate" min="0" max="200"
:initial-value="saturate" @update="updateValue">
</filter-control>
</div>
</div>
<script type="text/x-template" id="filter-control-template">
<div class="filter-control">
<label :for="filterKey">{{label}}</label>
<div class="filter-inputs">
<input type="range" class="filter-range" :min="min" :max="max" v-model="number">
<input type="number" class="filter-number" :id="filterKey" :min="min" :max="max" v-model="number">
<div class="filter-unit">{{unit}}</div>
</div>
</div>
</script>
@import url('https://fonts.googleapis.com/css?family=Roboto+Mono|Roboto:400,700');
*, *::before, *::after {
box-sizing: border-box;
}
body {
margin: 20px;
font: 18px 'Roboto', sans-serif;
}
.gist, h1, #app {
max-width: 800px;
margin: 0 auto;
}
h1 {
margin-bottom: 20px;
color: #666;
text-align: center;
font-size: 32px;
}
.filter-css {
display: block;
width: 100%;
height: 110px;
margin: 30px 0 0;
padding: 10px 15px;
border: 1px dashed #ccc;
font: 15px/1.5 'Roboto Mono', monospace;
resize: none;
}
.filters {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
}
.filter-control {
width: 100%;
margin-top: 20px;
label {
display: inline-block;
font-weight: bold;
margin-bottom: 5px;
}
}
.filter-inputs {
display: flex;
align-items: center;
}
.filter-range {
flex: 1;
}
.filter-number {
width: 60px;
margin-left: 10px;
text-align: right;
font: 15px/1.5 'Roboto Mono', monospace;
}
.filter-unit {
margin-left: 5px;
width: 30px;
}
@media only screen and (min-width: 600px) {
.filter-css {
height: 90px;
}
.filter-control {
width: calc(50% - 20px);
}
}
View Compiled
Vue.component('filter-control', {
template: '#filter-control-template',
props: ['label', 'filterKey', 'initialValue', 'min', 'max'],
data() {
return {
number: 0,
unit: ''
}
},
computed: {
value() {
return (this.number || 0) + this.unit;
}
},
watch: {
value() {
this.$emit('update', this.filterKey, this.value);
}
},
created() {
// parse an inital value like "50%" to get 50 and "%"
let parsedInitialValue = this.initialValue.toString().match(/(\d+)(\D*)/);
if (parsedInitialValue && parsedInitialValue.length === 3) {
this.number = +parsedInitialValue[1];
this.unit = parsedInitialValue[2]
}
}
});
new Vue({
el: '#app',
data: {
hueRotate: '0deg',
brightness: '70%',
contrast: '100%',
saturate: '150%',
codeLineEls: document.querySelectorAll('.blob-code')
},
computed: {
cssFilterValue() {
let filters = [];
this.appendFilterPart(filters, 'hue-rotate', this.hueRotate, '0deg');
this.appendFilterPart(filters, 'brightness', this.brightness, '100%');
this.appendFilterPart(filters, 'contrast', this.contrast, '100%');
this.appendFilterPart(filters, 'saturate', this.saturate, '100%');
return filters.join(' ') || 'none';
},
cssFilterDisplay() {
return 'body .gist .blob-code {\n' +
' filter: ' + this.cssFilterValue + ';\n' +
'}';
}
},
watch: {
cssFilterValue() {
this.setCodeLineStyles();
}
},
created() {
this.setCodeLineStyles();
},
methods: {
updateValue: function(valueKey, value) {
this[valueKey] = value;
},
appendFilterPart(filters, filterName, value, ignoreValue) {
if (value !== ignoreValue) {
filters.push(filterName + '(' + value + ')');
}
},
setCodeLineStyles() {
this.codeLineEls.forEach((el) => {
el.style.filter = this.cssFilterValue;
});
}
}
});
This Pen doesn't use any external CSS resources.