136 lines
3.4 KiB
JavaScript
136 lines
3.4 KiB
JavaScript
export function $(expr, con) {
|
|
return typeof expr === 'string'
|
|
? (con || document).querySelector(expr)
|
|
: expr || null;
|
|
}
|
|
|
|
export function createSVG(tag, attrs) {
|
|
const elem = document.createElementNS('http://www.w3.org/2000/svg', tag);
|
|
for (let attr in attrs) {
|
|
if (attr === 'append_to') {
|
|
const parent = attrs.append_to;
|
|
parent.appendChild(elem);
|
|
} else if (attr === 'innerHTML') {
|
|
elem.innerHTML = attrs.innerHTML;
|
|
} else if (attr === 'clipPath') {
|
|
elem.setAttribute('clip-path', 'url(#' + attrs[attr] + ')');
|
|
} else {
|
|
elem.setAttribute(attr, attrs[attr]);
|
|
}
|
|
}
|
|
return elem;
|
|
}
|
|
|
|
export function animateSVG(svgElement, attr, from, to) {
|
|
const animatedSvgElement = getAnimationElement(svgElement, attr, from, to);
|
|
|
|
if (animatedSvgElement === svgElement) {
|
|
// triggered 2nd time programmatically
|
|
// trigger artificial click event
|
|
const event = document.createEvent('HTMLEvents');
|
|
event.initEvent('click', true, true);
|
|
event.eventName = 'click';
|
|
animatedSvgElement.dispatchEvent(event);
|
|
}
|
|
}
|
|
|
|
function getAnimationElement(
|
|
svgElement,
|
|
attr,
|
|
from,
|
|
to,
|
|
dur = '0.4s',
|
|
begin = '0.1s',
|
|
) {
|
|
const animEl = svgElement.querySelector('animate');
|
|
if (animEl) {
|
|
$.attr(animEl, {
|
|
attributeName: attr,
|
|
from,
|
|
to,
|
|
dur,
|
|
begin: 'click + ' + begin, // artificial click
|
|
});
|
|
return svgElement;
|
|
}
|
|
|
|
const animateElement = createSVG('animate', {
|
|
attributeName: attr,
|
|
from,
|
|
to,
|
|
dur,
|
|
begin,
|
|
calcMode: 'spline',
|
|
values: from + ';' + to,
|
|
keyTimes: '0; 1',
|
|
keySplines: cubic_bezier('ease-out'),
|
|
});
|
|
svgElement.appendChild(animateElement);
|
|
|
|
return svgElement;
|
|
}
|
|
|
|
function cubic_bezier(name) {
|
|
return {
|
|
ease: '.25 .1 .25 1',
|
|
linear: '0 0 1 1',
|
|
'ease-in': '.42 0 1 1',
|
|
'ease-out': '0 0 .58 1',
|
|
'ease-in-out': '.42 0 .58 1',
|
|
}[name];
|
|
}
|
|
|
|
$.on = (element, event, selector, callback) => {
|
|
if (!callback) {
|
|
callback = selector;
|
|
$.bind(element, event, callback);
|
|
} else {
|
|
$.delegate(element, event, selector, callback);
|
|
}
|
|
};
|
|
|
|
$.off = (element, event, handler) => {
|
|
element.removeEventListener(event, handler);
|
|
};
|
|
|
|
$.bind = (element, event, callback) => {
|
|
event.split(/\s+/).forEach(function (event) {
|
|
element.addEventListener(event, callback);
|
|
});
|
|
};
|
|
|
|
$.delegate = (element, event, selector, callback) => {
|
|
element.addEventListener(event, function (e) {
|
|
const delegatedTarget = e.target.closest(selector);
|
|
if (delegatedTarget) {
|
|
e.delegatedTarget = delegatedTarget;
|
|
callback.call(this, e, delegatedTarget);
|
|
}
|
|
});
|
|
};
|
|
|
|
$.closest = (selector, element) => {
|
|
if (!element) return null;
|
|
|
|
if (element.matches(selector)) {
|
|
return element;
|
|
}
|
|
|
|
return $.closest(selector, element.parentNode);
|
|
};
|
|
|
|
$.attr = (element, attr, value) => {
|
|
if (!value && typeof attr === 'string') {
|
|
return element.getAttribute(attr);
|
|
}
|
|
|
|
if (typeof attr === 'object') {
|
|
for (let key in attr) {
|
|
$.attr(element, key, attr[key]);
|
|
}
|
|
return;
|
|
}
|
|
|
|
element.setAttribute(attr, value);
|
|
};
|