Animating Registered Custom Properties
Experiments with animating registered custom properties.
by Travis Almand
HTML
<div id="panel">
<label id="duration"><span>duration: </span><input value="1s" /></label>
<label id="timing_function">
<span>timing-function</span>
<select>
<option>linear</option>
<option selected>ease</option>
<option>ease-in</option>
<option>ease-out</option>
<option>ease-in-out</option>
<option>cubic-bezier(0,2,1,-1)</option>
<option>cubic-bezier(0,.5,.5,1.5)</option>
</select>
</label>
<button id="update">update</button>
</div>
<div class="container hover"></div>
<div class="container class"></div>
<div class="container range">
<input type="range" min="-100" max="100" step="1" value="0" />
</div>
<div class="container button">
<button data-value="-100">-100</button>
<button data-value="-50">-50</button>
<button data-value="0">0</button>
<button data-value="50">50</button>
<button data-value="100">100</button>
</div>
<div class="container digital">
<div class="hours"></div>
<div class="minutes"></div>
<div class="seconds"></div>
</div>
<div class="container analog">
<div class="clock">
<div class="hours"></div>
<div class="minutes"></div>
<div class="seconds"></div>
</div>
</div>
SCSS
@import url('https://fonts.googleapis.com/css?family=Luckiest+Guy');
html,
body {
color: rebeccapurple;
font-family: 'Luckiest Guy', sans-serif;
margin: 0;
padding: 0;
}
body {
--duration: 1s;
--timingFunction: 'ease';
}
p {
margin: 10px;
}
button {
background-color: #fff;
border-color: rebeccapurple;
border-radius: 10px;
color: rebeccapurple;
cursor: pointer;
font-family: 'Luckiest Guy', sans-serif;
font-size: 20px;
outline: none;
padding: 5px 10px;
}
#panel {
border: 3px solid rebeccapurple;
margin: 20px;
padding: 20px;
label {
align-items: center;
display: flex;
margin: 5px 0;
span {
margin-right: 5px;
}
}
input {
border: 1px solid rebeccapurple;
color: rebeccapurple;
flex-grow: 1;
font-family: monospace;
font-size: 20px;
padding: 0 10px;
}
select {
border: 1px solid rebeccapurple;
color: rebeccapurple;
flex-grow: 1;
font-size: 20px;
}
}
.container {
border: 3px solid rebeccapurple;
font-size: 40px;
margin: 20px;
padding: 10px;
position: relative;
text-align: center;
transition-duration: var(--duration);
transition-property: --integer;
transition-timing-function: var(--timingFunction);
&::before {
display: block;
font-size: 16px;
left: 10px;
position: absolute;
top: 0;
}
}
.hover {
--integer: 0;
counter-reset: integer var(--integer);
&:hover {
--integer: 100;
}
&::before {
content: 'hover based';
}
&::after {
content: counter(integer);
//content: counter(integer, upper-roman);
//content: counter(integer, decimal-leading-zero);
//content: counter(integer, lower-alpha);
display: block;
}
}
.class {
--integer: 0;
counter-reset: integer var(--integer);
&.active {
--integer: 100;
}
&::before {
content: 'class based (click)';
}
&::after {
content: counter(integer);
display: block;
}
}
.range {
--integer: 0;
...
JavaScript
CSS.registerProperty({
name: '--integer',
syntax: '<integer>',
inherits: false,
initialValue: '0'
});
CSS.registerProperty({
name: '--analog',
syntax: '<angle>',
inherits: false,
initialValue: '0deg'
});
document.querySelector('#update').addEventListener('click', function () {
const duration = document.querySelector('#duration input').value;
const timingFunction = document.querySelector('#timing_function select').value;
document.body.style.cssText = `--duration: ${duration}; --timingFunction: ${timingFunction};`;
});
document.querySelector('.class').addEventListener('click', function () {
this.classList.toggle('active');
});
document.querySelector('.range input').addEventListener('input', function () {
this.parentElement.style.cssText = `--integer: ${this.value}`;
});
document.querySelectorAll('.button button').forEach(button => {
button.addEventListener('click', function () {
this.parentElement.style.cssText = `--integer: ${parseInt(this.dataset.value)}`;
});
});
window.setInterval(function () {
const digital = document.querySelector('.digital');
const date = new Date();
digital.querySelector('.hours').style.cssText = `--hours: ${date.getHours()}`;
digital.querySelector('.minutes').style.cssText = `--minutes: ${date.getMinutes()}`;
digital.querySelector('.seconds').style.cssText = `--seconds: ${date.getSeconds()}`;
}, 1000);
window.setInterval(function () {
const analog = document.querySelector('.analog');
const date = new Date();
const hoursAngle = date.getHours() * 30 - 180;
const minutesAngle = date.getMinutes() * 6 - 180;
const secondsAngle = date.getSeconds() * 6 - 180;
analog.querySelector('.hours').style.cssText = `--analog: ${hoursAngle}deg`;
analog.querySelector('.minutes').style.cssText = `--analog: ${minutesAngle}deg`;
analog.querySelector('.seconds').style.cssText = `--analog: ${secondsAngle}deg`;
});