Compare commits

..

187 commits

Author SHA1 Message Date
thednp 426a45596c Update v2.2.2 2021-12-27 17:36:00 +02:00
thednp 1dfd1e6109
Update svgMorph.html 2021-12-08 22:19:28 +00:00
thednp 19f9df8a93 Update demo 2021-12-08 23:48:42 +02:00
thednp fd8d4ba7c8 Update demo 2021-11-13 18:54:21 +02:00
thednp 62854fba05 Changes:
* updated dependencies
* ESM is now ES6+
2021-05-31 07:47:31 +00:00
thednp af6f86f983 Easing 'easingCircularOut' typo. 2021-03-31 08:29:40 +00:00
thednp 990f6aea93 Fixed some IE issues with new code base. 2021-03-31 08:08:59 +00:00
thednp f524f7ecaf 2021-03-30 11:26:48 +00:00
thednp dd664e9bc7 2021-03-30 11:19:25 +00:00
thednp 72b1847a0a Changes:
* added ESLint and updated all code base 
* updated SVGPathCommander, CubicBezier, shorter-js, minifill
* updated polyfills
* minor CSS fixes
2021-03-30 09:23:30 +00:00
thednp df3505b3c3 Changes:
* updated SVGPathCommander
* fixed small typo issue with SVGDraw
* version bump
2021-01-09 13:31:02 +00:00
thednp ab686c7cdb 2020-09-24 05:07:44 +00:00
thednp 541daab882 Changes, lots of them:
* Updated SVGPathCommander to make use of the new features, the SVG morph components can now process paths in Node.js
* svgCubicMorph component now can morph lines properly, as shown in the updated demo
* svgMorph component also updated
* documentation updates for more clarity with the morph components
2020-09-23 18:59:42 +00:00
thednp 5ce011d41a Changes:
* Solved some minor issue with `pathToCurve` utility
* Updated SVGPathCommander
2020-09-02 13:26:43 +00:00
thednp 441a9ecedb 2020-08-31 15:35:20 +00:00
thednp 17ce738de3 2020-08-18 12:31:44 +00:00
thednp 60950ffd02 2020-08-18 12:24:08 +00:00
thednp a62165820f 2020-08-16 20:15:17 +00:00
thednp eb72644cfe 2020-08-14 11:50:15 +00:00
thednp 307f59a7a4 2020-07-27 09:40:03 +00:00
thednp ffeb00d326 2020-07-03 09:24:10 +00:00
thednp 1b82c657a1 2020-07-02 10:57:47 +00:00
thednp ca0b3c6a15 2020-07-02 10:49:06 +00:00
thednp 997972c35f 2020-06-24 05:44:51 +00:00
thednp 48dc93e438 2020-06-24 05:38:42 +00:00
thednp d6e9319dc3 2020-06-23 22:07:51 +00:00
thednp 732312431b 2020-06-23 16:37:54 +00:00
thednp 918d566d1e 2020-06-20 10:23:40 +00:00
thednp 18cfa78abb 2020-06-20 09:20:10 +00:00
thednp 88a28c8d19 2020-06-16 15:22:06 +00:00
thednp 784e092960 2020-06-16 15:18:29 +00:00
thednp a8c06c1dad 2020-06-16 14:37:41 +00:00
thednp dab615ea54 2020-06-12 08:03:08 +00:00
thednp 8c52e9f9e7 Demo fixes 2020-06-12 06:12:19 +00:00
thednp 7c12028d46 2020-06-11 15:41:39 +00:00
thednp 97c795a972 2020-06-11 15:30:34 +00:00
thednp ced91131a3 2020-06-11 05:01:56 +00:00
thednp 94fe92738c 2020-06-10 14:34:18 +00:00
thednp 7e098ab567 2020-06-10 13:40:18 +00:00
thednp 96f7709012 2020-06-10 13:20:07 +00:00
thednp 2c756c90b7 2020-06-09 21:34:47 +00:00
thednp acbeb00a48 2020-06-09 20:46:08 +00:00
thednp 1d8cf96422 2020-06-09 20:23:36 +00:00
thednp b51196cb86 2020-06-09 20:22:33 +00:00
thednp cb72a478c2 2020-06-09 20:08:43 +00:00
thednp e313cb369f Added a simple example for #96 2019-11-07 17:49:09 +02:00
thednp 81a86026c5 Removed jQuery plugin from documentation 2019-02-01 12:42:31 +02:00
thednp bd73c423b6 Updating gh-pages as well 2019-02-01 12:06:43 +02:00
thednp 96d7f8952a Trying to solve scroll with EDGE/Safari 2018-11-05 13:27:08 +02:00
thednp 2f1fed31e3 Cleanup 2018-10-19 08:56:10 +03:00
thednp 52bfc84ef7 Another typo. 2018-10-19 08:47:51 +03:00
thednp 1e5deb1f1c Typo 2018-10-18 12:09:27 +03:00
thednp 850b1e5960 Fix SVG Plugin function getPolyLength for <polyline> elements. 2018-10-18 11:47:43 +03:00
thednp 393d0a0a2b Added callback reference #73 2018-07-19 09:25:29 +03:00
thednp d33b3b313b A possible fix for #81 2018-07-19 08:39:11 +03:00
thednp 7cd7fe226c Update start.html 2017-10-05 12:57:13 +03:00
thednp c977185621 Update dist files. 2017-10-05 12:55:29 +03:00
thednp 6192946161 Right, changes:
* fixed `scroll` tweening with new webkit browsers versions
* removed `isMobile` for transforms and svg transforms for now
* code cleanup
2017-09-29 00:56:18 +03:00
thednp 9303db19f1 Update Twitter share links. 2017-09-08 11:43:15 +03:00
thednp da327643c7 Improvement for https://github.com/thednp/kute.js/issues/63 2017-05-29 16:03:59 +03:00
thednp af9e57b1f7 Changes:
* code cleanup, more readable code in plugins
* documentation fixes, the easing selectors fixed
2017-02-02 01:23:21 +02:00
thednp e0274126d8 Changes:
* improved `selector` utility
* improved/simplified `getPrefix()` utility
* improve code readability for the core engine
* improved demos
2017-01-31 19:15:13 +02:00
thednp 024fdd2322 Changes:
* Disabled SVG Plugin related scripts on IE8
* Some documentation fine tuning
2017-01-23 18:54:59 +02:00
thednp 449f5ed1e3 Minor documentation fixes:
* Documentation updates
* Fixed navigation on IE8
2017-01-23 15:55:10 +02:00
thednp 293098c0c6 Some minor documentation fixes. 2017-01-14 23:07:41 +02:00
thednp b414705778 Fixed missing default value for repeatDelay. 2017-01-14 22:03:21 +02:00
thednp 709e78ac3b Changes:
* Included fix https://github.com/thednp/kute.js/pull/49
* Implemented https://github.com/thednp/kute.js/issues/47
* Documentation updates
2017-01-03 00:48:28 +02:00
thednp 7b5bb32c17 Mostly doc updates 2016-12-20 22:06:23 +02:00
thednp 2f4810a0a1 SVG transforms now work properly with fromTo() method, no need to do crossCheck for the method 2016-12-16 22:23:23 +02:00
thednp 3f4e0c4d47 Trying to fix a small issue with SVG transforms when initial rotations/scale/skews aren't 0 (zero) 2016-12-14 16:37:50 +02:00
thednp d5d29d53d2 Adjustments for the SVG Plugin:
* `draw` property will work with 2 decimals for more precise animation
* `svgTransform` will also work with  decimals for translation and rotation
* regular transform will need 2 decimals for translation and rotation
2016-12-14 14:41:50 +02:00
thednp 585de95907 LOL 2016-12-11 04:24:18 +02:00
thednp ead5728dcd Major change:
* fixing SVG transforms for good https://github.com/thednp/kute.js/issues/33
* major changes to the tween objects https://github.com/thednp/kute.js/issues/39
* back to Infinity repeat https://github.com/thednp/kute.js/issues/43
* all round performance improvements
2016-12-11 03:48:37 +02:00
thednp 05c15ee98c Issue with text plugin demo 2016-11-30 18:48:20 +02:00
thednp a82263d4b4 Small typo with Attributes plugin 2016-11-30 18:18:53 +02:00
thednp 4f7682cdca Changes:
* Now all parseProperty functions and prepareStart functions are bound to `this`, the tween object
* changed the Tween constructor for lighter size
* rewritten alot of code for readability
* Documentation updates
2016-11-30 18:12:11 +02:00
thednp 1cf5f87f82 Testing new build. 2016-11-28 03:07:16 +02:00
thednp f07f783731 2016-11-26 19:00:32 +02:00
thednp 3005967c32 2016-11-26 18:56:23 +02:00
thednp 6db0e4b92d 2016-11-26 18:31:59 +02:00
thednp 8d70c4a6c7 2016-11-26 18:24:30 +02:00
thednp 5660465f20 2016-11-26 18:22:38 +02:00
thednp 93ab1779a8 2016-11-26 18:15:00 +02:00
thednp d2141361a3 2016-11-26 18:00:23 +02:00
thednp 6ff8e274d1 Documentation updates and performance test page reworked with safety features. 2016-11-26 17:47:17 +02:00
thednp 90dee25c42 Changes:
* Fixed some bug with Attr plugin
* preparing code for a bundle build script
* documentation updates
2016-11-25 22:54:27 +02:00
thednp 5b61b7a1fe Changes:
* fixed minor issue with `borderRadius` on legacy browsers
* removed CSS properties from SVG plugin (forgot in previous commit)
* simplified core box model properties and CSS transform tween objects to always use `px` or `deg` as unit, with converted values
* implemented the `crossCheck` function for SVG transforms (for stacking transform chains) and the SVG morph
* simplified the `coords` interpolation used for SVG morph
* general code cleanup
* simplified the `processEasing` since the additional easing plugins have been removed, the old version can be found in the js file for easing examples page
* doc updates
2016-11-24 22:57:33 +02:00
thednp 112b35aa5f Changes:
* removed CSS props from SVG plugins
* improved CSS transform performance by creating a more simple tween object
* improved radius properties performance 
* removed Bezier and Physics plugins
* fixes scroll animation performance bottlenecks
* code cleanup and a more readable code
* documentation updates
2016-11-23 20:42:01 +02:00
thednp 178cac445e Removed some recursion from plugins. 2016-10-13 15:32:23 +03:00
thednp b8e98b9764 Minor issue with trueColor fixed after previously changed. 2016-10-13 06:09:23 +03:00
thednp 2121a43c8a Type 2016-10-13 05:46:00 +03:00
thednp c8f46895a8 Changes:
* Recursive functions found in `examples.js` and `css.js`. Now `Maximum call stack size exceeded` error is completely eliminated
* Further optimization for the Tween object constructor
2016-10-12 06:44:42 +03:00
thednp 663e98d810 Docs 2016-10-11 11:19:45 +03:00
thednp 2a1b6aebcf docs 2016-10-10 02:34:32 +03:00
thednp 19bd7f7000 docs 2016-10-09 07:58:02 +03:00
thednp 61182d9b68 Small fix to prevent Uncaught RangeError: Maximum call stack size exceeded error. 2016-10-09 06:10:41 +03:00
thednp 670d18894c Changes:
* Removing the `getBestIndex` utility https://github.com/thednp/kute.js/issues/41
* simplify the SVG plugin
* documentation updates
2016-10-09 05:45:21 +03:00
thednp 352069e5c4 Related to CommonJS https://github.com/thednp/kute.js/issues/40#issuecomment-252348599 2016-10-07 23:10:34 +03:00
thednp 188bd1953b Attempting to fix https://github.com/thednp/kute.js/issues/40 2016-10-07 22:59:35 +03:00
thednp 0348584c94 Documentation typo and README updates 2016-10-04 14:39:34 +03:00
thednp 6a6b7cfad0 2016-10-03 19:42:44 +03:00
thednp 901edb1a8b Trying to simplify the tween object, as described
https://github.com/thednp/kute.js/issues/39

Also fixing https://github.com/thednp/kute.js/issues/40

Minor documentation changes
2016-10-03 19:26:17 +03:00
thednp 56883c77a4 Trying to filter this error ReferenceError: window is not defined https://runkit.com/npm/kute.js 2016-09-24 21:34:19 +03:00
thednp f93780061a Type 2016-09-24 12:12:29 +03:00
thednp 7bcc0049d7 2016-09-24 11:02:21 +03:00
thednp 9bbcffb7b6 Quick fixes and some change:
* Removed `dom` from KUTE object, 
* Re-added Tween to KUTE object (was deleted by mistake). 
* Updated demo and the kute-box-shadow sample plugin
2016-09-24 10:39:04 +03:00
thednp cafb243a36 Attributes Plugin can also tween color attributes: fill, stroke, stopColor. Perhaps some things can be removed from SVG Plugin. 2016-09-24 06:02:25 +03:00
thednp 94116ebe76 Changelog 1.5.7:
* changed the jQuery plugin, it's lighter and plays well with tween control methods
* changed the scope of ticker, tick, easing functions, interpolate functions, all to global, for better performance, some will only be available in the global and will be removed from KUTE object
* added transform interpolate functions
* documentation updates
2016-09-24 03:37:02 +03:00
thednp 15a4d86a8f 2016-09-23 00:43:58 +03:00
thednp 3449aa9a7e 2016-09-22 21:11:57 +03:00
thednp 1c2650a154 More experiments. 2016-09-22 20:58:25 +03:00
thednp 4ff44da4f9 2016-09-22 15:30:31 +03:00
thednp ee496ee8f6 Experimenting with exporting stuff to KUTE object, suspecting a bug with Google Chrome 2016-09-22 15:24:49 +03:00
thednp 4b3108c871 Register additional interpolate functions into the object 2016-09-22 14:46:03 +03:00
thednp f05e454f3e Type 2016-09-22 14:42:06 +03:00
thednp f1b2399d05 Code cleanup 2016-09-22 05:26:43 +03:00
thednp d5724b01e4 Minor fix/improvement with Attributes Plugin regarding current attribute value suffix. 2016-09-20 23:32:11 +03:00
thednp 1736cc9c9a SVG Plugin is broken in 1.5.4, now fixed for 1.5.5
Doc Updates for c6e7caabc6
2016-09-20 13:57:05 +03:00
thednp aba3219412 Minor fix 2016-09-20 06:52:42 +03:00
thednp 695f77c9c4 Small improvement, attribute tweening can also take into account current measurement unit if no unit is specified in end values. 2016-09-20 06:45:11 +03:00
thednp 44816ad61e Now the Attributes Plugin should be able to handle properties like 'stroke-width' or fillOpacity 2016-09-20 06:30:53 +03:00
thednp 9aef44f813 Hopefully fixing https://github.com/thednp/kute.js/issues/35 2016-09-19 20:17:17 +03:00
thednp ab8ef18953 Docs update 2016-09-18 01:16:02 +03:00
thednp 3c9683b002 Decreased default morphPrecision from 25 to 15 for better visual. 2016-09-18 01:07:15 +03:00
thednp eaf8f73b07 Replaced prototype with regular object for AttrPlugin I hope it's for better performance 2016-09-18 01:03:06 +03:00
thednp 069a20430c Missing share icons in svg.html page 2016-09-12 17:47:42 +03:00
thednp fde91644b0 NodeList isn't a good idea for the KUTE.selector utility, it's not workin in IE8.
Added a link in the documentation for a pathToAbslute utility for SVG morphing.
2016-09-12 17:09:09 +03:00
thednp e29d08f6bf Quick docs update 2016-09-07 19:09:41 +03:00
thednp 9ed385e4c2 Fixed issue with text-plugin https://github.com/thednp/kute.js/issues/36
Improved overall performance https://github.com/thednp/kute.js/issues/34
Fixed some issues with SVG Plugin https://github.com/thednp/kute.js/issues/33
Documentation updates
2016-09-03 18:35:49 +03:00
thednp 04010b4cab Fixed https://github.com/thednp/kute.js/issues/30
The script didn't handle polygon morphs when both shapes have same number of points.
2016-08-22 17:10:26 +03:00
thednp 5482d8383e * Added SVG Transforms for SVG Plugin
* Documentation/demo updates
2016-08-22 01:45:23 +03:00
thednp 7d44719816 Added support for stroking animation via draw for <ellipse>, some demo improvements. 2016-08-21 00:11:42 +03:00
thednp dfa70cd629 2016-08-20 16:46:16 +03:00
thednp d184fe310c Small demo improvements. 2016-08-20 16:44:53 +03:00
thednp f121972947 SVG Plugin added draw (stroke animations) support for additional elements: <line>, <circle>, <rect>, <polygon> and <polyline>.
https://github.com/thednp/kute.js/issues/28
2016-08-20 15:58:30 +03:00
thednp 3d8fadde3e 2016-08-19 01:30:41 +03:00
thednp 5f10bd711e 2016-08-19 01:12:53 +03:00
thednp 1d153c363a Fixed Angular related issue with SVG Plugin.
https://github.com/thednp/kute.js/issues/29
2016-08-18 22:28:15 +03:00
thednp ed59e23b77 Some documentation updates and social sharing fixes. 2016-08-17 23:40:42 +03:00
thednp b3b806279d Documentation updates, now we have cdnjs repository.
https://cdnjs.com/libraries/kute.js
2016-08-16 19:24:13 +03:00
thednp 95ecba3f67 2016-03-28 16:07:31 +03:00
thednp 2040b36e44 Added a codepen example for SVG, plus more SEO improvements. 2016-03-27 16:20:24 +03:00
thednp a666fda6e3 2016-03-26 21:32:52 +02:00
thednp 428b45f8e8 Some SEO fixes. 2016-03-26 21:27:48 +02:00
thednp ef57b574f2 Minor issue with opacity, scale. 2016-03-25 19:56:46 +02:00
thednp 45901d20ab 2016-03-24 14:49:42 +02:00
thednp 1423ab641e 2016-03-24 14:48:26 +02:00
thednp d972e26f59 Final version 1.5 commit. 2016-03-24 14:27:27 +02:00
thednp b7b82afb6c minor IE8 demo fix 2016-03-19 10:08:34 +02:00
thednp c1e1d06a9f 2016-03-18 19:18:55 +02:00
thednp ac8f5ebe05 Added Text Plugin, updated docs 2016-03-18 16:23:23 +02:00
thednp f909b46a7e 2016-03-17 16:54:13 +02:00
thednp ccd18a2855 typo 2016-03-17 09:00:32 +02:00
thednp ba36c3bf31 2016-03-16 16:28:07 +02:00
thednp 71929b7fc7 The 1.5 first commit, still testing, changelog in the comments 2016-03-16 15:44:23 +02:00
thednp ddb8212e4c Update kute.js 2016-02-18 21:33:15 +02:00
thednp 6b9b095118 Update kute.js 2016-02-18 21:30:40 +02:00
thednp a6534763af Create kute-svg.js 2016-02-18 20:46:32 +02:00
thednp 9fa0b5a328 Create kute.js 2016-02-18 20:44:34 +02:00
thednp 6943b0c7e8 Update api.html 2015-12-30 01:51:22 +02:00
thednp f00ef2dcd6 2015-10-26 13:04:58 +02:00
thednp af58612491 2015-10-26 12:04:54 +02:00
thednp 74f35db07c 2015-10-26 08:36:48 +02:00
thednp 7808f9760f fixed tween.min.js file 2015-10-18 20:38:49 +03:00
thednp 26f03f6134 2015-10-18 10:19:08 +03:00
thednp 9e693955ed 2015-10-18 09:42:40 +03:00
thednp 6860924799 Added some more features, a changelog will come with the tagged release. 2015-10-18 09:40:11 +03:00
thednp 2a15904963 Update kute-dev.js 2015-10-03 22:02:25 +03:00
thednp eec0a2751a Update kute-dev.js 2015-10-03 21:06:08 +03:00
thednp b2bfd94608 Update kute-dev.js 2015-10-03 21:03:46 +03:00
thednp f0d5096572 Update kute-dev.js 2015-10-03 20:58:57 +03:00
thednp 05748003ea Update kute-dev.js 2015-10-03 14:36:33 +03:00
thednp 7ca29f28c9 Update kute-dev.js 2015-10-03 13:35:12 +03:00
thednp 147a8e9fc8 Update kute-dev.js 2015-09-29 17:05:48 +03:00
thednp 641281daf5 Update kute-dev.js 2015-09-28 18:14:23 +03:00
thednp c2d8f0805e Update kute-dev.js 2015-09-28 17:59:54 +03:00
thednp 780e3a0793 Update kute-dev.js 2015-09-28 15:13:27 +03:00
thednp 1e1bb9306c Update kute-dev.js 2015-09-28 15:12:41 +03:00
thednp a89203dfda Update kute-dev.js 2015-09-27 23:37:40 +03:00
thednp f3353235c2 Update kute-dev.js 2015-09-27 23:35:49 +03:00
thednp fad2ff91bc Update kute-dev.js 2015-09-27 18:39:44 +03:00
thednp c4effded66 Update kute-dev.js 2015-09-26 21:58:29 +03:00
thednp d5a18b8fd5 Update kute-dev.js 2015-09-18 18:29:15 +03:00
thednp 0a24580341 Update kute-dev.js 2015-08-03 18:29:58 +03:00
thednp ae8d9d9472 Update kute-dev.js 2015-08-03 18:28:28 +03:00
thednp 7903fcd166 Rename kute-dev to kute-dev.js 2015-08-03 16:54:59 +03:00
thednp 65c14a6cce Create kute-dev 2015-08-03 16:53:17 +03:00
211 changed files with 14 additions and 19963 deletions

View file

@ -1,41 +0,0 @@
{
"extends": [
// Extend the airbnb eslint config
"airbnb-base"
// Vue
// "plugin:vue/vue3-recommended"
],
// "parser": "vue-eslint-parser",
// "parser": "eslint-plugin-vue",
"parserOptions": {
"sourceType": "module"
},
// ESLint will not look in parent folders for eslint configs
"root": false,
// An environment defines global variables that are predefined.
"env": {
"browser": true,
"es6": true,
"node": true
},
// Rule overrides
"rules": {
// Disable no-restricted-globals for global objects
"no-restricted-globals": 0,
// Disable no-params-reassign for properties
// "no-param-reassign": ["error", { "props": false }],
// Allow strict mode (we are not dealing with modules)
// "strict": [0],
// Allow use of "private methods" - impossible to satisfy
"no-underscore-dangle": 0
// Disable alert rule till we have a CE in place
// "no-alert": 0
// Allow extensions on imports
// "import/extensions": 0,
// Allow exporting mutable 'let' binding
// "import/no-mutable-exports": 0,
// Allow no named as default / member
// "import/no-named-as-default": 0,
// "import/no-named-as-default-member": 0
}
}

8
.gitignore vendored
View file

@ -1,4 +1,4 @@
node_modules/
experiments/
.npmignore
package-lock.json
node_modules
package-lock.json
experiments

View file

@ -1,5 +0,0 @@
node_modules/
demo/
experiments/
package-lock.json
.gitignore

22
LICENSE
View file

@ -1,22 +0,0 @@
The MIT License (MIT)
Copyright (c) 2015 thednp
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View file

@ -1,62 +0,0 @@
# KUTE.js
A modern JavaScript animation engine built on ES6+ standards with strong TypeScript definitions and most essential features for the web with easy to use methods to set up high performance, cross-browser animations. The focus is code quality, flexibility, performance and size.
[![NPM Version](https://img.shields.io/npm/v/kute.js.svg?style=flat-square)](https://www.npmjs.com/package/kute.js)
[![NPM Downloads](https://img.shields.io/npm/dm/kute.js.svg?style=flat-square)](http://npm-stat.com/charts.html?package=kute.js)
[![jsDeliver](https://data.jsdelivr.com/v1/package/npm/kute.js/badge)](https://www.jsdelivr.com/package/npm/kute.js)
[![CDNJS](https://img.shields.io/cdnjs/v/kute.js.svg?style=flat-square)](https://cdnjs.com/libraries/kute.js)
KUTE.js packs a series of components for presentation attributes, SVG transform, draw SVG strokes and path morphing, text string write up or number countdowns, plus additional CSS properties like colors, border-radius or typographic properties.
For components documentation, examples and other cool tips, check the [demo](http://thednp.github.io/kute.js/).
# Components
KUTE.js includes 18 components, but not all of them are bundled with the default distribution package:
* [backgroundPosition](http://thednp.github.io/kute.js/backgroundPosition.html) - enables the animation for the `backgroundPosition` CSS property
* [borderRadius](http://thednp.github.io/kute.js/borderRadius.html) - enables the animation for all the **borderRadius** properties
* [boxModel](http://thednp.github.io/kute.js/boxModel.html) - enables the animation for the **boxModel** properties like `top` , `left`, `width`, etc
* [clipProperty](http://thednp.github.io/kute.js/clipProperty.html) - enables the animation for the `clip` property
* [colorProperties](http://thednp.github.io/kute.js/colorProperties.html) - enables the animation for the **color** properties like `color`, `backgroundColor`
* [filterEffects](http://thednp.github.io/kute.js/filterEffects.html) - enables the animation for the `filter` property
* [htmlAttributes](http://thednp.github.io/kute.js/htmlAttributes.html) - enables the animation for any numeric as well as some color based **HTML Attributes**
* [opacityProperty](http://thednp.github.io/kute.js/opacityProperty.html) - enables the animation for the `opacity` property
* [scrollProperty](http://thednp.github.io/kute.js/scrollProperty.html) - enables the animation for the window/Element `scrollTop` Object property
* [shadowProperties](http://thednp.github.io/kute.js/shadowProperties.html) - enables the animation for the **shadowProperties** properties: `textShadow` &amp; `boxShadow`
* [svgCubicMorph](http://thednp.github.io/kute.js/svgCubicMorph.html) - enables the animation for the `d` Presentation Attribute of the `<path>` SVGElement targets; this implements some [Raphael.js](https://dmitrybaranovskiy.github.io/raphael/) functionality
* [svgMorph](http://thednp.github.io/kute.js/svgMorph.html) - enables the animation for the `d` Presentation Attribute of the `<path>` SVGElement targets; this component implements some [D3.js](https://github.com/d3/d3) and [flubber](https://github.com/veltman/flubber) functionality
* [svgDraw](http://thednp.github.io/kute.js/svgDraw.html) - enables the animation for the `strokeDasharray` and `strokeDashoffset` CSS properties specific to `<path>` SVGElement
* [svgTransform](http://thednp.github.io/kute.js/svgTransform.html) - enables the animation for the `transform` presentation attribute
* [textProperties](http://thednp.github.io/kute.js/textProperties.html) - enables the animation for numeric `HTMLTextElement` related CSS properties like `fontSize` or `letterSpacing`
* [textWrite](http://thednp.github.io/kute.js/textWrite.html) - enables the animation for the content of various strings
* [transformFunctions](http://thednp.github.io/kute.js/transformFunctions.html) - enables the animation for the `transform` CSS3 property, the default component bundled with the official build
* transformLegacy - enables the animation for the `transform` CSS3 property on legacy browsers IE9+, not included with the official build, but can be used in your custom builds
* [transformMatrix](http://thednp.github.io/kute.js/transformMatrix.html) - enables the animation for the `transform` CSS3 property; this component implements `DOMMatrix()` API and is super light
All above mentioned components have a BASE version which doesn't include value processing, and their purpose is to provide a way to ship your super light version of your application.
# Wiki
For a complete developer guide, usage and stuff like npm, visit [the wiki](https://github.com/thednp/kute.js/wiki).
# Browser Support
KUTE.js is redeveloped for maximum performance on modern browsers. Some legacy browsers might some help, so give them a small polyfill set with most essential features required by KUTE.js to work, powered by [minifill](https://github.com/thednp/minifill), try it. For broader projects you might want to consider <a href="https://cdn.polyfill.io/v2/docs/">polyfills</a>.
# Special Thanks
* [Mike Bostock](https://bost.ocks.org/mike/) for his awesome [D3.js](https://github.com/d3/d3), one of the sources for our reworked [SVGMorph](http://thednp.github.io/kute.js/svgMorph.html) component.
* [Noah Veltman](https://github.com/veltman) for his awesome [flubber](https://github.com/veltman/flubber), another one of the sources for the SVGMorph component.
* [Andrew Willems](https://stackoverflow.com/users/5218951/andrew-willems) for his awesome [Stackoverflow answer](https://stackoverflow.com/questions/35560989/javascript-how-to-determine-a-svg-path-draw-direction) resulting in the creation of our [SVGCubicMorph](http://thednp.github.io/kute.js/svgCubicMorph.html) component.
* [Dmitry Baranovskiy](https://dmitry.baranovskiy.com/) for his awesome [Raphael.js](https://dmitrybaranovskiy.github.io/raphael/), another source for our SVGCubicMorph component.
* [@dalisoft](https://github.com/dalisoft) contributed a great deal to the performance and functionality of previous versions of KUTE.js.
# Contributions
* [Contributors &amp; Collaborators](https://github.com/thednp/kute.js/graphs/contributors)
# License
[MIT License](https://github.com/thednp/kute.js/blob/master/LICENSE)

View file

Before

Width:  |  Height:  |  Size: 4.2 KiB

After

Width:  |  Height:  |  Size: 4.2 KiB

View file

Before

Width:  |  Height:  |  Size: 131 KiB

After

Width:  |  Height:  |  Size: 131 KiB

View file

Before

Width:  |  Height:  |  Size: 5.1 KiB

After

Width:  |  Height:  |  Size: 5.1 KiB

View file

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

View file

Before

Width:  |  Height:  |  Size: 31 KiB

After

Width:  |  Height:  |  Size: 31 KiB

View file

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

View file

Before

Width:  |  Height:  |  Size: 997 B

After

Width:  |  Height:  |  Size: 997 B

View file

Before

Width:  |  Height:  |  Size: 3 KiB

After

Width:  |  Height:  |  Size: 3 KiB

File diff suppressed because one or more lines are too long

4621
dist/kute.esm.js vendored

File diff suppressed because it is too large Load diff

File diff suppressed because one or more lines are too long

4675
dist/kute.js vendored

File diff suppressed because it is too large Load diff

140
dist/polyfill.js vendored
View file

@ -1,140 +0,0 @@
/*!
* KUTE.js Polyfill v2.1.1-alpha1 (http://thednp.github.io/kute.js)
* Copyright 2015-2021 © thednp
* Licensed under MIT (https://github.com/thednp/bootstrap.native/blob/master/LICENSE)
*/
"use strict";
if (!Array.from) {
Array.from = (function () {
var toStr = Object.prototype.toString;
var isCallable = function (fn) {
return typeof fn === 'function' || toStr.call(fn) === '[object Function]';
};
var toInteger = function (value) {
var number = Number(value);
if (isNaN(number)) { return 0; }
if (number === 0 || !isFinite(number)) { return number; }
return (number > 0 ? 1 : -1) * Math.floor(Math.abs(number));
};
var maxSafeInteger = Math.pow(2, 53) - 1;
var toLength = function (value) {
var len = toInteger(value);
return Math.min(Math.max(len, 0), maxSafeInteger);
};
return function from(arrayLike/*, mapFn, thisArg */) {
var C = this, items = Object(arrayLike);
if (arrayLike == null) {
throw new TypeError('Array.from requires an array-like object - not null or undefined');
}
var mapFn = arguments.length > 1 ? arguments[1] : void undefined, T;
if (typeof mapFn !== 'undefined') {
if (!isCallable(mapFn)) {
throw new TypeError('Array.from: when provided, the second argument must be a function');
}
if (arguments.length > 2) {
T = arguments[2];
}
}
var len = toLength(items.length);
var A = isCallable(C) ? Object(new C(len)) : new Array(len);
var k = 0;
var kValue;
while (k < len) {
kValue = items[k];
if (mapFn) {
A[k] = typeof T === 'undefined' ? mapFn(kValue, k) : mapFn.call(T, kValue, k);
} else {
A[k] = kValue;
}
k += 1;
}
A.length = len;
return A;
}
}());
}
// https://github.com/jonathantneal/array-flat-polyfill/blob/master/src/polyfill-flat.js
if (!Array.prototype.flat) {
Object.defineProperty(Array.prototype, 'flat', {
configurable: true,
value: function flat () {
var depth = isNaN(arguments[0]) ? 1 : Number(arguments[0]);
return depth ? Array.prototype.reduce.call(this, function (acc, cur) {
if (Array.isArray(cur)) {
acc.push.apply(acc, flat.call(cur, depth - 1));
} else {
acc.push(cur);
}
return acc;
}, []) : Array.prototype.slice.call(this);
},
writable: true
});
}
if (!Array.prototype.includes) {
Array.prototype.includes = function(searchElement /*, fromIndex*/ ) {
var O = Object(this);
var len = parseInt(O.length) || 0;
if (len === 0) {
return false;
}
var n = parseInt(arguments[1]) || 0;
var k;
if (n >= 0) {
k = n;
} else {
k = len + n;
if (k < 0) {k = 0;}
}
var currentElement;
while (k < len) {
currentElement = O[k];
if (searchElement === currentElement ||
(searchElement !== searchElement && currentElement !== currentElement)) {
return true;
}
k++;
}
return false;
};
}
if (!String.prototype.includes) {
String.prototype.includes = function(search, start) {
if (search instanceof RegExp) {
throw TypeError('first argument must not be a RegExp');
}
if (start === undefined) { start = 0; }
return this.indexOf(search, start) !== -1;
};
}
if (!Number.isFinite) {
Number.isFinite = function(value) {
return typeof value === 'number'
&& isFinite(value);
};
}
if (!Number.isInteger) {
Number.isInteger = function(value) {
return typeof value === 'number'
&& isFinite(value)
&& Math.floor(value) === value;
};
}
if (!Number.isNaN) {
Number.isNaN = function(value) {
return typeof value === 'number'
&& value !== value;
};
}

View file

@ -1,3 +0,0 @@
// KUTE.js Polyfill v2.1.1-alpha1 | 2021 © thednp | MIT-License
"use strict";
var r,t,e,n;Array.from||(Array.from=(r=Object.prototype.toString,t=function(t){return"function"==typeof t||"[object Function]"===r.call(t)},e=Math.pow(2,53)-1,n=function(r){var t=function(r){var t=Number(r);return isNaN(t)?0:0!==t&&isFinite(t)?(t>0?1:-1)*Math.floor(Math.abs(t)):t}(r);return Math.min(Math.max(t,0),e)},function(r){var e=this,i=Object(r);if(null==r)throw new TypeError("Array.from requires an array-like object - not null or undefined");var o,a=arguments.length>1?arguments[1]:void 0;if(void 0!==a){if(!t(a))throw new TypeError("Array.from: when provided, the second argument must be a function");arguments.length>2&&(o=arguments[2])}for(var u,f=n(i.length),p=t(e)?Object(new e(f)):new Array(f),c=0;c<f;)u=i[c],p[c]=a?void 0===o?a(u,c):a.call(o,u,c):u,c+=1;return p.length=f,p})),Array.prototype.flat||Object.defineProperty(Array.prototype,"flat",{configurable:!0,value:function r(){var t=isNaN(arguments[0])?1:Number(arguments[0]);return t?Array.prototype.reduce.call(this,(function(e,n){return Array.isArray(n)?e.push.apply(e,r.call(n,t-1)):e.push(n),e}),[]):Array.prototype.slice.call(this)},writable:!0}),Array.prototype.includes||(Array.prototype.includes=function(r){var t=Object(this),e=parseInt(t.length)||0;if(0===e)return!1;var n,i,o=parseInt(arguments[1])||0;for(o>=0?n=o:(n=e+o)<0&&(n=0);n<e;){if(r===(i=t[n])||r!=r&&i!=i)return!0;n++}return!1}),String.prototype.includes||(String.prototype.includes=function(r,t){if(r instanceof RegExp)throw TypeError("first argument must not be a RegExp");return void 0===t&&(t=0),-1!==this.indexOf(r,t)}),Number.isFinite||(Number.isFinite=function(r){return"number"==typeof r&&isFinite(r)}),Number.isInteger||(Number.isInteger=function(r){return"number"==typeof r&&isFinite(r)&&Math.floor(r)===r}),Number.isNaN||(Number.isNaN=function(r){return"number"==typeof r&&r!=r});

View file

@ -1,80 +0,0 @@
{
"name": "kute.js",
"version": "2.2.3",
"description": "JavaScript animation engine",
"main": "dist/kute.min.js",
"module": "dist/kute.esm.js",
"jsnext": "src/index.js",
"types": "types/index.d.ts",
"files": [
"dist",
"types",
"src"
],
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"help": "rollup --help",
"build1": "npm-run-all --parallel copy-build build-*",
"build": "npm run lint:js && npm-run-all --parallel copy-build build-*",
"custom": "rollup -c --environment",
"fix:js": "eslint src/ --config .eslintrc --fix",
"lint:js": "eslint src/ --config .eslintrc",
"copy-build": "rollup --environment OUTPUTFILE:demo/src/kute.min.js,DIST:standard,MIN:true,FORMAT:umd -c",
"build-standard": "rollup --environment DIST:standard,MIN:false,FORMAT:umd -c",
"build:ts": "tsc -d",
"build-standard-min": "rollup --environment DIST:standard,MIN:true,FORMAT:umd -c",
"build-standard-esm": "rollup --environment DIST:standard,MIN:false,FORMAT:esm -c",
"build-standard-esm-min": "rollup --environment DIST:standard,MIN:true,FORMAT:esm -c",
"build-base": "rollup --environment OUTPUTFILE:demo/src/kute-base.js,DIST:base,MIN:false,FORMAT:umd -c",
"build-base-min": "rollup --environment OUTPUTFILE:demo/src/kute-base.min.js,DIST:base,MIN:true,FORMAT:umd -c",
"build-extra": "rollup --environment OUTPUTFILE:demo/src/kute-extra.js,DIST:extra,MIN:false,FORMAT:umd -c",
"build-extra-min": "rollup --environment OUTPUTFILE:demo/src/kute-extra.min.js,DIST:extra,MIN:true,FORMAT:umd -c",
"polyfill": "npm-run-all --parallel polyfill-unminified polyfill-minified copy-polyfill copy-polyfill-legacy",
"copy-polyfill-legacy": "rollup --environment INPUTFILE:src/util/polyfill-legacy.js,OUTPUTFILE:demo/src/polyfill-legacy.min.js,MIN:true -c rollup.polyfill.js",
"copy-polyfill": "rollup --environment OUTPUTFILE:demo/src/polyfill.min.js,MIN:true -c rollup.polyfill.js",
"polyfill-unminified": "rollup --environment MIN:false -c rollup.polyfill.js",
"polyfill-minified": "rollup --environment MIN:true -c rollup.polyfill.js"
},
"repository": {
"type": "git",
"url": "git+https://github.com/thednp/kute.js.git"
},
"keywords": [
"kute.js",
"svg morph",
"svg transform",
"css3 transform",
"matrix transform",
"tweening engine",
"animation engine",
"javascript animation engine",
"javascript animation",
"animation",
"native javascript"
],
"author": "thednp",
"license": "MIT",
"bugs": {
"url": "https://github.com/thednp/kute.js/issues"
},
"homepage": "http://thednp.github.io/kute.js",
"dependencies": {
"cubic-bezier-easing": "^1.0.18",
"minifill": "^0.0.16",
"shorter-js": "^0.2.6",
"svg-path-commander": "0.1.23"
},
"devDependencies": {
"@rollup/plugin-buble": "^0.21.3",
"@rollup/plugin-json": "^4.1.0",
"@rollup/plugin-node-resolve": "^9.0.0",
"eslint": "^7.22.0",
"eslint-config-airbnb-base": "^14.2.1",
"eslint-plugin-import": "^2.22.1",
"eslint-plugin-vue": "^7.7.0",
"npm-run-all": "^4.1.5",
"rollup": "^2.38.4",
"rollup-plugin-terser": "^7.0.2",
"typescript": "^4.5.2"
}
}

View file

@ -1,58 +0,0 @@
'use strict'
import buble from '@rollup/plugin-buble'
import {terser} from 'rollup-plugin-terser'
import node from '@rollup/plugin-node-resolve'
import json from '@rollup/plugin-json'
import * as pkg from "./package.json"
let INPUTFILE = process.env.INPUTFILE
let OUTPUTFILE = process.env.OUTPUTFILE
const DIST = process.env.DIST // base|standard|extra
const NAME = DIST.charAt(0).toUpperCase() + DIST.slice(1); // Base|Standard|Extra
const MIN = process.env.MIN === 'true' // true/false|unset
const FORMAT = process.env.FORMAT // umd|iife|esm
const year = (new Date).getFullYear()
const banner =
`/*!
* KUTE.js ${NAME} v${pkg.version} (${pkg.homepage})
* Copyright 2015-${year} © ${pkg.author}
* Licensed under MIT (https://github.com/thednp/kute.js/blob/master/LICENSE)
*/`
const miniBanner = `// KUTE.js ${NAME} v${pkg.version} | ${pkg.author} © ${year} | ${pkg.license}-License`
INPUTFILE = INPUTFILE ? INPUTFILE : (DIST === 'standard' ? 'src/index.js' : 'src/index-'+DIST+'.js')
OUTPUTFILE = OUTPUTFILE ? OUTPUTFILE : ('dist/kute'+(DIST!=='standard'?'-'+DIST:'')+(FORMAT==='esm'?'.esm':'')+(MIN?'.min':'')+'.js')
const OUTPUT = {
file: OUTPUTFILE,
format: FORMAT, // or iife
}
const PLUGINS = [
node({mainFields: ['jsnext','module'], dedupe: ['svg-path-commander']}) ,
json(),
]
if (FORMAT!=='esm'){
PLUGINS.push(buble({objectAssign: 'Object.assign'}));
}
if (MIN){
PLUGINS.push(terser({output: {preamble: miniBanner}}));
} else {
OUTPUT.banner = banner;
}
if (FORMAT!=='esm') {
OUTPUT.name = 'KUTE';
}
export default [
{
input: INPUTFILE,
output: OUTPUT,
plugins: PLUGINS
}
]

View file

@ -1,54 +0,0 @@
'use strict'
import buble from '@rollup/plugin-buble'
import node from '@rollup/plugin-node-resolve'
import json from '@rollup/plugin-json'
import {terser} from 'rollup-plugin-terser'
import * as pkg from "./package.json";
// set headers
const year = (new Date).getFullYear()
const banner = `/*!
* KUTE.js Polyfill v${pkg.version} (${pkg.homepage})
* Copyright 2015-${year} © ${pkg.author}
* Licensed under MIT (https://github.com/thednp/bootstrap.native/blob/master/LICENSE)
*/
"use strict";`
const miniBanner = `// KUTE.js Polyfill v${pkg.version} | ${year} © ${pkg.author} | ${pkg.license}-License
"use strict";`
// set config
const MIN = process.env.MIN === 'true' // true/false|unset
const FORMAT = 'esm' // umd|iife|esm|cjs
const INPUTFILE = process.env.INPUTFILE ? process.env.INPUTFILE : 'src/util/polyfill.js'
const OUTPUTFILE = process.env.OUTPUTFILE ? process.env.OUTPUTFILE : 'dist/polyfill'+(MIN?'.min':'')+'.js'
const OUTPUT = {
file: OUTPUTFILE,
format: FORMAT, // or iife
}
const PLUGINS = [
node(),
json(),
buble(),
]
if (MIN){
PLUGINS.push(terser({output: {preamble: miniBanner}}));
} else {
OUTPUT.banner = banner;
}
// if (FORMAT!=='esm') {
// OUTPUT.name = 'BSN';
// }
export default [
{
input: INPUTFILE,
output: OUTPUT,
plugins: PLUGINS
}
]

View file

@ -1,137 +0,0 @@
import supportedProperties from '../objects/supportedProperties';
import defaultValues from '../objects/defaultValues';
import defaultOptions from '../objects/defaultOptions';
import prepareProperty from '../objects/prepareProperty';
import prepareStart from '../objects/prepareStart';
import onStart from '../objects/onStart';
import onComplete from '../objects/onComplete';
import crossCheck from '../objects/crossCheck';
import linkProperty from '../objects/linkProperty';
import Util from '../objects/util';
import Interpolate from '../objects/interpolate';
/**
* Animation Class
*
* Registers components by populating KUTE.js objects and makes sure
* no duplicate component / property is allowed.
*/
export default class Animation {
/**
* @constructor
* @param {KUTE.fullComponent} Component
*/
constructor(Component) {
try {
if (Component.component in supportedProperties) {
throw Error(`KUTE - ${Component.component} already registered`);
} else if (Component.property in defaultValues) {
throw Error(`KUTE - ${Component.property} already registered`);
}
} catch (e) {
throw Error(e);
}
const propertyInfo = this;
const ComponentName = Component.component;
// const Objects = { defaultValues, defaultOptions, Interpolate, linkProperty, Util }
const Functions = {
prepareProperty, prepareStart, onStart, onComplete, crossCheck,
};
const Category = Component.category;
const Property = Component.property;
const Length = (Component.properties && Component.properties.length)
|| (Component.subProperties && Component.subProperties.length);
// single property
// {property,defaultvalue,defaultOptions,Interpolate,functions}
// category colors, boxModel, borderRadius
// {category,properties,defaultvalues,defaultOptions,Interpolate,functions}
// property with multiple sub properties. Eg transform, filter
// {property,subProperties,defaultvalues,defaultOptions,Interpolate,functions}
// property with multiple sub properties. Eg htmlAttributes
// {category,subProperties,defaultvalues,defaultOptions,Interpolate,functions}
// set supported category/property
supportedProperties[ComponentName] = Component.properties
|| Component.subProperties || Component.property;
// set defaultValues
if ('defaultValue' in Component) { // value 0 will invalidate
defaultValues[Property] = Component.defaultValue;
// minimal info
propertyInfo.supports = `${Property} property`;
} else if (Component.defaultValues) {
Object.keys(Component.defaultValues).forEach((dv) => {
defaultValues[dv] = Component.defaultValues[dv];
});
// minimal info
propertyInfo.supports = `${Length || Property} ${Property || Category} properties`;
}
// set additional options
if (Component.defaultOptions) {
// Object.keys(Component.defaultOptions).forEach((op) => {
// defaultOptions[op] = Component.defaultOptions[op];
// });
Object.assign(defaultOptions, Component.defaultOptions);
}
// set functions
if (Component.functions) {
Object.keys(Functions).forEach((fn) => {
if (fn in Component.functions) {
if (typeof (Component.functions[fn]) === 'function') {
// if (!Functions[fn][ Category||Property ]) {
// Functions[fn][ Category||Property ] = Component.functions[fn];
// }
if (!Functions[fn][ComponentName]) Functions[fn][ComponentName] = {};
if (!Functions[fn][ComponentName][Category || Property]) {
Functions[fn][ComponentName][Category || Property] = Component.functions[fn];
}
} else {
Object.keys(Component.functions[fn]).forEach((ofn) => {
// !Functions[fn][ofn] && (Functions[fn][ofn] = Component.functions[fn][ofn])
if (!Functions[fn][ComponentName]) Functions[fn][ComponentName] = {};
if (!Functions[fn][ComponentName][ofn]) {
Functions[fn][ComponentName][ofn] = Component.functions[fn][ofn];
}
});
}
}
});
}
// set component interpolation functions
if (Component.Interpolate) {
Object.keys(Component.Interpolate).forEach((fni) => {
const compIntObj = Component.Interpolate[fni];
if (typeof (compIntObj) === 'function' && !Interpolate[fni]) {
Interpolate[fni] = compIntObj;
} else {
Object.keys(compIntObj).forEach((sfn) => {
if (typeof (compIntObj[sfn]) === 'function' && !Interpolate[fni]) {
Interpolate[fni] = compIntObj[sfn];
}
});
}
});
linkProperty[ComponentName] = Component.Interpolate;
}
// set component util
if (Component.Util) {
Object.keys(Component.Util).forEach((fnu) => {
if (!Util[fnu]) Util[fnu] = Component.Util[fnu];
});
}
return propertyInfo;
}
}

View file

@ -1,97 +0,0 @@
import supportedProperties from '../objects/supportedProperties';
import defaultOptions from '../objects/defaultOptions';
import onStart from '../objects/onStart';
import onComplete from '../objects/onComplete';
import linkProperty from '../objects/linkProperty';
import Util from '../objects/util';
import Interpolate from '../objects/interpolate';
/**
* Animation Base Class
*
* Registers components by populating KUTE.js objects and makes sure
* no duplicate component / property is allowed.
*
* This class only registers the minimal amount of component information
* required to enable components animation, which means value processing
* as well as `to()` and `allTo()` methods are not supported.
*/
export default class AnimationBase {
/**
* @class
* @param {KUTE.baseComponent} Component
*/
constructor(Component) {
const ComponentName = Component.component;
// const Objects = { defaultValues, defaultOptions, Interpolate, linkProperty }
const Functions = { onStart, onComplete };
const Category = Component.category;
const Property = Component.property;
// ESLint
this._ = 0;
// set supported category/property
supportedProperties[ComponentName] = Component.properties
|| Component.subProperties || Component.property;
// set additional options
if (Component.defaultOptions) {
// Object.keys(Component.defaultOptions).forEach((op) => {
// defaultOptions[op] = Component.defaultOptions[op];
// });
Object.assign(defaultOptions, Component.defaultOptions);
}
// set functions
if (Component.functions) {
Object.keys(Functions).forEach((fn) => {
if (fn in Component.functions) {
if (typeof (Component.functions[fn]) === 'function') {
// if (!Functions[fn][ Category||Property ]) {
// Functions[fn][ Category||Property ] = Component.functions[fn];
// }
if (!Functions[fn][ComponentName]) Functions[fn][ComponentName] = {};
if (!Functions[fn][ComponentName][Category || Property]) {
Functions[fn][ComponentName][Category || Property] = Component.functions[fn];
}
} else {
Object.keys(Component.functions[fn]).forEach((ofn) => {
// if (!Functions[fn][ofn]) Functions[fn][ofn] = Component.functions[fn][ofn];
if (!Functions[fn][ComponentName]) Functions[fn][ComponentName] = {};
if (!Functions[fn][ComponentName][ofn]) {
Functions[fn][ComponentName][ofn] = Component.functions[fn][ofn];
}
});
}
}
});
}
// set interpolate
if (Component.Interpolate) {
Object.keys(Component.Interpolate).forEach((fni) => {
const compIntObj = Component.Interpolate[fni];
if (typeof (compIntObj) === 'function' && !Interpolate[fni]) {
Interpolate[fni] = compIntObj;
} else {
Object.keys(compIntObj).forEach((sfn) => {
if (typeof (compIntObj[sfn]) === 'function' && !Interpolate[fni]) {
Interpolate[fni] = compIntObj[sfn];
}
});
}
});
linkProperty[ComponentName] = Component.Interpolate;
}
// set component util
if (Component.Util) {
Object.keys(Component.Util).forEach((fnu) => {
if (!Util[fnu]) Util[fnu] = Component.Util[fnu];
});
}
return { name: ComponentName };
}
}

View file

@ -1,137 +0,0 @@
import prepareProperty from '../objects/prepareProperty';
import prepareStart from '../objects/prepareStart';
import onStart from '../objects/onStart';
import onComplete from '../objects/onComplete';
import crossCheck from '../objects/crossCheck';
import Interpolate from '../objects/interpolate';
import Animation from './animation';
/**
* Animation Development Class
*
* Registers components by populating KUTE.js objects and makes sure
* no duplicate component / property is allowed.
*
* In addition to the default class, this one provides more component
* information to help you with custom component development.
*/
export default class AnimationDevelopment extends Animation {
/**
*
* @param {KUTE.fullComponent} args
*/
constructor(Component) {
super(Component);
const propertyInfo = this;
// const Objects = { defaultValues, defaultOptions, Interpolate, linkProperty, Util }
const Functions = {
prepareProperty, prepareStart, onStart, onComplete, crossCheck,
};
const Category = Component.category;
const Property = Component.property;
const Length = (Component.properties && Component.properties.length)
|| (Component.subProperties && Component.subProperties.length);
// set defaultValues
if ('defaultValue' in Component) { // value 0 will invalidate
propertyInfo.supports = `${Property} property`;
propertyInfo.defaultValue = `${(`${Component.defaultValue}`).length ? 'YES' : 'not set or incorrect'}`;
} else if (Component.defaultValues) {
propertyInfo.supports = `${Length || Property} ${Property || Category} properties`;
propertyInfo.defaultValues = Object.keys(Component.defaultValues).length === Length ? 'YES' : 'Not set or incomplete';
}
// set additional options
if (Component.defaultOptions) {
propertyInfo.extends = [];
Object.keys(Component.defaultOptions).forEach((op) => {
propertyInfo.extends.push(op);
});
if (propertyInfo.extends.length) {
propertyInfo.extends = `with <${propertyInfo.extends.join(', ')}> new option(s)`;
} else {
delete propertyInfo.extends;
}
}
// set functions
if (Component.functions) {
propertyInfo.interface = [];
propertyInfo.render = [];
propertyInfo.warning = [];
Object.keys(Functions).forEach((fnf) => {
if (fnf in Component.functions) {
if (fnf === 'prepareProperty') propertyInfo.interface.push('fromTo()');
if (fnf === 'prepareStart') propertyInfo.interface.push('to()');
if (fnf === 'onStart') propertyInfo.render = 'can render update';
} else {
if (fnf === 'prepareProperty') propertyInfo.warning.push('fromTo()');
if (fnf === 'prepareStart') propertyInfo.warning.push('to()');
if (fnf === 'onStart') propertyInfo.render = 'no function to render update';
}
});
if (propertyInfo.interface.length) {
propertyInfo.interface = `${Category || Property} can use [${propertyInfo.interface.join(', ')}] method(s)`;
} else {
delete propertyInfo.uses;
}
if (propertyInfo.warning.length) {
propertyInfo.warning = `${Category || Property} can't use [${propertyInfo.warning.join(', ')}] method(s) because values aren't processed`;
} else {
delete propertyInfo.warning;
}
}
// register Interpolation functions
if (Component.Interpolate) {
propertyInfo.uses = [];
propertyInfo.adds = [];
Object.keys(Component.Interpolate).forEach((fni) => {
const compIntObj = Component.Interpolate[fni];
// register new Interpolation functions
if (typeof (compIntObj) === 'function') {
if (!Interpolate[fni]) {
propertyInfo.adds.push(`${fni}`);
}
propertyInfo.uses.push(`${fni}`);
} else {
Object.keys(compIntObj).forEach((sfn) => {
if (typeof (compIntObj[sfn]) === 'function' && !Interpolate[fni]) {
propertyInfo.adds.push(`${sfn}`);
}
propertyInfo.uses.push(`${sfn}`);
});
}
});
if (propertyInfo.uses.length) {
propertyInfo.uses = `[${propertyInfo.uses.join(', ')}] interpolation function(s)`;
} else {
delete propertyInfo.uses;
}
if (propertyInfo.adds.length) {
propertyInfo.adds = `new [${propertyInfo.adds.join(', ')}] interpolation function(s)`;
} else {
delete propertyInfo.adds;
}
} else {
propertyInfo.critical = `For ${Property || Category} no interpolation function[s] is set`;
}
// set component util
if (Component.Util) {
propertyInfo.hasUtil = Object.keys(Component.Util).join(',');
}
return propertyInfo;
}
}

View file

@ -1,57 +0,0 @@
import defaultValues from '../objects/defaultValues';
import getStyleForProperty from '../process/getStyleForProperty';
import numbers from '../interpolation/numbers';
import trueDimension from '../util/trueDimension';
import { onStartBgPos } from './backgroundPositionBase';
// Component Functions
/**
* Returns the property computed style.
* @param {string} prop the property
* @returns {string} the property computed style
*/
function getBgPos(prop/* , value */) {
return getStyleForProperty(this.element, prop) || defaultValues[prop];
}
/**
* Returns the property tween object.
* @param {string} _ the property name
* @param {string} value the property value
* @returns {number[]} the property tween object
*/
function prepareBgPos(/* prop, */_, value) {
if (value instanceof Array) {
const x = trueDimension(value[0]).v;
const y = trueDimension(value[1]).v;
return [!Number.isNaN(x * 1) ? x : 50, !Number.isNaN(y * 1) ? y : 50];
}
let posxy = value.replace(/top|left/g, 0)
.replace(/right|bottom/g, 100)
.replace(/center|middle/g, 50);
posxy = posxy.split(/(,|\s)/g);
posxy = posxy.length === 2 ? posxy : [posxy[0], 50];
return [trueDimension(posxy[0]).v, trueDimension(posxy[1]).v];
}
// All Component Functions
const bgPositionFunctions = {
prepareStart: getBgPos,
prepareProperty: prepareBgPos,
onStart: onStartBgPos,
};
// Component Full Object
const BackgroundPosition = {
component: 'backgroundPositionProp',
property: 'backgroundPosition',
defaultValue: [50, 50],
Interpolate: { numbers },
functions: bgPositionFunctions,
Util: { trueDimension },
};
export default BackgroundPosition;

View file

@ -1,26 +0,0 @@
import KEC from '../objects/kute';
import numbers from '../interpolation/numbers';
// Component Functions
/**
* Sets the property update function.
* @param {string} prop the property name
*/
export function onStartBgPos(prop) {
if (this.valuesEnd[prop] && !KEC[prop]) {
KEC[prop] = (elem, a, b, v) => {
/* eslint-disable -- no-bitwise & no-param-reassign impossible to satisfy */
elem.style[prop] = `${(numbers(a[0], b[0], v) * 100 >> 0) / 100}% ${((numbers(a[1], b[1], v) * 100 >> 0) / 100)}%`;
/* eslint-enable -- no-bitwise & no-param-reassign impossible to satisfy */
};
}
}
// Component Base Object
const BackgroundPositionBase = {
component: 'baseBackgroundPosition',
property: 'backgroundPosition',
Interpolate: { numbers },
functions: { onStart: onStartBgPos },
};
export default BackgroundPositionBase;

View file

@ -1,58 +0,0 @@
import defaultValues from '../objects/defaultValues';
import getStyleForProperty from '../process/getStyleForProperty';
import trueDimension from '../util/trueDimension';
import units from '../interpolation/units';
import { radiusOnStartFn } from './borderRadiusBase';
// Component Properties
const radiusProps = [
'borderRadius',
'borderTopLeftRadius', 'borderTopRightRadius',
'borderBottomLeftRadius', 'borderBottomRightRadius'];
const radiusValues = {};
radiusProps.forEach((x) => { radiusValues[x] = 0; });
// Component Functions
const radiusOnStart = {};
radiusProps.forEach((tweenProp) => {
radiusOnStart[tweenProp] = radiusOnStartFn;
});
/**
* Returns the current property computed style.
* @param {string} tweenProp the property name
* @returns {string} the property computed style
*/
export function getRadius(tweenProp) {
return getStyleForProperty(this.element, tweenProp) || defaultValues[tweenProp];
}
/**
* Returns the property tween object.
* @param {string} value the property value
* @returns {{v: number, u: string}} the property tween object
*/
export function prepareRadius(/* tweenProp, */_, value) {
return trueDimension(value);
}
// All Component Functions
export const radiusFunctions = {
prepareStart: getRadius,
prepareProperty: prepareRadius,
onStart: radiusOnStart,
};
// Full Component
const BorderRadius = {
component: 'borderRadiusProperties',
category: 'borderRadius',
properties: radiusProps,
defaultValues: radiusValues,
Interpolate: { units },
functions: radiusFunctions,
Util: { trueDimension },
};
export default BorderRadius;

View file

@ -1,43 +0,0 @@
import KEC from '../objects/kute';
import units from '../interpolation/units';
/* borderRadius = {
category: 'borderRadius',
properties : [..],
defaultValues: {..},
interpolation: {units}
} */
// Component Properties
const radiusProps = [
'borderRadius',
'borderTopLeftRadius', 'borderTopRightRadius',
'borderBottomLeftRadius', 'borderBottomRightRadius',
];
// Component Functions
/**
* Sets the property update function.
* @param {string} tweenProp the property name
*/
export function radiusOnStartFn(tweenProp) {
if (tweenProp in this.valuesEnd && !KEC[tweenProp]) {
KEC[tweenProp] = (elem, a, b, v) => {
// eslint-disable-next-line no-param-reassign -- impossible to satisfy
elem.style[tweenProp] = units(a.v, b.v, b.u, v);
};
}
}
const radiusOnStart = {};
radiusProps.forEach((tweenProp) => {
radiusOnStart[tweenProp] = radiusOnStartFn;
});
// Base Component
const BorderRadiusBase = {
component: 'baseBorderRadius',
category: 'borderRadius',
Interpolate: { units },
functions: { onStart: radiusOnStart },
};
export default BorderRadiusBase;

View file

@ -1,57 +0,0 @@
import defaultValues from '../objects/defaultValues';
import getStyleForProperty from '../process/getStyleForProperty';
import trueDimension from '../util/trueDimension';
import numbers from '../interpolation/numbers';
import { boxModelOnStart } from './boxModelBase';
// Component Properties
const boxModelProperties = ['top', 'left', 'width', 'height', 'right', 'bottom', 'minWidth', 'minHeight', 'maxWidth', 'maxHeight',
'padding', 'paddingTop', 'paddingBottom', 'paddingLeft', 'paddingRight',
'margin', 'marginTop', 'marginBottom', 'marginLeft', 'marginRight',
'borderWidth', 'borderTopWidth', 'borderRightWidth', 'borderBottomWidth', 'borderLeftWidth', 'outlineWidth'];
const boxModelValues = {};
boxModelProperties.forEach((x) => { boxModelValues[x] = 0; });
// Component Functions
/**
* Returns the current property computed style.
* @param {string} tweenProp the property name
* @returns {string} computed style for property
*/
function getBoxModel(tweenProp) {
return getStyleForProperty(this.element, tweenProp) || defaultValues[tweenProp];
}
/**
* Returns the property tween object.
* @param {string} tweenProp the property name
* @param {string} value the property value
* @returns {number} the property tween object
*/
function prepareBoxModel(tweenProp, value) {
const boxValue = trueDimension(value); const
offsetProp = tweenProp === 'height' ? 'offsetHeight' : 'offsetWidth';
return boxValue.u === '%' ? (boxValue.v * this.element[offsetProp]) / 100 : boxValue.v;
}
const boxPropsOnStart = {};
boxModelProperties.forEach((x) => { boxPropsOnStart[x] = boxModelOnStart; });
// All Component Functions
const boxModelFunctions = {
prepareStart: getBoxModel,
prepareProperty: prepareBoxModel,
onStart: boxPropsOnStart,
};
// Component Full Component
const BoxModel = {
component: 'boxModelProperties',
category: 'boxModel',
properties: boxModelProperties,
defaultValues: boxModelValues,
Interpolate: { numbers },
functions: boxModelFunctions,
};
export default BoxModel;

View file

@ -1,37 +0,0 @@
import KEC from '../objects/kute';
import numbers from '../interpolation/numbers';
// Component Functions
/**
* Sets the update function for the property.
* @param {string} tweenProp the property name
*/
export function boxModelOnStart(tweenProp) {
if (tweenProp in this.valuesEnd && !KEC[tweenProp]) {
KEC[tweenProp] = (elem, a, b, v) => {
/* eslint-disable no-param-reassign -- impossible to satisfy */
/* eslint-disable no-bitwise -- impossible to satisfy */
elem.style[tweenProp] = `${v > 0.99 || v < 0.01
? ((numbers(a, b, v) * 10) >> 0) / 10
: (numbers(a, b, v)) >> 0}px`;
/* eslint-enable no-bitwise */
/* eslint-enable no-param-reassign */
};
}
}
// Component Base Props
const baseBoxProps = ['top', 'left', 'width', 'height'];
const baseBoxOnStart = {};
baseBoxProps.forEach((x) => { baseBoxOnStart[x] = boxModelOnStart; });
// Component Base
const BoxModelBase = {
component: 'baseBoxModel',
category: 'boxModel',
properties: baseBoxProps,
Interpolate: { numbers },
functions: { onStart: baseBoxOnStart },
};
export default BoxModelBase;

View file

@ -1,56 +0,0 @@
import defaultValues from '../objects/defaultValues';
import getStyleForProperty from '../process/getStyleForProperty';
import trueDimension from '../util/trueDimension';
import numbers from '../interpolation/numbers';
import { boxModelOnStart } from './boxModelBase';
// Component Functions
/**
* Returns the current property computed style.
* @param {string} tweenProp the property name
* @returns {string} computed style for property
*/
function getBoxModel(tweenProp) {
return getStyleForProperty(this.element, tweenProp) || defaultValues[tweenProp];
}
/**
* Returns the property tween object.
* @param {string} tweenProp the property name
* @param {string} value the property name
* @returns {number} the property tween object
*/
function prepareBoxModel(tweenProp, value) {
const boxValue = trueDimension(value);
const offsetProp = tweenProp === 'height' ? 'offsetHeight' : 'offsetWidth';
return boxValue.u === '%' ? (boxValue.v * this.element[offsetProp]) / 100 : boxValue.v;
}
// Component Base Props
const essentialBoxProps = ['top', 'left', 'width', 'height'];
const essentialBoxPropsValues = {
top: 0, left: 0, width: 0, height: 0,
};
const essentialBoxOnStart = {};
essentialBoxProps.forEach((x) => { essentialBoxOnStart[x] = boxModelOnStart; });
// All Component Functions
const essentialBoxModelFunctions = {
prepareStart: getBoxModel,
prepareProperty: prepareBoxModel,
onStart: essentialBoxOnStart,
};
// Component Essential
const BoxModelEssential = {
component: 'essentialBoxModel',
category: 'boxModel',
properties: essentialBoxProps,
defaultValues: essentialBoxPropsValues,
Interpolate: { numbers },
functions: essentialBoxModelFunctions,
Util: { trueDimension },
};
export default BoxModelEssential;

View file

@ -1,52 +0,0 @@
import getStyleForProperty from '../process/getStyleForProperty';
import trueDimension from '../util/trueDimension';
import numbers from '../interpolation/numbers';
import { onStartClip } from './clipPropertyBase';
// Component Functions
/**
* Returns the current property computed style.
* @param {string} tweenProp the property name
* @returns {string | number[]} computed style for property
*/
function getClip(tweenProp/* , value */) {
const { element } = this;
const currentClip = getStyleForProperty(element, tweenProp);
const width = getStyleForProperty(element, 'width');
const height = getStyleForProperty(element, 'height');
return !/rect/.test(currentClip) ? [0, width, height, 0] : currentClip;
}
/**
* Returns the property tween object.
* @param {string} _ the property name
* @param {string} value the property value
* @returns {number[]} the property tween object
*/
function prepareClip(/* tweenProp, */_, value) {
if (value instanceof Array) {
return value.map((x) => trueDimension(x));
}
let clipValue = value.replace(/rect|\(|\)/g, '');
clipValue = /,/g.test(clipValue) ? clipValue.split(',') : clipValue.split(/\s/);
return clipValue.map((x) => trueDimension(x));
}
// All Component Functions
const clipFunctions = {
prepareStart: getClip,
prepareProperty: prepareClip,
onStart: onStartClip,
};
// Component Full
const ClipProperty = {
component: 'clipProperty',
property: 'clip',
defaultValue: [0, 0, 0, 0],
Interpolate: { numbers },
functions: clipFunctions,
Util: { trueDimension },
};
export default ClipProperty;

View file

@ -1,36 +0,0 @@
import KEC from '../objects/kute';
import numbers from '../interpolation/numbers';
// Component Functions
/**
* Sets the property update function.
* @param {string} tweenProp the property name
*/
export function onStartClip(tweenProp) {
if (this.valuesEnd[tweenProp] && !KEC[tweenProp]) {
KEC[tweenProp] = (elem, a, b, v) => {
let h = 0; const
cl = [];
for (h; h < 4; h += 1) {
const c1 = a[h].v;
const c2 = b[h].v;
const cu = b[h].u || 'px';
// eslint-disable-next-line no-bitwise -- impossible to satisfy
cl[h] = ((numbers(c1, c2, v) * 100 >> 0) / 100) + cu;
}
// eslint-disable-next-line no-param-reassign -- impossible to satisfy
elem.style.clip = `rect(${cl})`;
};
}
}
// Component Base
const ClipPropertyBase = {
component: 'baseClip',
property: 'clip',
// defaultValue: [0,0,0,0],
Interpolate: { numbers },
functions: { onStart: onStartClip },
};
export default ClipPropertyBase;

View file

@ -1,65 +0,0 @@
import defaultValues from '../objects/defaultValues';
import getStyleForProperty from '../process/getStyleForProperty';
import trueColor from '../util/trueColor';
import numbers from '../interpolation/numbers';
import colors from '../interpolation/colors';
import { onStartColors } from './colorPropertiesBase';
// Component Properties
// supported formats
// 'hex', 'rgb', 'rgba' '#fff' 'rgb(0,0,0)' / 'rgba(0,0,0,0)' 'red' (IE9+)
const supportedColors = [
'color', 'backgroundColor', 'outlineColor',
'borderColor', 'borderTopColor', 'borderRightColor',
'borderBottomColor', 'borderLeftColor',
];
const defaultColors = {};
supportedColors.forEach((tweenProp) => {
defaultColors[tweenProp] = '#000';
});
// Component Functions
const colorsOnStart = {};
supportedColors.forEach((x) => {
colorsOnStart[x] = onStartColors;
});
/**
* Returns the current property computed style.
* @param {string} prop the property name
* @returns {string} property computed style
*/
function getColor(prop/* , value */) {
return getStyleForProperty(this.element, prop) || defaultValues[prop];
}
/**
* Returns the property tween object.
* @param {string} _ the property name
* @param {string} value the property value
* @returns {KUTE.colorObject} the property tween object
*/
function prepareColor(/* prop, */_, value) {
return trueColor(value);
}
// All Component Functions
const colorFunctions = {
prepareStart: getColor,
prepareProperty: prepareColor,
onStart: colorsOnStart,
};
// Component Full
const colorProperties = {
component: 'colorProperties',
category: 'colors',
properties: supportedColors,
defaultValues: defaultColors,
Interpolate: { numbers, colors },
functions: colorFunctions,
Util: { trueColor },
};
export default colorProperties;

View file

@ -1,45 +0,0 @@
import KEC from '../objects/kute';
import numbers from '../interpolation/numbers';
import colors from '../interpolation/colors';
// Component Interpolation
// rgba1, rgba2, progress
// Component Properties
// supported formats
// 'hex', 'rgb', 'rgba' '#fff' 'rgb(0,0,0)' / 'rgba(0,0,0,0)' 'red' (IE9+)
const supportedColors = [
'color', 'backgroundColor', 'outlineColor',
'borderColor',
'borderTopColor', 'borderRightColor',
'borderBottomColor', 'borderLeftColor',
];
// Component Functions
/**
* Sets the property update function.
* @param {string} tweenProp the property name
*/
export function onStartColors(tweenProp) {
if (this.valuesEnd[tweenProp] && !KEC[tweenProp]) {
KEC[tweenProp] = (elem, a, b, v) => {
// eslint-disable-next-line no-param-reassign
elem.style[tweenProp] = colors(a, b, v);
};
}
}
const colorsOnStart = {};
supportedColors.forEach((x) => { colorsOnStart[x] = onStartColors; });
// Component Base
export const baseColors = {
component: 'baseColors',
category: 'colors',
// properties: supportedColors,
// defaultValues: defaultColors,
Interpolate: { numbers, colors },
functions: { onStart: colorsOnStart },
};
export default baseColors;

View file

@ -1,95 +0,0 @@
import KEC from '../objects/kute';
import getInlineStyle from '../process/getInlineStyle';
import defaultValues from '../objects/defaultValues';
import trueProperty from '../util/trueProperty';
import numbers from '../interpolation/numbers';
// Component Const
const transformProperty = trueProperty('transform');
const supportTransform = transformProperty in document.body.style ? 1 : 0;
// Component Functions
/**
* Returns the property current style.
*/
function getComponentCurrentValue(/* tweenProp, value */) {
const currentTransform = getInlineStyle(this.element);
const { left } = this.element.style;
const { top } = this.element.style;
let x = 0;
let y = 0;
if (supportTransform && currentTransform.translate) {
[x, y] = currentTransform.translate;
} else {
x = Number.isFinite(left * 1) ? left : defaultValues.move[0];
y = Number.isFinite(top * 1) ? top : defaultValues.move[1];
}
return [x, y];
}
/**
* Returns the property tween object.
* @param {string} _ property name
* @param {string} value property value
* @returns {number[]} the property tween object
*/
function prepareComponentValue(/* tweenProp */_, value) {
const x = Number.isFinite(value * 1) ? parseInt(value, 10) : parseInt(value[0], 10) || 0;
const y = parseInt(value[1], 10) || 0;
return [x, y];
}
/**
* Sets the property update function.
* @param {string} tweenProp the `path` property
*/
export function onStartComponent(tweenProp/* , value */) {
if (!KEC[tweenProp] && this.valuesEnd[tweenProp]) {
if (supportTransform) {
KEC[tweenProp] = (elem, a, b, v) => {
/* eslint-disable-next-line no-param-reassign -- impossible to satisfy */
elem.style[transformProperty] = `translate(${numbers(a[0], b[0], v)}px,${numbers(a[1], b[1], v)}px)`;
};
} else {
KEC[tweenProp] = (elem, a, b, v) => {
if (a[0] || b[0]) {
/* eslint-disable-next-line no-param-reassign -- impossible to satisfy */
elem.style.left = `${numbers(a[0], b[0], v)}px`;
}
if (a[1] || b[1]) {
/* eslint-disable-next-line no-param-reassign -- impossible to satisfy */
elem.style.top = `${numbers(a[1], b[1], v)}px`;
}
};
}
}
}
// All Component Functions
const componentFunctions = {
prepareStart: getComponentCurrentValue,
prepareProperty: prepareComponentValue,
onStart: onStartComponent,
};
// Base Component
export const baseCrossBrowserMove = {
component: 'baseCrossBrowserMove',
property: 'move',
Interpolate: { numbers },
functions: { onStart: onStartComponent },
};
// Full Component
const crossBrowserMove = {
component: 'crossBrowserMove',
property: 'move',
defaultValue: [0, 0],
Interpolate: { numbers },
functions: componentFunctions,
Util: { trueProperty },
};
export default crossBrowserMove;

View file

@ -1,201 +0,0 @@
import getStyleForProperty from '../process/getStyleForProperty';
import defaultValues from '../objects/defaultValues';
import trueColor from '../util/trueColor';
import numbers from '../interpolation/numbers';
import colors from '../interpolation/colors';
import { dropshadow, onStartFilter } from './filterEffectsBase';
/* filterEffects = {
property: 'filter',
subProperties: {},
defaultValue: {},
interpolators: {},
functions = { prepareStart, prepareProperty, onStart, crossCheck }
} */
// Component Util
/**
* Returns camelCase filter sub-property.
* @param {string} str source string
* @returns {string} camelCase property name
*/
function replaceDashNamespace(str) {
return str.replace('-r', 'R').replace('-s', 'S');
}
/**
* Returns `drop-shadow` sub-property object.
* @param {(string | number)[]} shadow and `Array` with `drop-shadow` parameters
* @returns {KUTE.filterList['dropShadow']} the expected `drop-shadow` values
*/
function parseDropShadow(shadow) {
let newShadow;
if (shadow.length === 3) { // [h-shadow, v-shadow, color]
newShadow = [shadow[0], shadow[1], 0, shadow[2]];
} else if (shadow.length === 4) { // ideal [<offset-x>, <offset-y>, <blur-radius>, <color>]
newShadow = [shadow[0], shadow[1], shadow[2], shadow[3]];
}
// make sure the values are ready to tween
for (let i = 0; i < 3; i += 1) {
newShadow[i] = parseFloat(newShadow[i]);
}
// also the color must be a rgb object
newShadow[3] = trueColor(newShadow[3]);
return newShadow;
}
/**
* Returns an array with current filter sub-properties values.
* @param {string} currentStyle the current filter computed style
* @returns {{[x: string]: number}} the filter tuple
*/
function parseFilterString(currentStyle) {
const result = {};
const fnReg = /(([a-z].*?)\(.*?\))(?=\s([a-z].*?)\(.*?\)|\s*$)/g;
const matches = currentStyle.match(fnReg);
const fnArray = currentStyle !== 'none' ? matches : 'none';
if (fnArray instanceof Array) {
for (let j = 0, jl = fnArray.length; j < jl; j += 1) {
const p = fnArray[j].trim().split(/\((.+)/);
const pp = replaceDashNamespace(p[0]);
if (pp === 'dropShadow') {
const shadowColor = p[1].match(/(([a-z].*?)\(.*?\))(?=\s(.*?))/)[0];
const params = p[1].replace(shadowColor, '').split(/\s/).map(parseFloat);
result[pp] = params.filter((el) => !Number.isNaN(el)).concat(shadowColor);
} else {
result[pp] = p[1].replace(/'|"|\)/g, '');
}
}
}
return result;
}
// Component Functions
/**
* Returns the current property computed style.
* @param {string} tweenProp the property name
* @param {string} value the property value
* @returns {string} computed style for property
*/
function getFilter(tweenProp, value) {
const currentStyle = getStyleForProperty(this.element, tweenProp);
const filterObject = parseFilterString(currentStyle);
let fnp;
Object.keys(value).forEach((fn) => {
fnp = replaceDashNamespace(fn);
if (!filterObject[fnp]) {
filterObject[fnp] = defaultValues[tweenProp][fn];
}
});
return filterObject;
}
/**
* Returns the property tween object.
* @param {string} _ the property name
* @param {string} value the property name
* @returns {KUTE.filterList} the property tween object
*/
function prepareFilter(/* tweenProp, */_, value) {
const filterObject = {};
let fnp;
// property: range | default
// opacity: [0-100%] | 100
// blur: [0-Nem] | 0
// saturate: [0-N%] | 100
// invert: [0-100%] | 0
// grayscale: [0-100%] | 0
// brightness: [0-N%] | 100
// contrast: [0-N%] | 100
// sepia: [0-N%] | 0
// 'hueRotate': [0-Ndeg] | 0
// 'dropShadow': [0,0,0,(r:0,g:0,b:0)] | 0
// url: '' | ''
Object.keys(value).forEach((fn) => {
fnp = replaceDashNamespace(fn);
if (/hue/.test(fn)) {
filterObject[fnp] = parseFloat(value[fn]);
} else if (/drop/.test(fn)) {
filterObject[fnp] = parseDropShadow(value[fn]);
} else if (fn === 'url') {
filterObject[fn] = value[fn];
// } else if ( /blur|opacity|grayscale|sepia/.test(fn) ) {
} else {
filterObject[fn] = parseFloat(value[fn]);
}
});
return filterObject;
}
/**
* Adds missing sub-properties in `valuesEnd` from `valuesStart`.
* @param {string} tweenProp the property name
*/
function crossCheckFilter(tweenProp) {
if (this.valuesEnd[tweenProp]) {
Object.keys(this.valuesStart[tweenProp]).forEach((fn) => {
if (!this.valuesEnd[tweenProp][fn]) {
this.valuesEnd[tweenProp][fn] = this.valuesStart[tweenProp][fn];
}
});
}
}
// All Component Functions
const filterFunctions = {
prepareStart: getFilter,
prepareProperty: prepareFilter,
onStart: onStartFilter,
crossCheck: crossCheckFilter,
};
// Full Component
const filterEffects = {
component: 'filterEffects',
property: 'filter',
// opacity function interfere with opacityProperty
// subProperties: [
// 'blur', 'brightness','contrast','dropShadow',
// 'hueRotate','grayscale','invert','opacity','saturate','sepia','url'
// ],
defaultValue: {
opacity: 100,
blur: 0,
saturate: 100,
grayscale: 0,
brightness: 100,
contrast: 100,
sepia: 0,
invert: 0,
hueRotate: 0,
dropShadow: [0, 0, 0, { r: 0, g: 0, b: 0 }],
url: '',
},
Interpolate: {
opacity: numbers,
blur: numbers,
saturate: numbers,
grayscale: numbers,
brightness: numbers,
contrast: numbers,
sepia: numbers,
invert: numbers,
hueRotate: numbers,
dropShadow: { numbers, colors, dropshadow },
},
functions: filterFunctions,
Util: {
parseDropShadow, parseFilterString, replaceDashNamespace, trueColor,
},
};
export default filterEffects;

View file

@ -1,74 +0,0 @@
import KEC from '../objects/kute';
import numbers from '../interpolation/numbers';
import colors from '../interpolation/colors';
// Component Interpolation
/**
* Sets the `drop-shadow` sub-property update function.
* * disimbiguation `dropshadow` interpolation function and `dropShadow` property
* @param {string} tweenProp the property name
*/
export function dropshadow(a, b, v) {
const params = [];
const unit = 'px';
for (let i = 0; i < 3; i += 1) {
// eslint-disable-next-line no-bitwise
params[i] = ((numbers(a[i], b[i], v) * 100 >> 0) / 100) + unit;
}
return `drop-shadow(${params.concat(colors(a[3], b[3], v)).join(' ')})`;
}
// Component Functions
/**
* Sets the property update function.
* @param {string} tweenProp the property name
*/
export function onStartFilter(tweenProp) {
if (this.valuesEnd[tweenProp] && !KEC[tweenProp]) {
KEC[tweenProp] = (elem, a, b, v) => {
/* eslint-disable-next-line no-param-reassign -- impossible to satisfy */
elem.style[tweenProp] = (b.url ? `url(${b.url})` : '')
/* eslint-disable no-bitwise -- impossible to satisfy */
+ (a.opacity || b.opacity ? `opacity(${((numbers(a.opacity, b.opacity, v) * 100) >> 0) / 100}%)` : '')
+ (a.blur || b.blur ? `blur(${((numbers(a.blur, b.blur, v) * 100) >> 0) / 100}em)` : '')
+ (a.saturate || b.saturate ? `saturate(${((numbers(a.saturate, b.saturate, v) * 100) >> 0) / 100}%)` : '')
+ (a.invert || b.invert ? `invert(${((numbers(a.invert, b.invert, v) * 100) >> 0) / 100}%)` : '')
+ (a.grayscale || b.grayscale ? `grayscale(${((numbers(a.grayscale, b.grayscale, v) * 100) >> 0) / 100}%)` : '')
+ (a.hueRotate || b.hueRotate ? `hue-rotate(${((numbers(a.hueRotate, b.hueRotate, v) * 100) >> 0) / 100}deg)` : '')
+ (a.sepia || b.sepia ? `sepia(${((numbers(a.sepia, b.sepia, v) * 100) >> 0) / 100}%)` : '')
+ (a.brightness || b.brightness ? `brightness(${((numbers(a.brightness, b.brightness, v) * 100) >> 0) / 100}%)` : '')
+ (a.contrast || b.contrast ? `contrast(${((numbers(a.contrast, b.contrast, v) * 100) >> 0) / 100}%)` : '')
+ (a.dropShadow || b.dropShadow ? dropshadow(a.dropShadow, b.dropShadow, v) : '');
/* eslint-enable no-bitwise -- impossible to satisfy */
};
}
}
// Base Component
const baseFilter = {
component: 'baseFilter',
property: 'filter',
// opacity function interfere with opacityProperty
// subProperties: ['blur', 'brightness','contrast','dropShadow',
// 'hueRotate','grayscale','invert','opacity','saturate','sepia','url'],
// defaultValue: {
// opacity: 100, blur: 0, saturate: 100, grayscale: 0,
// brightness: 100, contrast: 100, sepia: 0, invert: 0, hueRotate:0,
// dropShadow: [0,0,0,{r:0,g:0,b:0}], url:''
// },
Interpolate: {
opacity: numbers,
blur: numbers,
saturate: numbers,
grayscale: numbers,
brightness: numbers,
contrast: numbers,
sepia: numbers,
invert: numbers,
hueRotate: numbers,
dropShadow: { numbers, colors, dropshadow },
},
functions: { onStart: onStartFilter },
};
export default baseFilter;

View file

@ -1,131 +0,0 @@
import defaultValues from '../objects/defaultValues';
import onStart from '../objects/onStart';
import trueColor from '../util/trueColor';
import trueDimension from '../util/trueDimension';
import numbers from '../interpolation/numbers';
import colors from '../interpolation/colors';
import { attributes, onStartAttr } from './htmlAttributesBase';
// Component Name
const ComponentName = 'htmlAttributes';
// Component Properties
const svgColors = ['fill', 'stroke', 'stop-color'];
// Component Util
/**
* Returns non-camelcase property name.
* @param {string} a the camelcase property name
* @returns {string} the non-camelcase property name
*/
function replaceUppercase(a) { return a.replace(/[A-Z]/g, '-$&').toLowerCase(); }
// Component Functions
/**
* Returns the current attribute value.
* @param {string} _ the property name
* @param {string} value the property value
* @returns {{[x:string]: string}} attribute value
*/
export function getAttr(/* tweenProp, */_, value) {
const attrStartValues = {};
Object.keys(value).forEach((attr) => {
// get the value for 'fill-opacity' not fillOpacity
// also 'width' not the internal 'width_px'
const attribute = replaceUppercase(attr).replace(/_+[a-z]+/, '');
const currentValue = this.element.getAttribute(attribute);
attrStartValues[attribute] = svgColors.includes(attribute)
? (currentValue || 'rgba(0,0,0,0)')
: (currentValue || (/opacity/i.test(attr) ? 1 : 0));
});
return attrStartValues;
}
/**
* Returns the property tween object.
* @param {string} tweenProp the property name
* @param {string} attrObj the property value
* @returns {number} the property tween object
*/
export function prepareAttr(tweenProp, attrObj) { // attr (string),attrObj (object)
const attributesObject = {};
Object.keys(attrObj).forEach((p) => {
const prop = replaceUppercase(p);
const regex = /(%|[a-z]+)$/;
const currentValue = this.element.getAttribute(prop.replace(/_+[a-z]+/, ''));
if (!svgColors.includes(prop)) {
// attributes set with unit suffixes
if (currentValue !== null && regex.test(currentValue)) {
const unit = trueDimension(currentValue).u || trueDimension(attrObj[p]).u;
const suffix = /%/.test(unit) ? '_percent' : `_${unit}`;
// most "unknown" attributes cannot register into onStart, so we manually add them
onStart[ComponentName][prop + suffix] = (tp) => {
if (this.valuesEnd[tweenProp] && this.valuesEnd[tweenProp][tp] && !(tp in attributes)) {
attributes[tp] = (elem, oneAttr, a, b, v) => {
const _p = oneAttr.replace(suffix, '');
/* eslint no-bitwise: ["error", { "allow": [">>"] }] */
elem.setAttribute(_p, ((numbers(a.v, b.v, v) * 1000 >> 0) / 1000) + b.u);
};
}
};
attributesObject[prop + suffix] = trueDimension(attrObj[p]);
} else if (!regex.test(attrObj[p]) || currentValue === null
|| (currentValue !== null && !regex.test(currentValue))) {
// most "unknown" attributes cannot register into onStart, so we manually add them
onStart[ComponentName][prop] = (tp) => {
if (this.valuesEnd[tweenProp] && this.valuesEnd[tweenProp][tp] && !(tp in attributes)) {
attributes[tp] = (elem, oneAttr, a, b, v) => {
elem.setAttribute(oneAttr, (numbers(a, b, v) * 1000 >> 0) / 1000);
};
}
};
attributesObject[prop] = parseFloat(attrObj[p]);
}
} else { // colors
// most "unknown" attributes cannot register into onStart, so we manually add them
onStart[ComponentName][prop] = (tp) => {
if (this.valuesEnd[tweenProp] && this.valuesEnd[tweenProp][tp] && !(tp in attributes)) {
attributes[tp] = (elem, oneAttr, a, b, v) => {
elem.setAttribute(oneAttr, colors(a, b, v));
};
}
};
attributesObject[prop] = trueColor(attrObj[p]) || defaultValues.htmlAttributes[p];
}
});
return attributesObject;
}
// All Component Functions
const attrFunctions = {
prepareStart: getAttr,
prepareProperty: prepareAttr,
onStart: onStartAttr,
};
// Component Full
const htmlAttributes = {
component: ComponentName,
property: 'attr',
// the Animation class will need some values to validate this Object attribute
subProperties: ['fill', 'stroke', 'stop-color', 'fill-opacity', 'stroke-opacity'],
defaultValue: {
fill: 'rgb(0,0,0)',
stroke: 'rgb(0,0,0)',
'stop-color': 'rgb(0,0,0)',
opacity: 1,
'stroke-opacity': 1,
'fill-opacity': 1, // same here
},
Interpolate: { numbers, colors },
functions: attrFunctions,
// export to global for faster execution
Util: { replaceUppercase, trueColor, trueDimension },
};
export default htmlAttributes;

View file

@ -1,59 +0,0 @@
import KEC from '../objects/kute';
import numbers from '../interpolation/numbers';
import colors from '../interpolation/colors';
// Component Name
const ComponentName = 'baseHTMLAttributes';
// Component Special
const attributes = {};
export { attributes };
export const onStartAttr = {
/**
* onStartAttr.attr
*
* Sets the sub-property update function.
* @param {string} tweenProp the property name
*/
attr(tweenProp) {
if (!KEC[tweenProp] && this.valuesEnd[tweenProp]) {
KEC[tweenProp] = (elem, vS, vE, v) => {
Object.keys(vE).forEach((oneAttr) => {
KEC.attributes[oneAttr](elem, oneAttr, vS[oneAttr], vE[oneAttr], v);
});
};
}
},
/**
* onStartAttr.attributes
*
* Sets the update function for the property.
* @param {string} tweenProp the property name
*/
attributes(tweenProp) {
if (!KEC[tweenProp] && this.valuesEnd.attr) {
KEC[tweenProp] = attributes;
}
},
};
// Component Base
const baseAttributes = {
component: ComponentName,
property: 'attr',
// the Animation class will need some values to validate this Object attribute
// subProperties: ['fill','stroke','stop-color','fill-opacity','stroke-opacity'],
// defaultValue:
// fill : 'rgb(0,0,0)',
// stroke: 'rgb(0,0,0)',
// 'stop-color': 'rgb(0,0,0)',
// opacity: 1,
// 'stroke-opacity': 1,
// 'fill-opacity': 1 // same here
// },
Interpolate: { numbers, colors },
functions: { onStart: onStartAttr },
};
export default baseAttributes;

View file

@ -1,41 +0,0 @@
import getStyleForProperty from '../process/getStyleForProperty';
import numbers from '../interpolation/numbers';
import { onStartOpacity } from './opacityPropertyBase';
// Component Functions
/**
* Returns the current property computed style.
* @param {string} tweenProp the property name
* @returns {string} computed style for property
*/
function getOpacity(tweenProp/* , value */) {
return getStyleForProperty(this.element, tweenProp);
}
/**
* Returns the property tween object.
* @param {string} _ the property name
* @param {string} value the property value
* @returns {number} the property tween object
*/
function prepareOpacity(/* tweenProp, */_, value) {
return parseFloat(value); // opacity always FLOAT
}
// All Component Functions
const opacityFunctions = {
prepareStart: getOpacity,
prepareProperty: prepareOpacity,
onStart: onStartOpacity,
};
// Full Component
const OpacityProperty = {
component: 'opacityProperty',
property: 'opacity',
defaultValue: 1,
Interpolate: { numbers },
functions: opacityFunctions,
};
export default OpacityProperty;

View file

@ -1,36 +0,0 @@
import KEC from '../objects/kute';
import numbers from '../interpolation/numbers';
/* opacityProperty = {
property: 'opacity',
defaultValue: 1,
interpolators: {numbers},
functions = { prepareStart, prepareProperty, onStart }
} */
// Component Functions
/**
* Sets the property update function.
* @param {string} tweenProp the property name
*/
export function onStartOpacity(tweenProp/* , value */) {
// opacity could be 0 sometimes, we need to check regardless
if (tweenProp in this.valuesEnd && !KEC[tweenProp]) {
KEC[tweenProp] = (elem, a, b, v) => {
/* eslint-disable */
elem.style[tweenProp] = ((numbers(a, b, v) * 1000) >> 0) / 1000;
/* eslint-enable */
};
}
}
// Base Component
const OpacityPropertyBase = {
component: 'baseOpacity',
property: 'opacity',
// defaultValue: 1,
Interpolate: { numbers },
functions: { onStart: onStartOpacity },
};
export default OpacityPropertyBase;

View file

@ -1,59 +0,0 @@
import numbers from '../interpolation/numbers';
import {
scrollContainer,
onStartScroll,
onCompleteScroll,
scrollIn,
scrollOut,
getScrollTargets,
preventScroll,
toggleScrollEvents,
} from './scrollPropertyBase';
// Component Functions
/**
* Returns the current property computed style.
* @returns {number} computed style for property
*/
function getScroll() {
this.element = ('scroll' in this.valuesEnd) && (!this.element || this.element === window)
? scrollContainer : this.element;
return this.element === scrollContainer
? (window.pageYOffset || scrollContainer.scrollTop)
: this.element.scrollTop;
}
/**
* Returns the property tween object.
* @param {string} _ the property name
* @param {string} value the property value
* @returns {number} the property tween object
*/
function prepareScroll(/* prop, */_, value) {
return parseInt(value, 10);
}
// All Component Functions
const scrollFunctions = {
prepareStart: getScroll,
prepareProperty: prepareScroll,
onStart: onStartScroll,
onComplete: onCompleteScroll,
};
// Full Component
const ScrollProperty = {
component: 'scrollProperty',
property: 'scroll',
defaultValue: 0,
Interpolate: { numbers },
functions: scrollFunctions,
// export stuff to global
Util: {
preventScroll, scrollIn, scrollOut, getScrollTargets, toggleScrollEvents,
},
};
export default ScrollProperty;

View file

@ -1,113 +0,0 @@
import passiveHandler from 'shorter-js/src/misc/passiveHandler';
import mouseHoverEvents from 'shorter-js/src/strings/mouseHoverEvents';
import supportTouch from 'shorter-js/src/boolean/supportTouch';
import numbers from '../interpolation/numbers';
import KEC from '../objects/kute';
// Component Util
// events preventing scroll
const touchOrWheel = supportTouch ? 'touchstart' : 'mousewheel';
// true scroll container
// very important and specific to the component
export const scrollContainer = navigator && /(EDGE|Mac)/i.test(navigator.userAgent)
? document.body
: document.documentElement;
/**
* Prevent further scroll events until scroll animation is over.
* @param {Event} e event object
*/
export function preventScroll(e) {
if (this.scrolling) e.preventDefault();
}
/**
* Returns the scroll element / target.
* @returns {{el: Element, st: Element}}
*/
export function getScrollTargets() {
const el = this.element;
return el === scrollContainer ? { el: document, st: document.body } : { el, st: el };
}
/**
* Toggles scroll prevention callback on scroll events.
* @param {string} action addEventListener / removeEventListener
* @param {Element} element target
*/
export function toggleScrollEvents(action, element) {
element[action](mouseHoverEvents[0], preventScroll, passiveHandler);
element[action](touchOrWheel, preventScroll, passiveHandler);
}
/**
* Action performed before scroll animation start.
*/
export function scrollIn() {
const targets = getScrollTargets.call(this);
if ('scroll' in this.valuesEnd && !targets.el.scrolling) {
targets.el.scrolling = 1;
toggleScrollEvents('addEventListener', targets.el);
targets.st.style.pointerEvents = 'none';
}
}
/**
* Action performed when scroll animation ends.
*/
export function scrollOut() { // prevent scroll when tweening scroll
const targets = getScrollTargets.call(this);
if ('scroll' in this.valuesEnd && targets.el.scrolling) {
targets.el.scrolling = 0;
toggleScrollEvents('removeEventListener', targets.el);
targets.st.style.pointerEvents = '';
}
}
// Component Functions
/**
* * Sets the scroll target.
* * Adds the scroll prevention event listener.
* * Sets the property update function.
* @param {string} tweenProp the property name
*/
export function onStartScroll(tweenProp) {
// checking 0 will NOT add the render function
if (tweenProp in this.valuesEnd && !KEC[tweenProp]) {
this.element = ('scroll' in this.valuesEnd) && (!this.element || this.element === window)
? scrollContainer : this.element;
scrollIn.call(this);
KEC[tweenProp] = (elem, a, b, v) => {
/* eslint-disable */
elem.scrollTop = (numbers(a, b, v)) >> 0;
/* eslint-enable */
};
}
}
/**
* Removes the scroll prevention event listener.
*/
export function onCompleteScroll(/* tweenProp */) {
scrollOut.call(this);
}
// Base Component
const ScrollPropertyBase = {
component: 'baseScroll',
property: 'scroll',
// defaultValue: 0,
Interpolate: { numbers },
functions: {
onStart: onStartScroll,
onComplete: onCompleteScroll,
},
// unfortunatelly scroll needs all them no matter the packaging
Util: {
preventScroll, scrollIn, scrollOut, getScrollTargets,
},
};
export default ScrollPropertyBase;

View file

@ -1,127 +0,0 @@
import defaultValues from '../objects/defaultValues';
import getStyleForProperty from '../process/getStyleForProperty';
import trueColor from '../util/trueColor';
import numbers from '../interpolation/numbers';
import colors from '../interpolation/colors';
import { onStartShadow } from './shadowPropertiesBase';
// Component Properties
const shadowProps = ['boxShadow', 'textShadow'];
// Component Util
/**
* Return the box-shadow / text-shadow tween object.
* * box-shadow: none | h-shadow v-shadow blur spread color inset|initial|inherit
* * text-shadow: none | offset-x offset-y blur-radius color |initial|inherit
* * numbers must be floats and color must be rgb object
*
* @param {(number | string)[]} shadow an `Array` with shadow parameters
* @param {string} tweenProp the property name
* @returns {KUTE.shadowObject} the property tween object
*/
function processShadowArray(shadow, tweenProp) {
let newShadow;
// [h-shadow, v-shadow, color]
if (shadow.length === 3) {
newShadow = [shadow[0], shadow[1], 0, 0, shadow[2], 'none'];
// [h-shadow, v-shadow, color, inset] | [h-shadow, v-shadow, blur, color]
} else if (shadow.length === 4) {
newShadow = /inset|none/.test(shadow[3])
? [shadow[0], shadow[1], 0, 0, shadow[2], shadow[3]]
: [shadow[0], shadow[1], shadow[2], 0, shadow[3], 'none'];
// [h-shadow, v-shadow, blur, color, inset] | [h-shadow, v-shadow, blur, spread, color]
} else if (shadow.length === 5) {
newShadow = /inset|none/.test(shadow[4])
? [shadow[0], shadow[1], shadow[2], 0, shadow[3], shadow[4]]
: [shadow[0], shadow[1], shadow[2], shadow[3], shadow[4], 'none'];
// ideal [h-shadow, v-shadow, blur, spread, color, inset]
} else if (shadow.length === 6) {
newShadow = shadow;
}
// make sure the values are ready to tween
for (let i = 0; i < 4; i += 1) {
newShadow[i] = parseFloat(newShadow[i]);
}
// also the color must be a rgb object
newShadow[4] = trueColor(newShadow[4]);
newShadow = tweenProp === 'boxShadow'
? newShadow
: newShadow.filter((_, i) => [0, 1, 2, 4].includes(i));
return newShadow;
}
// Component Functions
/**
* Returns the current property computed style.
* @param {string} tweenProp the property name
* @returns {string} computed style for property
*/
export function getShadow(tweenProp/* , value */) {
const cssShadow = getStyleForProperty(this.element, tweenProp);
// '0px 0px 0px 0px rgb(0,0,0)'
return /^none$|^initial$|^inherit$|^inset$/.test(cssShadow)
? defaultValues[tweenProp]
: cssShadow;
}
/**
* Returns the property tween object.
* @param {string} tweenProp the property name
* @param {string} propValue the property value
* @returns {KUTE.shadowObject} the property tween object
*/
export function prepareShadow(tweenProp, propValue) {
// [horizontal, vertical, blur, spread, color: {r:0,g:0,b:0}, inset]
// parseProperty for boxShadow, builds basic structure with ready to tween values
let value = propValue;
if (typeof value === 'string') {
let inset = 'none';
// a full RegEx for color strings
const colRegEx = /(\s?(?:#(?:[\da-f]{3}){1,2}|rgba?\(\d{1,3},\s*\d{1,3},\s*\d{1,3}\))\s?)/gi;
const currentColor = value.match(colRegEx);
// make sure to always have the inset last if possible
inset = /inset/.test(value) ? 'inset' : inset;
value = /inset/.test(value) ? value.replace(/(\s+inset|inset+\s)/g, '') : value;
// also getComputedStyle often returns color first "rgb(0, 0, 0) 15px 15px 6px 0px inset"
value = value.replace(currentColor[0], '').split(' ').concat([currentColor[0].replace(/\s/g, '')], [inset]);
value = processShadowArray(value, tweenProp);
} else if (value instanceof Array) {
value = processShadowArray(value, tweenProp);
}
return value;
}
const shadowPropOnStart = {};
shadowProps.forEach((x) => { shadowPropOnStart[x] = onStartShadow; });
// All Component Functions
const shadowFunctions = {
prepareStart: getShadow,
prepareProperty: prepareShadow,
onStart: shadowPropOnStart,
};
// Component Full
const ShadowProperties = {
component: 'shadowProperties',
properties: shadowProps,
defaultValues: {
boxShadow: '0px 0px 0px 0px rgb(0,0,0)',
textShadow: '0px 0px 0px rgb(0,0,0)',
},
Interpolate: { numbers, colors },
functions: shadowFunctions,
Util: { processShadowArray, trueColor },
};
export default ShadowProperties;

View file

@ -1,46 +0,0 @@
import KEC from '../objects/kute';
import numbers from '../interpolation/numbers';
import colors from '../interpolation/colors';
// Component Properties
const shadowProps = ['boxShadow', 'textShadow'];
// Component Functions
/**
* Sets the property update function.
* @param {string} tweenProp the property name
*/
export function onStartShadow(tweenProp) {
if (this.valuesEnd[tweenProp] && !KEC[tweenProp]) {
KEC[tweenProp] = (elem, a, b, v) => {
// let's start with the numbers | set unit | also determine inset
const params = [];
const unit = 'px';
const sl = tweenProp === 'textShadow' ? 3 : 4;
const colA = sl === 3 ? a[3] : a[4];
const colB = sl === 3 ? b[3] : b[4];
const inset = (a[5] && a[5] !== 'none') || (b[5] && b[5] !== 'none') ? ' inset' : false;
for (let i = 0; i < sl; i += 1) {
/* eslint no-bitwise: ["error", { "allow": [">>"] }] */
params.push(((numbers(a[i], b[i], v) * 1000 >> 0) / 1000) + unit);
}
// the final piece of the puzzle, the DOM update
// eslint-disable-next-line no-param-reassign -- impossible to satisfy
elem.style[tweenProp] = inset
? colors(colA, colB, v) + params.join(' ') + inset
: colors(colA, colB, v) + params.join(' ');
};
}
}
const shadowPropOnStart = {};
shadowProps.forEach((x) => { shadowPropOnStart[x] = onStartShadow; });
// Component Base
const ShadowPropertiesBase = {
component: 'baseShadow',
Interpolate: { numbers, colors },
functions: { onStart: shadowPropOnStart },
};
export default ShadowPropertiesBase;

View file

@ -1,229 +0,0 @@
import parsePathString from 'svg-path-commander/src/parser/parsePathString';
import pathToAbsolute from 'svg-path-commander/src/convert/pathToAbsolute';
import pathToCurve from 'svg-path-commander/src/convert/pathToCurve';
import pathToString from 'svg-path-commander/src/convert/pathToString';
import reverseCurve from 'svg-path-commander/src/process/reverseCurve';
import getDrawDirection from 'svg-path-commander/src/util/getDrawDirection';
import clonePath from 'svg-path-commander/src/process/clonePath';
import splitCubic from 'svg-path-commander/src/process/splitCubic';
import splitPath from 'svg-path-commander/src/process/splitPath';
import fixPath from 'svg-path-commander/src/process/fixPath';
import segmentCubicFactory from 'svg-path-commander/src/util/segmentCubicFactory';
import distanceSquareRoot from 'svg-path-commander/src/math/distanceSquareRoot';
import { onStartCubicMorph } from './svgCubicMorphBase';
import numbers from '../interpolation/numbers';
import selector from '../util/selector';
// Component Util
/**
* Returns first `pathArray` from multi-paths path.
* @param {SVGPathCommander.pathArray | string} source the source `pathArray` or string
* @returns {KUTE.curveSpecs[]} an `Array` with a custom tuple for `equalizeSegments`
*/
function getCurveArray(source) {
return pathToCurve(splitPath(source)[0])
.map((segment, i, pathArray) => {
const segmentData = i && [...pathArray[i - 1].slice(-2), ...segment.slice(1)];
const curveLength = i ? segmentCubicFactory(...segmentData) : 0;
let subsegs;
if (i) {
// must be [segment,segment]
subsegs = curveLength ? splitCubic(segmentData) : [segment, segment];
} else {
subsegs = [segment];
}
return {
s: segment,
ss: subsegs,
l: curveLength,
};
});
}
/**
* Returns two `curveArray` with same amount of segments.
* @param {SVGPathCommander.curveArray} path1 the first `curveArray`
* @param {SVGPathCommander.curveArray} path2 the second `curveArray`
* @param {number} TL the maximum `curveArray` length
* @returns {SVGPathCommander.curveArray[]} equalized segments
*/
function equalizeSegments(path1, path2, TL) {
const c1 = getCurveArray(path1);
const c2 = getCurveArray(path2);
const L1 = c1.length;
const L2 = c2.length;
const l1 = c1.filter((x) => x.l).length;
const l2 = c2.filter((x) => x.l).length;
const m1 = c1.filter((x) => x.l).reduce((a, { l }) => a + l, 0) / l1 || 0;
const m2 = c2.filter((x) => x.l).reduce((a, { l }) => a + l, 0) / l2 || 0;
const tl = TL || Math.max(L1, L2);
const mm = [m1, m2];
const dif = [tl - L1, tl - L2];
let canSplit = 0;
const result = [c1, c2]
.map((x, i) => (x.l === tl
? x.map((y) => y.s)
: x.map((y, j) => {
canSplit = j && dif[i] && y.l >= mm[i];
dif[i] -= canSplit ? 1 : 0;
return canSplit ? y.ss : [y.s];
}).flat()));
return result[0].length === result[1].length
? result
: equalizeSegments(result[0], result[1], tl);
}
/**
* Returns all possible path rotations for `curveArray`.
* @param {SVGPathCommander.curveArray} a the source `curveArray`
* @returns {SVGPathCommander.curveArray[]} all rotations for source
*/
function getRotations(a) {
const segCount = a.length;
const pointCount = segCount - 1;
return a.map((_, idx) => a.map((__, i) => {
let oldSegIdx = idx + i;
let seg;
if (i === 0 || (a[oldSegIdx] && a[oldSegIdx][0] === 'M')) {
seg = a[oldSegIdx];
return ['M', ...seg.slice(-2)];
}
if (oldSegIdx >= segCount) oldSegIdx -= pointCount;
return a[oldSegIdx];
}));
}
/**
* Returns the `curveArray` rotation for the best morphing animation.
* @param {SVGPathCommander.curveArray} a the target `curveArray`
* @param {SVGPathCommander.curveArray} b the reference `curveArray`
* @returns {SVGPathCommander.curveArray} the best `a` rotation
*/
function getRotatedCurve(a, b) {
const segCount = a.length - 1;
const lineLengths = [];
let computedIndex = 0;
let sumLensSqrd = 0;
const rotations = getRotations(a);
rotations.forEach((_, i) => {
a.slice(1).forEach((__, j) => {
sumLensSqrd += distanceSquareRoot(a[(i + j) % segCount].slice(-2), b[j % segCount].slice(-2));
});
lineLengths[i] = sumLensSqrd;
sumLensSqrd = 0;
});
computedIndex = lineLengths.indexOf(Math.min.apply(null, lineLengths));
return rotations[computedIndex];
}
// Component Functions
/**
* Returns the current `d` attribute value.
* @returns {string}
*/
function getCubicMorph(/* tweenProp, value */) {
return this.element.getAttribute('d');
}
/**
* Returns the property tween object.
* @see KUTE.curveObject
*
* @param {string} _ is the `path` property name, not needed
* @param {string | KUTE.curveObject} value the `path` property value
* @returns {KUTE.curveObject}
*/
function prepareCubicMorph(/* tweenProp, */_, value) {
// get path d attribute or create a path from string value
const pathObject = {};
// remove newlines, they break some JSON strings
const pathReg = new RegExp('\\n', 'ig');
let el = null;
if (value instanceof SVGElement) {
el = value;
} else if (/^\.|^#/.test(value)) {
el = selector(value);
}
// make sure to return pre-processed values
if (typeof (value) === 'object' && value.curve) {
return value;
} if (el && /path|glyph/.test(el.tagName)) {
pathObject.original = el.getAttribute('d').replace(pathReg, '');
// maybe it's a string path already
} else if (!el && typeof (value) === 'string') {
pathObject.original = value.replace(pathReg, '');
}
return pathObject;
}
/**
* Enables the `to()` method by preparing the tween object in advance.
* @param {string} tweenProp is `path` tween property, but it's not needed
*/
function crossCheckCubicMorph(tweenProp/** , value */) {
if (this.valuesEnd[tweenProp]) {
const pathCurve1 = this.valuesStart[tweenProp].curve;
const pathCurve2 = this.valuesEnd[tweenProp].curve;
if (!pathCurve1 || !pathCurve2
|| (pathCurve1 && pathCurve2 && pathCurve1[0][0] === 'M' && pathCurve1.length !== pathCurve2.length)) {
const path1 = this.valuesStart[tweenProp].original;
const path2 = this.valuesEnd[tweenProp].original;
const curves = equalizeSegments(path1, path2);
const curve0 = getDrawDirection(curves[0]) !== getDrawDirection(curves[1])
? reverseCurve(curves[0])
: clonePath(curves[0]);
this.valuesStart[tweenProp].curve = curve0;
this.valuesEnd[tweenProp].curve = getRotatedCurve(curves[1], curve0);
}
}
}
// All Component Functions
const svgCubicMorphFunctions = {
prepareStart: getCubicMorph,
prepareProperty: prepareCubicMorph,
onStart: onStartCubicMorph,
crossCheck: crossCheckCubicMorph,
};
// Component Full
const svgCubicMorph = {
component: 'svgCubicMorph',
property: 'path',
defaultValue: [],
Interpolate: { numbers, pathToString },
functions: svgCubicMorphFunctions,
// export utils to global for faster execution
Util: {
pathToCurve,
pathToAbsolute,
pathToString,
parsePathString,
getRotatedCurve,
getRotations,
equalizeSegments,
reverseCurve,
clonePath,
getDrawDirection,
segmentCubicFactory,
splitCubic,
splitPath,
fixPath,
getCurveArray,
},
};
export default svgCubicMorph;

View file

@ -1,38 +0,0 @@
import pathToString from 'svg-path-commander/src/convert/pathToString';
import KEC from '../objects/kute';
import numbers from '../interpolation/numbers';
// Component Functions
/**
* Sets the property update function.
* @param {string} tweenProp the `path` property
*/
export function onStartCubicMorph(tweenProp) {
if (!KEC[tweenProp] && this.valuesEnd[tweenProp]) {
KEC[tweenProp] = function updateMorph(elem, a, b, v) {
const curve = [];
const path1 = a.curve;
const path2 = b.curve;
for (let i = 0, l = path2.length; i < l; i += 1) { // each path command
curve.push([path1[i][0]]);
for (let j = 1, l2 = path1[i].length; j < l2; j += 1) { // each command coordinate
/* eslint-disable-next-line no-bitwise -- impossible to satisfy */
curve[i].push((numbers(path1[i][j], path2[i][j], v) * 1000 >> 0) / 1000);
}
}
elem.setAttribute('d', v === 1 ? b.original : pathToString(curve));
};
}
}
// Component Base
const baseSvgCubicMorph = {
component: 'baseSVGCubicMorph',
property: 'path',
// defaultValue: [],
Interpolate: { numbers, pathToString },
functions: { onStart: onStartCubicMorph },
};
export default baseSvgCubicMorph;

View file

@ -1,210 +0,0 @@
import getStyleForProperty from '../process/getStyleForProperty';
import numbers from '../interpolation/numbers';
import { onStartDraw } from './svgDrawBase';
// Component Util
/**
* Convert a `<path>` length percent value to absolute.
* @param {string} v raw value
* @param {number} l length value
* @returns {number} the absolute value
*/
function percent(v, l) {
return (parseFloat(v) / 100) * l;
}
/**
* Returns the `<rect>` length.
* It doesn't compute `rx` and / or `ry` of the element.
* @see http://stackoverflow.com/a/30376660
* @param {SVGRectElement} el target element
* @returns {number} the `<rect>` length
*/
function getRectLength(el) {
const w = el.getAttribute('width');
const h = el.getAttribute('height');
return (w * 2) + (h * 2);
}
/**
* Returns the `<polyline>` / `<polygon>` length.
* @param {SVGPolylineElement | SVGPolygonElement} el target element
* @returns {number} the element length
*/
function getPolyLength(el) {
const points = el.getAttribute('points').split(' ');
let len = 0;
if (points.length > 1) {
const coord = (p) => {
const c = p.split(',');
if (c.length !== 2) { return 0; } // return undefined
if (Number.isNaN(c[0] * 1) || Number.isNaN(c[1] * 1)) { return 0; }
return [parseFloat(c[0]), parseFloat(c[1])];
};
const dist = (c1, c2) => {
if (c1 !== undefined && c2 !== undefined) {
return Math.sqrt((c2[0] - c1[0]) ** 2 + (c2[1] - c1[1]) ** 2);
}
return 0;
};
if (points.length > 2) {
for (let i = 0; i < points.length - 1; i += 1) {
len += dist(coord(points[i]), coord(points[i + 1]));
}
}
len += el.tagName === 'polygon'
? dist(coord(points[0]), coord(points[points.length - 1])) : 0;
}
return len;
}
/**
* Returns the `<line>` length.
* @param {SVGLineElement} el target element
* @returns {number} the element length
*/
function getLineLength(el) {
const x1 = el.getAttribute('x1');
const x2 = el.getAttribute('x2');
const y1 = el.getAttribute('y1');
const y2 = el.getAttribute('y2');
return Math.sqrt((x2 - x1) ** 2 + (y2 - y1) ** 2);
}
/**
* Returns the `<circle>` length.
* @param {SVGCircleElement} el target element
* @returns {number} the element length
*/
function getCircleLength(el) {
const r = el.getAttribute('r');
return 2 * Math.PI * r;
}
// returns the length of an ellipse
/**
* Returns the `<ellipse>` length.
* @param {SVGEllipseElement} el target element
* @returns {number} the element length
*/
function getEllipseLength(el) {
const rx = el.getAttribute('rx');
const ry = el.getAttribute('ry');
const len = 2 * rx;
const wid = 2 * ry;
return ((Math.sqrt(0.5 * ((len * len) + (wid * wid)))) * (Math.PI * 2)) / 2;
}
/**
* Returns the shape length.
* @param {SVGPathCommander.shapeTypes} el target element
* @returns {number} the element length
*/
function getTotalLength(el) {
if (el.tagName === 'rect') {
return getRectLength(el);
} if (el.tagName === 'circle') {
return getCircleLength(el);
} if (el.tagName === 'ellipse') {
return getEllipseLength(el);
} if (['polygon', 'polyline'].includes(el.tagName)) {
return getPolyLength(el);
} if (el.tagName === 'line') {
return getLineLength(el);
}
// ESLint
return 0;
}
/**
* Returns the property tween object.
* @param {SVGPathCommander.shapeTypes} element the target element
* @param {string | KUTE.drawObject} value the property value
* @returns {KUTE.drawObject} the property tween object
*/
function getDraw(element, value) {
const length = /path|glyph/.test(element.tagName)
? element.getTotalLength()
: getTotalLength(element);
let start;
let end;
let dasharray;
let offset;
if (value instanceof Object && Object.keys(value).every((v) => ['s', 'e', 'l'].includes(v))) {
return value;
} if (typeof value === 'string') {
const v = value.split(/,|\s/);
start = /%/.test(v[0]) ? percent(v[0].trim(), length) : parseFloat(v[0]);
end = /%/.test(v[1]) ? percent(v[1].trim(), length) : parseFloat(v[1]);
} else if (typeof value === 'undefined') {
offset = parseFloat(getStyleForProperty(element, 'stroke-dashoffset'));
dasharray = getStyleForProperty(element, 'stroke-dasharray').split(',');
start = 0 - offset;
end = parseFloat(dasharray[0]) + start || length;
}
return { s: start, e: end, l: length };
}
/**
* Reset CSS properties associated with the `draw` property.
* @param {SVGPathCommander.shapeTypes} element target
*/
function resetDraw(elem) {
/* eslint-disable no-param-reassign -- impossible to satisfy */
elem.style.strokeDashoffset = '';
elem.style.strokeDasharray = '';
/* eslint-disable no-param-reassign -- impossible to satisfy */
}
// Component Functions
/**
* Returns the property tween object.
* @returns {KUTE.drawObject} the property tween object
*/
function getDrawValue(/* prop, value */) {
return getDraw(this.element);
}
/**
* Returns the property tween object.
* @param {string} _ the property name
* @param {string | KUTE.drawObject} value the property value
* @returns {KUTE.drawObject} the property tween object
*/
function prepareDraw(_, value) {
return getDraw(this.element, value);
}
// All Component Functions
const svgDrawFunctions = {
prepareStart: getDrawValue,
prepareProperty: prepareDraw,
onStart: onStartDraw,
};
// Component Full
const SvgDrawProperty = {
component: 'svgDraw',
property: 'draw',
defaultValue: '0% 0%',
Interpolate: { numbers },
functions: svgDrawFunctions,
// Export to global for faster execution
Util: {
getRectLength,
getPolyLength,
getLineLength,
getCircleLength,
getEllipseLength,
getTotalLength,
resetDraw,
getDraw,
percent,
},
};
export default SvgDrawProperty;

View file

@ -1,35 +0,0 @@
import KEC from '../objects/kute';
import numbers from '../interpolation/numbers';
// Component Functions
/**
* Sets the property update function.
* @param {string} tweenProp the property name
*/
export function onStartDraw(tweenProp) {
if (tweenProp in this.valuesEnd && !KEC[tweenProp]) {
KEC[tweenProp] = (elem, a, b, v) => {
/* eslint-disable no-bitwise -- impossible to satisfy */
const pathLength = (a.l * 100 >> 0) / 100;
const start = (numbers(a.s, b.s, v) * 100 >> 0) / 100;
const end = (numbers(a.e, b.e, v) * 100 >> 0) / 100;
const offset = 0 - start;
const dashOne = end + offset;
// eslint-disable-next-line no-param-reassign -- impossible to satisfy
elem.style.strokeDashoffset = `${offset}px`;
// eslint-disable-next-line no-param-reassign -- impossible to satisfy
elem.style.strokeDasharray = `${((dashOne < 1 ? 0 : dashOne) * 100 >> 0) / 100}px, ${pathLength}px`;
/* eslint-disable no-bitwise -- impossible to satisfy */
};
}
}
// Component Base
const SvgDrawBase = {
component: 'baseSVGDraw',
property: 'draw',
Interpolate: { numbers },
functions: { onStart: onStartDraw },
};
export default SvgDrawBase;

View file

@ -1,367 +0,0 @@
import pathToCurve from 'svg-path-commander/src/convert/pathToCurve';
import pathToString from 'svg-path-commander/src/convert/pathToString';
import normalizePath from 'svg-path-commander/src/process/normalizePath';
import splitPath from 'svg-path-commander/src/process/splitPath';
import roundPath from 'svg-path-commander/src/process/roundPath';
import getTotalLength from 'svg-path-commander/src/util/getTotalLength';
import invalidPathValue from 'svg-path-commander/src/parser/invalidPathValue';
import getPointAtLength from 'svg-path-commander/src/util/getPointAtLength';
import polygonArea from 'svg-path-commander/src/math/polygonArea';
import polygonLength from 'svg-path-commander/src/math/polygonLength';
import epsilon from 'svg-path-commander/src/math/epsilon';
import midPoint from 'svg-path-commander/src/math/midPoint';
import distanceSquareRoot from 'svg-path-commander/src/math/distanceSquareRoot';
import { onStartSVGMorph } from './svgMorphBase';
import coords from '../interpolation/coords';
import defaultOptions from '../objects/defaultOptions';
import selector from '../util/selector';
// Component Util
// original script flubber
// https://github.com/veltman/flubber
/**
* Returns an existing polygon or false if it's not a polygon.
* @param {SVGPathCommander.pathArray} pathArray target `pathArray`
* @returns {KUTE.exactPolygon | false} the resulted polygon
*/
function exactPolygon(pathArray) {
const polygon = [];
const pathlen = pathArray.length;
let segment = [];
let pathCommand = '';
if (!pathArray.length || pathArray[0][0] !== 'M') {
return false;
}
for (let i = 0; i < pathlen; i += 1) {
segment = pathArray[i];
[pathCommand] = segment;
if ((pathCommand === 'M' && i) || pathCommand === 'Z') {
break; // !!
} else if ('ML'.includes(pathCommand)) {
polygon.push([segment[1], segment[2]]);
} else {
return false;
}
}
return pathlen ? { polygon } : false;
}
/**
* Returns a new polygon polygon.
* @param {SVGPathCommander.pathArray} parsed target `pathArray`
* @param {number} maxLength the maximum segment length
* @returns {KUTE.exactPolygon} the resulted polygon
*/
function approximatePolygon(parsed, maxLength) {
const ringPath = splitPath(pathToString(parsed))[0];
const normalPath = normalizePath(ringPath);
const pathLength = getTotalLength(normalPath);
const polygon = [];
let numPoints = 3;
let point;
if (maxLength && !Number.isNaN(maxLength) && +maxLength > 0) {
numPoints = Math.max(numPoints, Math.ceil(pathLength / maxLength));
}
for (let i = 0; i < numPoints; i += 1) {
point = getPointAtLength(normalPath, (pathLength * i) / numPoints);
polygon.push([point.x, point.y]);
}
// Make all rings clockwise
if (polygonArea(polygon) > 0) {
polygon.reverse();
}
return {
polygon,
skipBisect: true,
};
}
/**
* Parses a path string and returns a polygon array.
* @param {string} str path string
* @param {number} maxLength maximum amount of points
* @returns {KUTE.exactPolygon} the polygon array we need
*/
function pathStringToPolygon(str, maxLength) {
const parsed = normalizePath(str);
return exactPolygon(parsed) || approximatePolygon(parsed, maxLength);
}
/**
* Rotates a polygon to better match its pair.
* @param {KUTE.polygonMorph} polygon the target polygon
* @param {KUTE.polygonMorph} vs the reference polygon
*/
function rotatePolygon(polygon, vs) {
const len = polygon.length;
let min = Infinity;
let bestOffset;
let sumOfSquares = 0;
let spliced;
let d;
let p;
for (let offset = 0; offset < len; offset += 1) {
sumOfSquares = 0;
for (let i = 0; i < vs.length; i += 1) {
p = vs[i];
d = distanceSquareRoot(polygon[(offset + i) % len], p);
sumOfSquares += d * d;
}
if (sumOfSquares < min) {
min = sumOfSquares;
bestOffset = offset;
}
}
if (bestOffset) {
spliced = polygon.splice(0, bestOffset);
polygon.splice(polygon.length, 0, ...spliced);
}
}
/**
* Sample additional points for a polygon to better match its pair.
* @param {KUTE.polygonObject} polygon the target polygon
* @param {number} numPoints the amount of points needed
*/
function addPoints(polygon, numPoints) {
const desiredLength = polygon.length + numPoints;
const step = polygonLength(polygon) / numPoints;
let i = 0;
let cursor = 0;
let insertAt = step / 2;
let a;
let b;
let segment;
while (polygon.length < desiredLength) {
a = polygon[i];
b = polygon[(i + 1) % polygon.length];
segment = distanceSquareRoot(a, b);
if (insertAt <= cursor + segment) {
polygon.splice(i + 1, 0, segment
? midPoint(a, b, (insertAt - cursor) / segment)
: a.slice(0));
insertAt += step;
} else {
cursor += segment;
i += 1;
}
}
}
/**
* Split segments of a polygon until it reaches a certain
* amount of points.
* @param {number[][]} polygon the target polygon
* @param {number} maxSegmentLength the maximum amount of points
*/
function bisect(polygon, maxSegmentLength = Infinity) {
let a = [];
let b = [];
for (let i = 0; i < polygon.length; i += 1) {
a = polygon[i];
b = i === polygon.length - 1 ? polygon[0] : polygon[i + 1];
// Could splice the whole set for a segment instead, but a bit messy
while (distanceSquareRoot(a, b) > maxSegmentLength) {
b = midPoint(a, b, 0.5);
polygon.splice(i + 1, 0, b);
}
}
}
/**
* Checks the validity of a polygon.
* @param {KUTE.polygonMorph} polygon the target polygon
* @returns {boolean} the result of the check
*/
function validPolygon(polygon) {
return Array.isArray(polygon)
&& polygon.every((point) => Array.isArray(point)
&& point.length === 2
&& !Number.isNaN(point[0])
&& !Number.isNaN(point[1]));
}
/**
* Returns a new polygon and its length from string or another `Array`.
* @param {KUTE.polygonMorph | string} input the target polygon
* @param {number} maxSegmentLength the maximum amount of points
* @returns {KUTE.polygonMorph} normalized polygon
*/
function getPolygon(input, maxSegmentLength) {
let skipBisect;
let polygon;
if (typeof (input) === 'string') {
const converted = pathStringToPolygon(input, maxSegmentLength);
({ polygon, skipBisect } = converted);
} else if (!Array.isArray(input)) {
throw Error(`${invalidPathValue}: ${input}`);
}
/** @type {KUTE.polygonMorph} */
const points = [...polygon];
if (!validPolygon(points)) {
throw Error(`${invalidPathValue}: ${points}`);
}
// TODO skip this test to avoid scale issues?
// Chosen epsilon (1e-6) is problematic for small coordinate range, we now use 1e-9
if (points.length > 1 && distanceSquareRoot(points[0], points[points.length - 1]) < epsilon) {
points.pop();
}
if (!skipBisect && maxSegmentLength
&& !Number.isNaN(maxSegmentLength) && (+maxSegmentLength) > 0) {
bisect(points, maxSegmentLength);
}
return points;
}
/**
* Returns two new polygons ready to tween.
* @param {string} path1 the first path string
* @param {string} path2 the second path string
* @param {number} precision the morphPrecision option value
* @returns {KUTE.polygonMorph[]} the two polygons
*/
function getInterpolationPoints(path1, path2, precision) {
const morphPrecision = precision || defaultOptions.morphPrecision;
const fromRing = getPolygon(path1, morphPrecision);
const toRing = getPolygon(path2, morphPrecision);
const diff = fromRing.length - toRing.length;
addPoints(fromRing, diff < 0 ? diff * -1 : 0);
addPoints(toRing, diff > 0 ? diff : 0);
rotatePolygon(fromRing, toRing);
return [roundPath(fromRing), roundPath(toRing)];
}
// Component functions
/**
* Returns the current `d` attribute value.
* @returns {string} the `d` attribute value
*/
function getSVGMorph(/* tweenProp */) {
return this.element.getAttribute('d');
}
/**
* Returns the property tween object.
* @param {string} _ the property name
* @param {string | KUTE.polygonObject} value the property value
* @returns {KUTE.polygonObject} the property tween object
*/
function prepareSVGMorph(/* tweenProp */_, value) {
const pathObject = {};
// remove newlines, they brake JSON strings sometimes
const pathReg = new RegExp('\\n', 'ig');
let elem = null;
if (value instanceof SVGPathElement) {
elem = value;
} else if (/^\.|^#/.test(value)) {
elem = selector(value);
}
// first make sure we return pre-processed values
if (typeof (value) === 'object' && value.polygon) {
return value;
} if (elem && ['path', 'glyph'].includes(elem.tagName)) {
pathObject.original = elem.getAttribute('d').replace(pathReg, '');
// maybe it's a string path already
} else if (!elem && typeof (value) === 'string') {
pathObject.original = value.replace(pathReg, '');
}
return pathObject;
}
/**
* Enables the `to()` method by preparing the tween object in advance.
* @param {string} prop the `path` property name
*/
function crossCheckSVGMorph(prop) {
if (this.valuesEnd[prop]) {
const pathArray1 = this.valuesStart[prop].polygon;
const pathArray2 = this.valuesEnd[prop].polygon;
// skip already processed paths
// allow the component to work with pre-processed values
if (!pathArray1 || !pathArray2
|| (pathArray1 && pathArray2 && pathArray1.length !== pathArray2.length)) {
const p1 = this.valuesStart[prop].original;
const p2 = this.valuesEnd[prop].original;
// process morphPrecision
const morphPrecision = this._morphPrecision
? parseInt(this._morphPrecision, 10)
: defaultOptions.morphPrecision;
const [path1, path2] = getInterpolationPoints(p1, p2, morphPrecision);
this.valuesStart[prop].polygon = path1;
this.valuesEnd[prop].polygon = path2;
}
}
}
// All Component Functions
const svgMorphFunctions = {
prepareStart: getSVGMorph,
prepareProperty: prepareSVGMorph,
onStart: onStartSVGMorph,
crossCheck: crossCheckSVGMorph,
};
// Component Full
const SVGMorph = {
component: 'svgMorph',
property: 'path',
defaultValue: [],
Interpolate: coords,
defaultOptions: { morphPrecision: 10 },
functions: svgMorphFunctions,
// Export utils to global for faster execution
Util: {
// component
addPoints,
bisect,
getPolygon,
validPolygon,
getInterpolationPoints,
pathStringToPolygon,
distanceSquareRoot,
midPoint,
approximatePolygon,
rotatePolygon,
// svg-path-commander
pathToString,
pathToCurve,
getTotalLength,
getPointAtLength,
polygonArea,
roundPath,
},
};
export default SVGMorph;

View file

@ -1,34 +0,0 @@
import KEC from '../objects/kute';
import coords from '../interpolation/coords';
/* SVGMorph = {
property: 'path',
defaultValue: [],
interpolators: {numbers,coords} },
functions = { prepareStart, prepareProperty, onStart, crossCheck }
} */
// Component functions
/**
* Sets the property update function.
* @param {string} tweenProp the property name
*/
export function onStartSVGMorph(tweenProp) {
if (!KEC[tweenProp] && this.valuesEnd[tweenProp]) {
KEC[tweenProp] = (elem, a, b, v) => {
const path1 = a.polygon; const path2 = b.polygon;
const len = path2.length;
elem.setAttribute('d', (v === 1 ? b.original : `M${coords(path1, path2, len, v).join('L')}Z`));
};
}
}
// Component Base
const SVGMorphBase = {
component: 'baseSVGMorph',
property: 'path',
Interpolate: coords,
functions: { onStart: onStartSVGMorph },
};
export default SVGMorphBase;

View file

@ -1,196 +0,0 @@
import numbers from '../interpolation/numbers';
import { svgTransformOnStart } from './svgTransformBase';
// Component Util
/**
* Returns a correct transform origin consistent with the shape bounding box.
* @param {string} origin transform origin string
* @param {SVGPathCommander.pathBBox} bbox path bounding box
* @returns {number}
*/
function parseStringOrigin(origin, bbox) {
let result;
const { x, width } = bbox;
if (/[a-z]/i.test(origin) && !/px/.test(origin)) {
result = origin.replace(/top|left/, 0)
.replace(/right|bottom/, 100)
.replace(/center|middle/, 50);
} else {
result = /%/.test(origin) ? (x + (parseFloat(origin) * width) / 100) : parseFloat(origin);
}
return result;
}
/**
* Parse SVG transform string and return an object.
* @param {string} a transform string
* @returns {Object<string, (string | number)>}
*/
function parseTransformString(a) {
const c = {};
const d = a && /\)/.test(a)
? a.substring(0, a.length - 1).split(/\)\s|\)/)
: 'none';
if (d instanceof Array) {
for (let j = 0, jl = d.length; j < jl; j += 1) {
const [prop, val] = d[j].trim().split('(');
c[prop] = val;
}
}
return c;
}
/**
* Returns the SVG transform tween object.
* @param {string} _ property name
* @param {Object<string, (string | number)>} v property value object
* @returns {KUTE.transformSVGObject} the SVG transform tween object
*/
function parseTransformSVG(/* prop */_, v) {
/** @type {KUTE.transformSVGObject} */
const svgTransformObject = {};
// by default the transformOrigin is "50% 50%" of the shape box
const bb = this.element.getBBox();
const cx = bb.x + bb.width / 2;
const cy = bb.y + bb.height / 2;
let origin = this._transformOrigin;
let translation;
if (typeof (origin) !== 'undefined') {
origin = origin instanceof Array ? origin : origin.split(/\s/);
} else {
origin = [cx, cy];
}
origin[0] = typeof origin[0] === 'number' ? origin[0] : parseStringOrigin(origin[0], bb);
origin[1] = typeof origin[1] === 'number' ? origin[1] : parseStringOrigin(origin[1], bb);
svgTransformObject.origin = origin;
// populate the valuesStart and / or valuesEnd
Object.keys(v).forEach((i) => {
if (i === 'rotate') {
if (typeof v[i] === 'number') {
svgTransformObject[i] = v[i];
} else if (v[i] instanceof Array) {
[svgTransformObject[i]] = v[i];
} else {
svgTransformObject[i] = v[i].split(/\s/)[0] * 1;
}
} else if (i === 'translate') {
if (v[i] instanceof Array) {
translation = v[i];
} else if (/,|\s/.test(v[i])) {
translation = v[i].split(',');
} else {
translation = [v[i], 0];
}
svgTransformObject[i] = [translation[0] * 1 || 0, translation[1] * 1 || 0];
} else if (/skew/.test(i)) {
svgTransformObject[i] = v[i] * 1 || 0;
} else if (i === 'scale') {
svgTransformObject[i] = parseFloat(v[i]) || 1;
}
});
return svgTransformObject;
}
// Component Functions
/**
* Returns the property tween object.
* @param {string} prop the property name
* @param {string} value the property value
* @returns {KUTE.transformSVGObject} the property tween object
*/
function prepareSvgTransform(prop, value) {
return parseTransformSVG.call(this, prop, value);
}
/**
* Returns an object with the current transform attribute value.
* @param {string} _ the property name
* @param {string} value the property value
* @returns {string} current transform object
*/
function getStartSvgTransform(/* tweenProp */_, value) {
const transformObject = {};
const currentTransform = parseTransformString(this.element.getAttribute('transform'));
// find a value in current attribute value or add a default value
Object.keys(value).forEach((j) => {
const scaleValue = j === 'scale' ? 1 : 0;
transformObject[j] = j in currentTransform ? currentTransform[j] : scaleValue;
});
return transformObject;
}
function svgTransformCrossCheck(prop) {
if (!this._resetStart) return; // fix since 1.6.1 for fromTo() method
if (this.valuesEnd[prop]) {
const valuesStart = this.valuesStart[prop];
const valuesEnd = this.valuesEnd[prop];
const currentTransform = parseTransformSVG.call(this, prop,
parseTransformString(this.element.getAttribute('transform')));
// populate the valuesStart first
Object.keys(currentTransform).forEach((tp) => {
valuesStart[tp] = currentTransform[tp];
});
// now try to determine the REAL translation
const parentSVG = this.element.ownerSVGElement;
const startMatrix = parentSVG.createSVGTransformFromMatrix(
parentSVG.createSVGMatrix()
.translate(-valuesStart.origin[0], -valuesStart.origin[1]) // - origin
.translate('translate' in valuesStart // the current translate
? valuesStart.translate[0] : 0, 'translate' in valuesStart ? valuesStart.translate[1]
: 0)
.rotate(valuesStart.rotate || 0)
.skewX(valuesStart.skewX || 0)
.skewY(valuesStart.skewY || 0)
.scale(valuesStart.scale || 1)// the other functions
.translate(+valuesStart.origin[0], +valuesStart.origin[1]), // + origin
);
// finally the translate we're looking for
valuesStart.translate = [startMatrix.matrix.e, startMatrix.matrix.f];
// copy existing and unused properties to the valuesEnd
Object.keys(valuesStart).forEach((s) => {
if (!(s in valuesEnd) || s === 'origin') {
valuesEnd[s] = valuesStart[s];
}
});
}
}
// All Component Functions
export const svgTransformFunctions = {
prepareStart: getStartSvgTransform,
prepareProperty: prepareSvgTransform,
onStart: svgTransformOnStart,
crossCheck: svgTransformCrossCheck,
};
// Component Full
export const svgTransform = {
component: 'svgTransformProperty',
property: 'svgTransform',
// subProperties: ['translate','rotate','skewX','skewY','scale'],
defaultOptions: { transformOrigin: '50% 50%' },
defaultValue: {
translate: 0, rotate: 0, skewX: 0, skewY: 0, scale: 1,
},
Interpolate: { numbers },
functions: svgTransformFunctions,
// export utils to globals for faster execution
Util: { parseStringOrigin, parseTransformString, parseTransformSVG },
};
export default svgTransform;

View file

@ -1,63 +0,0 @@
import KEC from '../objects/kute';
import numbers from '../interpolation/numbers';
// Component Functions
/**
* Sets the property update function.
* @param {string} tweenProp the property name
*/
export function svgTransformOnStart(tweenProp) {
if (!KEC[tweenProp] && this.valuesEnd[tweenProp]) {
KEC[tweenProp] = (l, a, b, v) => {
let x = 0;
let y = 0;
const deg = Math.PI / 180;
const scale = 'scale' in b ? numbers(a.scale, b.scale, v) : 1;
const rotate = 'rotate' in b ? numbers(a.rotate, b.rotate, v) : 0;
const sin = Math.sin(rotate * deg);
const cos = Math.cos(rotate * deg);
const skewX = 'skewX' in b ? numbers(a.skewX, b.skewX, v) : 0;
const skewY = 'skewY' in b ? numbers(a.skewY, b.skewY, v) : 0;
const complex = rotate || skewX || skewY || scale !== 1 || 0;
// start normalizing the translation, we start from last to first
// (from last chained translation)
// the normalized translation will handle the transformOrigin tween option
// and makes sure to have a consistent transformation
// we start with removing transformOrigin from translation
x -= complex ? b.origin[0] : 0; y -= complex ? b.origin[1] : 0;
x *= scale; y *= scale; // we now apply the scale
// now we apply skews
y += skewY ? x * Math.tan(skewY * deg) : 0; x += skewX ? y * Math.tan(skewX * deg) : 0;
const cxsy = cos * x - sin * y; // apply rotation as well
y = rotate ? sin * x + cos * y : y; x = rotate ? cxsy : x;
// now we apply the actual translation
x += 'translate' in b ? numbers(a.translate[0], b.translate[0], v) : 0;
y += 'translate' in b ? numbers(a.translate[1], b.translate[1], v) : 0;
// normalizing ends with the addition of the transformOrigin to the translation
x += complex ? b.origin[0] : 0; y += complex ? b.origin[1] : 0;
// finally we apply the transform attribute value
/* eslint no-bitwise: ["error", { "allow": [">>"] }] */
l.setAttribute('transform', (x || y ? (`translate(${(x * 1000 >> 0) / 1000}${y ? (`,${(y * 1000 >> 0) / 1000}`) : ''})`) : '')
+ (rotate ? `rotate(${(rotate * 1000 >> 0) / 1000})` : '')
+ (skewX ? `skewX(${(skewX * 1000 >> 0) / 1000})` : '')
+ (skewY ? `skewY(${(skewY * 1000 >> 0) / 1000})` : '')
+ (scale !== 1 ? `scale(${(scale * 1000 >> 0) / 1000})` : ''));
};
}
}
// Component Base
const baseSVGTransform = {
component: 'baseSVGTransform',
property: 'svgTransform',
// subProperties: ['translate','rotate','skewX','skewY','scale'],
// defaultValue: {translate:0, rotate:0, skewX:0, skewY:0, scale:1},
defaultOptions: { transformOrigin: '50% 50%' },
Interpolate: { numbers },
functions: { onStart: svgTransformOnStart },
};
export default baseSVGTransform;

Some files were not shown because too many files have changed in this diff Show more