You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

134 lines
3.3 KiB

  1. export function $(expr, con) {
  2. return typeof expr === 'string'
  3. ? (con || document).querySelector(expr)
  4. : expr || null;
  5. }
  6. export function createSVG(tag, attrs) {
  7. const elem = document.createElementNS('http://www.w3.org/2000/svg', tag);
  8. for (let attr in attrs) {
  9. if (attr === 'append_to') {
  10. const parent = attrs.append_to;
  11. parent.appendChild(elem);
  12. } else if (attr === 'innerHTML') {
  13. elem.innerHTML = attrs.innerHTML;
  14. } else {
  15. elem.setAttribute(attr, attrs[attr]);
  16. }
  17. }
  18. return elem;
  19. }
  20. export function animateSVG(svgElement, attr, from, to) {
  21. const animatedSvgElement = getAnimationElement(svgElement, attr, from, to);
  22. if (animatedSvgElement === svgElement) {
  23. // triggered 2nd time programmatically
  24. // trigger artificial click event
  25. const event = document.createEvent('HTMLEvents');
  26. event.initEvent('click', true, true);
  27. event.eventName = 'click';
  28. animatedSvgElement.dispatchEvent(event);
  29. }
  30. }
  31. function getAnimationElement(
  32. svgElement,
  33. attr,
  34. from,
  35. to,
  36. dur = '0.4s',
  37. begin = '0.1s'
  38. ) {
  39. const animEl = svgElement.querySelector('animate');
  40. if (animEl) {
  41. $.attr(animEl, {
  42. attributeName: attr,
  43. from,
  44. to,
  45. dur,
  46. begin: 'click + ' + begin, // artificial click
  47. });
  48. return svgElement;
  49. }
  50. const animateElement = createSVG('animate', {
  51. attributeName: attr,
  52. from,
  53. to,
  54. dur,
  55. begin,
  56. calcMode: 'spline',
  57. values: from + ';' + to,
  58. keyTimes: '0; 1',
  59. keySplines: cubic_bezier('ease-out'),
  60. });
  61. svgElement.appendChild(animateElement);
  62. return svgElement;
  63. }
  64. function cubic_bezier(name) {
  65. return {
  66. ease: '.25 .1 .25 1',
  67. linear: '0 0 1 1',
  68. 'ease-in': '.42 0 1 1',
  69. 'ease-out': '0 0 .58 1',
  70. 'ease-in-out': '.42 0 .58 1',
  71. }[name];
  72. }
  73. $.on = (element, event, selector, callback) => {
  74. if (!callback) {
  75. callback = selector;
  76. $.bind(element, event, callback);
  77. } else {
  78. $.delegate(element, event, selector, callback);
  79. }
  80. };
  81. $.off = (element, event, handler) => {
  82. element.removeEventListener(event, handler);
  83. };
  84. $.bind = (element, event, callback) => {
  85. event.split(/\s+/).forEach(function (event) {
  86. element.addEventListener(event, callback);
  87. });
  88. };
  89. $.delegate = (element, event, selector, callback) => {
  90. element.addEventListener(event, function (e) {
  91. const delegatedTarget = e.target.closest(selector);
  92. if (delegatedTarget) {
  93. e.delegatedTarget = delegatedTarget;
  94. callback.call(this, e, delegatedTarget);
  95. }
  96. });
  97. };
  98. $.closest = (selector, element) => {
  99. if (!element) return null;
  100. if (element.matches(selector)) {
  101. return element;
  102. }
  103. return $.closest(selector, element.parentNode);
  104. };
  105. $.attr = (element, attr, value) => {
  106. if (!value && typeof attr === 'string') {
  107. return element.getAttribute(attr);
  108. }
  109. if (typeof attr === 'object') {
  110. for (let key in attr) {
  111. $.attr(element, key, attr[key]);
  112. }
  113. return;
  114. }
  115. element.setAttribute(attr, value);
  116. };