Using Registered CSS Custom Properties and the Paint API
Paint API experiment with using transition for animation support.
by Travis Almand
HTML
<h2>Custom: color, width, duration</h2>
<pre>
--paint-outline-color: rebeccapurple;
--paint-outline-duration: 2s;
--paint-outline-width: 20;
</pre>
<div class="container" data-paint="outline"></div>
<h2>Default: no custom properties</h2>
<div class="container2" data-paint="outline"></div>
SCSS
html {
box-sizing: border-box;
}
*, *:before, *:after {
box-sizing: inherit;
}
html,body {
margin: 0;
padding: 0;
}
.container {
--paint-outline-color: rebeccapurple;
--paint-outline-duration: 2s;
--paint-outline-width: 20;
border: 3px solid rebeccapurple;
height: 200px;
margin: 50px auto;
transition-timing-function: ease;
//transition-timing-function: ease-in;
//transition-timing-function: ease-out;
//transition-timing-function: ease-in-out;
//transition-timing-function: linear;
//transition-timing-function: cubic-bezier(.5,1,1,0);
//transition-timing-function: steps(2, jump-end);
width: 200px;
&:hover {
//--paint-outline-color: green;
//--paint-outline-width: 20;
}
}
.container2 {
background-color: gainsboro;
height: 200px;
margin: 50px auto;
width: 200px;
}
[data-paint="outline"] {
--paint-outline-tick: 0;
position: relative;
}
[data-paint="outline"]:hover {
background-image: paint(paint-outline);
transition-duration: var(--paint-outline-duration);
transition-property: --paint-outline-tick, --paint-outline-color, --paint-outline-width;
}
[data-paint="outline"] .paintOutline-hitbox {
height: 100%;
left: 0;
position: absolute;
top: 0;
width: 100%;
}
[data-paint="outline"] .paintOutline-hitbox:hover {
clip-path: polygon(0% 0%, 100% 0%, 100% 100%, 0% 100%);
z-index: 2;
}
[data-paint-side="0"] {
clip-path: polygon(0% 0%, 100% 0%, 50% 50%);
}
[data-paint-side="1"] {
clip-path: polygon(100% 0%, 100% 100%, 50% 50%);
}
[data-paint-side="2"] {
clip-path: polygon(0% 100%, 100% 100%, 50% 50%);
}
[data-paint-side="3"] {
clip-path: polygon(0% 0%, 0% 100%, 50% 50%);
}
JavaScript
function paintOutlineInit (outline) {
const paintOutlineMouseover = e => {
const target = e.target;
if (target.classList.contains('paintOutline-hitbox')) {
const parent = target.parentElement;
const duration = parseFloat(window.getComputedStyle(parent).getPropertyValue('--paint-outline-duration')) * 1000;
let side = parseInt(target.dataset.paintSide);
let x = e.clientX - parent.offsetLeft;
let y = e.clientY - parent.offsetTop;
x = side === 3 ? 0 : x;
x = side === 1 ? target.offsetWidth : x;
y = side === 0 ? 0 : y;
y = side === 2 ? target.offsetWidth : y;
parent.style.cssText = `--paint-outline-tick: ${duration}; --paint-outline-x: ${x}; --paint-outline-y: ${y}; --paint-outline-side: ${side};`;
}
}
const paintOutlineMouseout = e => {
const target = e.target;
const parent = target.parentElement;
parent.style.cssText = '--paint-outline-tick: 0;';
}
outline.insertAdjacentHTML('beforeend', '<div class="paintOutline-hitbox" data-paint-side="0"></div><div class="paintOutline-hitbox" data-paint-side="1"></div><div class="paintOutline-hitbox" data-paint-side="2"></div><div class="paintOutline-hitbox" data-paint-side="3"></div>');
outline.addEventListener('mouseover', paintOutlineMouseover);
outline.addEventListener('mouseout', paintOutlineMouseout);
}
CSS.registerProperty({
name: '--paint-outline-color',
syntax: '<color>',
inherits: false,
initialValue: 'rgba(0, 0, 0, 1)'
});
CSS.registerProperty({
name: '--paint-outline-width',
syntax: '<integer>',
inherits: false,
initialValue: '5'
});
CSS.registerProperty({
name: '--paint-outline-duration',
syntax: '<time>',
inherits: false,
initialValue: '500ms'
});
CSS.registerProperty({
name: '--paint-outline-tick',
syntax: '<integer>',
inherits: false,
initialValue: '0'
});
CSS.paintWorklet.addModule(URL.createObjectURL(new Blob([`
class PaintOutline {
static get...