diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a8cbe9d --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ + +node_modules +package-lock.json +experiments diff --git a/LICENSE b/LICENSE deleted file mode 100644 index 0de8c9e..0000000 --- a/LICENSE +++ /dev/null @@ -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. - diff --git a/README.md b/README.md deleted file mode 100644 index afec5ec..0000000 --- a/README.md +++ /dev/null @@ -1,90 +0,0 @@ -# kute.js -A minimal Native Javascript tweening engine forked from tween.js. Since most of web developers don't actually use yoyo, repeat, play/pause/resume/timeline/whatever or tweening array values (processed with array interpolation functions), I've removed them. kute.js is like a merge of my own jQueryTween with tween.js, but generally it's a much more smarter build. You link the script at your ending <body> tag and write one line to do just about any animation you can think of. - -# CDN -Thanks to jsdelivr, we have CDN link here. - -# Basic Usage -At a glance, you can write one line and you're done. -
-//vanilla js
-new KUTE.Animate(el,options);
-
-//with jQuery plugin
-$('selector').Kute(options);
-
- - -# Advanced Usage -Quite easily, you can write 'bit more lines and you're making the earth go round. -
-//vanilla js
-new KUTE.Animate(el, {
-  //options
-    from	: {},
-    to	: {}, 
-    duration: 500,
-    delay	: 0,
-    easing	: 'exponentialInOut',
-    start			: functionOne, // run function when tween starts 
-    finish			: functionTwo, // run function when tween finishes
-    special			: functionThree // run function while tween runing    
-  }
-);
-
-//with jQuery plugin
-$('selector'). Kute({
-  //options
-    from	: {},
-    to	: {}, 
-    duration: 500,
-    delay	: 0,
-    easing	: 'exponentialInOut',
-    start			: functionOne, // run function when tween starts 
-    finish			: functionTwo, // run function when tween finishes
-    special			: functionThree // run function while tween runing    
-  }
-);
-
- -# Full distribution (12Kb min) -It does most of the animation work you need. -* size: width and height -* colors: text color and background-color (values ) -* transform: translate3D, scale, rotateX, rotateY, rotateZ -* position: top, left (ideal for IE9- translate3D(left,top,0) fallback) -* zoom: for scale on IE8 fallback -* backgroundPosition -* window scroll - -# Base Distribution (9Kb min) -This distribution is much lighter and more suitable for most projects: -* size: width and height -* transform: translate3D, scale, rotateX, rotateY, rotateZ -* position: top, left (ideal for IE9- translate3D(left,top,0) fallback) -* zoom: for scale on IE8 fallback -* window scroll - -#jQuery Plugin -That's right, there you have it, just a few bits of code to bridge the awesome kute.js to your jQuery projects. - -# What else it does -* computes option values properly according to their measurement unit (px,%,deg,etc) -* properly handles IE10+ 3D transforms when elements have a perspective -* it converts RGBA & HEX colors to RGB and tweens the inner values, then ALWAYS updates color via HEX -* properly replaces top, centered or any other background position with proper value to be able to tween -* for most supported properties it reads the current element style property value as initial value (via currentStyle || getComputedStyle) -* allows you to add 3 different callbacks: start, update, finish, and they can be set as tween options (so no more nested functions needed) -* since translate3D is best for performance, kute.js will always uses it -* accepts "nice & easy string" easing functions, like 'linear' or 'exponentialOut' (removes the use of the evil eval, making development easier and closer to fast development standards :) -* uses 31 easing functions, all Robert Penner's easing equations -* like mentioned above, for IE8 zoom is used for transform: scale(0.5), it's not perfect as the object moves from it's floating point to the middle, and some left & top adjustments can be done, but to keep it simple and performance driven, I leave it as is, it's better than nothing. - -# Browser Support -Since most modern browsers can handle pretty much everything, legacy browsers need some help, so give them polyfills.io. Also kute.js needs to know when doing stuff for IE9- like my other scripts here, I highy recommend Paul Irish's conditional stylesheets guides to add ie ie[version] to your site's HTML tag. - -# Demo -coming soon.. - -# License -MIT License diff --git a/assets/css/kute.css b/assets/css/kute.css new file mode 100644 index 0000000..25b4ca5 --- /dev/null +++ b/assets/css/kute.css @@ -0,0 +1,1223 @@ +/*! + * KUTE.js | https://github.com/thednp/kute.js/ + * Licensed under MIT (https://github.com/thednp/kute.js/blob/master/LICENSE) + */ + +/* GLOBAL STYLES +-------------------------------------------------- */ +body { + color: #ccc; + background-color: #08263d; + position: relative +} + +.condensed { + font-family: "Roboto Condensed", "Helvetica Neue", Helvetica, Arial, sans-serif; + font-weight: normal !important +} + +/* smooth scroll */ +html { + scroll-behavior: smooth; +} + +/* WRAPPER STYLES +-------------------------------------------------- */ +.content-wrap { max-width: 80%; position: relative; margin: 0 auto; clear: both; } + +@media (min-width: 1020px){ + .content-wrap { max-width: 100%; width: 980px; } +} + +/* check div consistency +div { background-color: rgba(0,0,0,0.2) }*/ + +/* TYPO STYLES +-------------------------------------------------- */ +p, ul, ol { margin: 0 0 12px } +h1, h2, h3, h4, strong {color: #fff} +h1, h2, h3, h4, h5 { margin: 24px 0; font-weight: bold; } +h1 { font-size: 54px; letter-spacing:-2px; line-height: 48px; } +h2 { font-size: 46px; letter-spacing:-1px; line-height: 48px; } +h3 { font-size: 32px; letter-spacing:-1px; line-height: 36px; } +h4 { font-size: 24px; letter-spacing: 0px; } +/* h1, h3, [class*="col"] > h4 {margin: 0 0 20px} */ + +h1.border, +h2.border, +h3.border, +.lead.border { + padding-bottom: 24px; + margin-bottom: 24px; + border-bottom: 1px solid rgba(150,150,150,0.5) +} + +.text-right { text-align: right } +.text-center { text-align: center } +.text-left { text-align: left } + +.float-left {float:left} +.float-right {float:right} + +.margin-bottom { margin-bottom: 24px !important; } + +.lead { font-size: 18px; color: #fff } + +.nomargin { margin: 0px !important } +@media (min-width: 768px){ + .nomarginlarge { margin: 0 !important } +} + +b,strong {font-weight: 600;color:white} + +footer { + clear: both; overflow: hidden; margin-top: 60px +} + +footer .content-wrap { + padding-top: 5px; + border-top: 1px solid rgb(120,120,120); + border-top: 1px solid rgba(255,255,255,0.2); +} + +footer p {margin: 0 0 10px} + +/* site-wrapper */ +.site-wrapper { position: relative; overflow: hidden; } + +.head-title { margin-top: 60px } + +/* navbar */ +.navbar { + display: flex; + /* justify-content: space-evenly; */ + flex-direction: column; +} + +@media (min-width:768px) { + .navbar { + align-items: center; + flex-direction: row; + } +} + +.nav-wrapper { + flex-basis: 100% +} +.navbar-wrapper { position: relative; clear: both; background: rgba(0,0,0,0.7); padding-bottom: 20px; } +.navbar-wrapper .content-wrap { padding: 20px 0 0; } + +.navbar-wrapper h1 { + color: #fff; + font-size: 32px; line-height: 24px; letter-spacing: 0px; + float: left; padding-right: 24px; margin-right: 24px; margin-top:0; margin-bottom: 0; + border-right: 1px solid rgba(255,255,255,0.2) +} +.navbar-wrapper h1 span { font-size: 24px; color: #555; letter-spacing: -1px; } +.navbar-wrapper h1.active span { color: #ffd626 } +.navbar-wrapper .nav { padding: 0; margin: 0; display: flex; flex-direction: row; } +.nav > li { display: block; line-height: 25px; list-style: none } +.nav > li:not(:last-child) { margin-right: 12px } +.nav li.active > a { color: #ffd626 } +.nav li a { color: #ccc } +.nav li a:hover, .nav li a:focus { text-decoration: none } + +/* share */ +#share { display: flex; flex-direction: row; margin: 0; padding: 0 } +#share li { list-style: none } +#share a { text-decoration: none; } +#share .icon {width:26px; height:26px; vertical-align: middle;} +#share path { fill: none; stroke: currentColor; stroke-width: 50; } +#share li:not(:last-child) { margin-right: 10px; } +#share li:hover path { fill: currentColor; } +#share li:hover a.facebook-link { color: #3578E5} +#share li:hover a.twitter-link { color: #1da1f2 } + + +@media (max-width: 768px){ + .navbar-wrapper h1 {border: 0} + .navbar-wrapper .nav, + .navbar-wrapper h1 { float: none; } +} + + + +/* featurettes */ +.featurettes { + background: #fff; + padding: 60px 0; + width: 100%; + clear: both; + margin: 60px 0; + float: left; + color: #777; +} +/*.featurettes.dark {background: #222}*/ +.featurettes h1, +.featurettes h2, +.featurettes h3, +.featurettes h4, +.featurettes b, +.featurettes .lead, +.featurettes strong {color: #222;} +.featurettes a {color: #2196F3} + +.content-wrap .featurettes { margin: 0 0 20px; padding: 20px 0 0 20px; display: inline-block; border-radius: 10px; position: relative } + +/* example box */ +.example-box { + font-size: 40px; line-height: 150px; text-align:center; font-weight: bold; + float: left; position:relative; + width: 150px; height: 150px; + border-radius: 5px; margin: 0 20px 20px 0; + color: white +} +/*.example-box-model { + font-size: 40px; text-align:center; font-weight: bold; + float: left; position:relative; + border-radius: 5px; margin: 0 20px 20px 0; +} + +svg.example-box { width: auto; height: auto; }*/ + +.easing-example {float: none; font-size: 24px; width: 320px} +.example-buttons {position: absolute; top: 18px; right:0} +.example-buttons + .example-buttons { top: auto; bottom: 18px} + +/* text properties example */ +h1.example-item { + font-size: 50px; + line-height:50px; + color: #333; + /* opacity: 0; */ +} + +h1.example-item span { + line-height: inherit; + display: inline; + vertical-align: top; +} + + + +/* UTILITY STYLES +-------------------------------------------------- */ +.clearfix:before, +.clearfix:after { + content: " "; + display: table; +} +.clearfix:after { + clear: both; +} +.center-block { + display: block; + margin-left: auto; + margin-right: auto; +} +.pull-right { + float: right !important; +} +.pull-left { + float: left !important; +} +.hide { + display: none !important; +} +.show { + display: block !important; +} +.invisible { + visibility: hidden; +} +.text-hide { + font: 0/0 a; + color: transparent; + text-shadow: none; + background-color: transparent; + border: 0; +} +.text-olive { color: #9C27B0;} +.text-indigo { color: #673AB7;} +.text-green { color: #4CAF50;} +.text-red { color: #e91b1f;} +.text-yellow { color: #ffd626;} +.text-blue { color: #2196F3;} +.text-pink { color: #E91E63;} +.text-orange { color: #FF5722;} +.text-lime { color: #CDDC39;} +.text-teal { color: #009688;} +.text-strong { font-weight: bold } + +.hidden { + display: none !important; + visibility: hidden !important; +} + +.hiddenoverflow { overflow: hidden } + +.media {float: left; margin: 5px 20px 0 0; height: auto; font-size: 64px; line-height: 64px; width: 64px; text-align: center} + +/* COLUMN STYLES +-------------------------------------------------- */ +.columns { position: relative; width: 100%; margin: 0 -20px; display:flex; flex-wrap: wrap; } +.columns > [class*="col"] { padding: 0 20px; position: relative } +.columns.welcome {min-height:330px} + +@media (min-width: 768px){ + .columns { flex-direction: row; flex-wrap: wrap } + .columns.welcome {min-height:none} + + + .col2 { max-width: 50%; flex: 0 0 50% } + .col3 { max-width: 33.33%; flex: 0 0 33.33% } + .col4 { max-width: 25%; flex: 0 0 25% } + .col8 { max-width: 75%; flex: 0 0 75% } + .col9 { max-width: 66.65%; flex: 0 0 66.65% } +} + +.table { display: table; height: 480px } +.cell { display: table-cell; vertical-align: middle } + +@media (max-width: 479px){ + .table { height: 320px } +} +@media (min-width: 480px) { + [class*="col"].border:not(:first-child) { + border-left: 1px solid rgba(150,150,150,0.5); + } +} + + +/* STYLING CONTENT +-------------------------------------------------- */ +/* images */ +img { max-width: 100% } +img.circle { border-radius: 50% } + +/* links */ +a { + color: #ffd626; + text-decoration: none; +} +a:hover, +a:focus { + color: #4CAF50; + text-decoration: underline; +} +a:focus { + outline: none; +} + +hr { + border: 1px solid #444; + margin: 10px 0; +} + +/* buttons */ +.btns { + display: inline-flex; + flex: 1 1 auto; + justify-content: center; + text-align: center; +} +.btns .btn { display:inline; line-height: 1; margin: 0 3px 3px 0;} +.btn { padding: 12px 15px; color:#fff; border:0; background-color: #999; line-height: 44px; } +.bg-gray { color:#fff; background-color: #555; fill: #555 } +.btn.active { background-color: #2196F3 } +.featurettes .btn, .featurettes .btn:hover, .featurettes .btn:active, .featurettes .btn:focus, +.btn:hover, .btn:active, .btn:focus { color: #fff; } +.btn:hover, .btn:active, .btn:focus { text-decoration: none; background-color: #777} +.btn-olive, .bg-olive {background-color: #9C27B0; color: #fff; fill: #9C27B0} .btn-olive:hover, .btn-olive:active, .btn-olive:focus { background-color: #FF5722 } +.btn-indigo, .bg-indigo { background-color: #673AB7; color: #fff; fill: #673AB7} .btn-indigo:hover, .btn-indigo:active, .btn-indigo:focus { background-color: #ffd626; color:#000 } +.btn-green, .bg-green { background-color: #4CAF50; color: #fff; fill: #4CAF50} .btn-green:hover, .btn-green:active, .btn-green:focus { background-color: #9C27B0 } +.btn-red, .bg-red { background-color: #e91b1f; color: #fff; fill: #e91b1f} .btn-red:hover, .btn-red:active, .btn-red:focus { background-color: #4CAF50 } +.btn-yellow, .bg-yellow { background-color: #ffd626; color:#000 !important; fill: #ffd626} .btn-yellow:hover, .btn-yellow:active, .btn-yellow:focus { background-color: #4CAF50; color: #fff !important } +.btn-blue, .bg-blue { background-color: #2196F3; color: #fff; fill: #2196F3} .btn-blue:hover, .btn-blue:active, .btn-blue:focus { background-color: #e91b1f } +.btn-pink, .bg-pink { background-color: #E91E63; color: #fff; fill: #E91E63} .btn-pink:hover, .btn-pink:active, .btn-pink:focus { background-color: #2196F3 } +.btn-orange, .bg-orange { background-color: #FF5722; color: #fff; fill: #FF5722} .btn-orange:hover, .btn-orange:active, .btn-orange:focus { background-color: #4CAF50 } +.btn-lime, .bg-lime { background-color: #CDDC39; color: #000; fill: #CDDC39} .btn-lime:hover, .btn-lime:active, .btn-lime:focus { background-color: #e91b1f } +.btn-teal, .bg-teal { background-color: #009688; color: #fff; fill: #009688} .btn-teal:hover, .btn-teal:active, .btn-teal:focus { background-color: #9C27B0 } + +.bg-light {background-color: #fff; color: #777; fill: #fff} + +.icon-large { font-size: 78px; line-height: 0.64; text-shadow: 2px 2px 0 #FFF,3px 3px 0px #ccc;} +.icon-large.fa-cogs:before { color: #4CAF50 } +.icon-large.fa-rocket:before { color: #673AB7 } +.icon-large.fa-code-fork:before { color: #9C27B0 } + +.btn span { + font-size: 150%; + vertical-align: middle; +} + +.btn span.right { margin: 0 0 0 10px } +.btn span.left { margin: 0 10px 0 0 } + +.btn-group { position: relative; display: inline-block } +.btn-group ul { + background: #555; width: 200px; color: #ccc; + position: absolute; bottom: -9999em; left: 0; list-style: none; + overflow: auto; padding: 0; z-index: 5 +} +.btn-group ul li { padding: 5px 15px; cursor: pointer; display: block } +.btn-group ul.subnav li { padding: 0; } +.btn-group ul.subnav li > a { padding: 5px 15px; display: block } +.btn-group ul li.titlef { font-weight: bold; } +.btn-group ul li:hover { + background: #333; +} + +.btn-group.open ul { + bottom: 26px; +} +.nav .btn-group ul {bottom: auto; top: -999em} +.nav .btn-group.open ul { + top: 36px; +} +@media (max-width: 768px){ + .nav .btn-group.open ul { + top: 30px; + } + #easings {left: auto; right: 0} +} + +/* Style the scrolBar for modern browsers */ +.btn-group ul::-webkit-scrollbar { + width: 7px; +} +.btn-group ul::-webkit-scrollbar-thumb { + -webkit-border-radius: 3px; + border-radius: 3px; + background: rgba(92,92,92,0.8); + box-shadow: inset 0 0 0 1px rgba(255,255,255,0.5); +} +.btn-group ul::-webkit-scrollbar-thumb:window-inactive { + background: rgba(255,255,255,0.4); +} + +/* caret */ +.caret { + display: inline-block; + width: 0; + height: 0; + margin-left: 2px; + vertical-align: middle; + border-top: 4px dashed; + border-top: 4px solid\9; + border-right: 4px solid transparent; + border-left: 4px solid transparent; +} + +/* STYLE CODE WRAPPING +-------------------------------------------------- */ +code, kbd, pre { + font-family: Menlo,Monaco,Consolas,"Courier New",monospace; +} +pre { + display: block; + padding: 10px 15px !important; + margin: 0 0 20px !important; + line-height: 2.08; + color: #999; + word-break: break-all; + background-color: rgb(33,33,33); + background-color: rgba(11,11,11,0.5); + /*border: 1px solid rgb(22,22,22); + border: 1px solid rgba(11,11,11,0.8);*/ + border-radius: 4px; + text-align: left; + position: relative; + font-size: 15px; +} +pre.language-javascript:after, +pre.language-clike:after, +pre.language-markup:after { + position: absolute; top:0; right:0; padding: 2px 5px; + background: #000; + border-radius: 0px 0px 0px 5px; + font-family: Helvetica, Arial, sans-serif; + font-size: 12px; color: #999; +} + +pre.language-javascript:after {content: 'JavaScript';} +pre.language-clike:after {content: 'node';} +pre.language-markup:after {content: 'HTML';} +pre code {background: none;padding: 0; font-weight: normal; font-size: 100%;} +code { + padding: 2px 4px; + font-size: 90%; + color: #999; + background-color: #111; + border-radius: 4px; + white-space: pre; + font-weight: bold +} + +kbd { + padding: 2px 4px; + font-size: 90%; + color: #333; + background-color: #eee; + border-radius: 3px; + font-weight: bold +} + +#rectangle {transform-origin: 50% 50%;} + +.w-20{ + width:20% !important +} +.w-25{ + width:25% !important +} +.w-33{ + width:33.33% !important +} +.w-50{ + width:50% !important +} +.w-66{ + width:66.67% !important +} +.w-75{ + width:75% !important +} +.w-80{ + width:80% !important +} +.w-100{ + width:100% !important +} +.w-auto{ + width:auto !important +} +.h-20vh{ + height:20vh !important +} +.h-25vh{ + height:25vh !important +} +.h-33vh{ + height:33.33vh !important +} +.h-50vh{ + height:50vh !important +} +.h-66vh{ + height:66.67vh !important +} +.h-75vh{ + height:75vh !important +} +.h-80vh{ + height:80vh !important +} +.h-100{ + height:100% !important +} +.h-auto{ + height:auto !important +} +@media(min-width: 768px){ + .w-md-20{ + width:20% !important + } + .w-md-25{ + width:25% !important + } + .w-md-33{ + width:33.33% !important + } + .w-md-50{ + width:50% !important + } + .w-md-66{ + width:66.67% !important + } + .w-md-75{ + width:75% !important + } + .w-md-80{ + width:80% !important + } + .w-md-100{ + width:100% !important + } + .w-md-auto{ + width:auto !important + } + .h-md-20vh{ + height:20vh !important + } + .h-md-25vh{ + height:25vh !important + } + .h-md-33vh{ + height:33.33vh !important + } + .h-md-50vh{ + height:50vh !important + } + .h-md-66vh{ + height:66.67vh !important + } + .h-md-75vh{ + height:75vh !important + } + .h-md-80vh{ + height:80vh !important + } + .h-md-100{ + height:100% !important + } + .h-md-auto{ + height:auto !important + } +} +.d-none{ + display:none +} +.d-inline{ + display:inline +} +.d-flex{ + display:flex +} +.d-block{ + display:block +} +@media(min-width: 768px){ + .d-md-none{ + display:none + } + .d-md-inline{ + display:inline + } + .d-md-flex{ + display:flex + } + .d-md-block{ + display:block + } +} +.flex-row{ + flex-direction:row +} +.flex-row-reverse{ + flex-direction:row-reverse +} +.flex-column{ + flex-direction:column +} +.flex-column-reverse{ + flex-direction:column-reverse +} +.align-items-start{ + align-items:flex-start +} +.align-items-end{ + align-items:flex-end +} +.align-items-center{ + align-items:center +} +.align-items-baseline{ + align-items:baseline +} +.align-items-stretch{ + align-items:stretch +} +.align-self-start{ + align-self:flex-start +} +.align-self-end{ + align-self:flex-end +} +.align-self-center{ + align-self:center +} +.align-self-baseline{ + align-self:baseline +} +.align-self-stretch{ + align-self:stretch +} +.justify-content-start{ + justify-content:flex-start +} +.justify-content-end{ + justify-content:flex-end +} +.justify-content-center{ + justify-content:center +} +.justify-content-between{ + justify-content:space-between +} +.justify-content-around{ + justify-content:space-around +} +.align-content-start{ + align-content:flex-start +} +.align-content-end{ + align-content:flex-end +} +.align-content-center{ + align-content:center +} +.align-content-around{ + align-content:space-around +} +.align-content-stretch{ + align-content:stretch +} +.flex-fill{ + flex:1 1 auto !important +} +.flex-grow-1{ + flex-grow:1 +} +.flex-grow-0{ + flex-grow:0 +} +.flex-shrink-1{ + flex-shrink:1 +} +.flex-shrink-0{ + flex-shrink:0 +} +.flex-nowrap{ + flex-wrap:nowrap +} +.flex-wrap{ + flex-wrap:wrap +} +.flex-wrap-reverse{ + flex-wrap:wrap-reverse +} +@media(min-width: 768px){ + .flex-md-row{ + flex-direction:row + } + .flex-md-row-reverse{ + flex-direction:row-reverse + } + .flex-md-column{ + flex-direction:column + } + .flex-md-column-reverse{ + flex-direction:column-reverse + } + .align-items-md-start{ + align-items:flex-start + } + .align-items-md-end{ + align-items:flex-end + } + .align-items-md-center{ + align-items:center + } + .align-items-md-baseline{ + align-items:baseline + } + .align-items-md-stretch{ + align-items:stretch + } + .align-self-md-start{ + align-self:flex-start + } + .align-self-md-end{ + align-self:flex-end + } + .align-self-md-center{ + align-self:center + } + .align-self-md-baseline{ + align-self:baseline + } + .align-self-md-stretch{ + align-self:stretch + } + .justify-content-md-start{ + justify-content:flex-start + } + .justify-content-md-end{ + justify-content:flex-end + } + .justify-content-md-center{ + justify-content:center + } + .justify-content-md-between{ + justify-content:space-between + } + .justify-content-md-around{ + justify-content:space-around + } + .flex-md-fill{ + flex:1 1 auto !important + } + .flex-md-grow-1{ + flex-grow:1 + } + .flex-md-grow-0{ + flex-grow:0 + } + .flex-md-shrink-1{ + flex-shrink:1 + } + .flex-md-shrink-0{ + flex-shrink:0 + } + .flex-md-nowrap{ + flex-wrap:nowrap + } + .flex-md-wrap{ + flex-wrap:wrap + } + .flex-md-wrap-reverse{ + flex-wrap:wrap-reverse + } +} + +.overflow-visible{ + overflow:visible +} +.overflow-hidden{ + overflow:hidden +} +.perspective{ + perspective:500px; + backface-visibility:hidden +} +.perspective-1000{ + perspective:1000px; + backface-visibility:hidden +} +.perspective-1500{ + perspective:1500px; + backface-visibility:hidden +} +.m-0{ + margin:0 !important +} +.m-1{ + margin:.25rem !important +} +.m-2{ + margin:.5rem !important +} +.m-3{ + margin:1rem !important +} +.m-4{ + margin:1.5rem !important +} +.m-5{ + margin:3rem !important +} +.m-auto{ + margin:auto !important +} +.mx-0{ + margin-right:0 !important; + margin-left:0 !important +} +.mx-1{ + margin-right:.25rem !important; + margin-left:.25rem !important +} +.mx-2{ + margin-right:.5rem !important; + margin-left:.5rem !important +} +.mx-3{ + margin-right:1rem !important; + margin-left:1rem !important +} +.mx-4{ + margin-right:1.5rem !important; + margin-left:1.5rem !important +} +.mx-5{ + margin-right:3rem !important; + margin-left:3rem !important +} +.mx-auto{ + margin-right:auto !important; + margin-left:auto !important +} +.my-0{ + margin-top:0 !important; + margin-bottom:0 !important +} +.my-1{ + margin-top:.25rem !important; + margin-bottom:.25rem !important +} +.my-2{ + margin-top:.5rem !important; + margin-bottom:.5rem !important +} +.my-3{ + margin-top:1rem !important; + margin-bottom:1rem !important +} +.my-4{ + margin-top:1.5rem !important; + margin-bottom:1.5rem !important +} +.my-5{ + margin-top:3rem !important; + margin-bottom:3rem !important +} +.my-auto{ + margin-top:auto !important; + margin-bottom:auto !important +} +.mt-0{ + margin-top:0 !important +} +.mt-1{ + margin-top:.25rem !important +} +.mt-2{ + margin-top:.5rem !important +} +.mt-3{ + margin-top:1rem !important +} +.mt-4{ + margin-top:1.5rem !important +} +.mt-5{ + margin-top:3rem !important +} +.mt-auto{ + margin-top:auto !important +} +.mr-0{ + margin-right:0 !important +} +.mr-1{ + margin-right:.25rem !important +} +.mr-2{ + margin-right:.5rem !important +} +.mr-3{ + margin-right:1rem !important +} +.mr-4{ + margin-right:1.5rem !important +} +.mr-5{ + margin-right:3rem !important +} +.mr-auto{ + margin-right:auto !important +} +.mb-0{ + margin-bottom:0 !important +} +.mb-1{ + margin-bottom:.25rem !important +} +.mb-2{ + margin-bottom:.5rem !important +} +.mb-3{ + margin-bottom:1rem !important +} +.mb-4{ + margin-bottom:1.5rem !important +} +.mb-5{ + margin-bottom:3rem !important +} +.mb-auto{ + margin-bottom:auto !important +} +.ml-0{ + margin-left:0 !important +} +.ml-1{ + margin-left:.25rem !important +} +.ml-2{ + margin-left:.5rem !important +} +.ml-3{ + margin-left:1rem !important +} +.ml-4{ + margin-left:1.5rem !important +} +.ml-5{ + margin-left:3rem !important +} +.ml-auto{ + margin-left:auto !important +} +.p-0{ + padding:0 !important +} +.p-1{ + padding:.25rem !important +} +.p-2{ + padding:.5rem !important +} +.p-3{ + padding:1rem !important +} +.p-4{ + padding:1.5rem !important +} +.p-5{ + padding:3rem !important +} +.px-0{ + padding-right:0 !important; + padding-left:0 !important +} +.px-1{ + padding-right:.25rem !important; + padding-left:.25rem !important +} +.px-2{ + padding-right:.5rem !important; + padding-left:.5rem !important +} +.px-3{ + padding-right:1rem !important; + padding-left:1rem !important +} +.px-4{ + padding-right:1.5rem !important; + padding-left:1.5rem !important +} +.px-5{ + padding-right:3rem !important; + padding-left:3rem !important +} +.py-0{ + padding-top:0 !important; + padding-bottom:0 !important +} +.py-1{ + padding-top:.25rem !important; + padding-bottom:.25rem !important +} +.py-2{ + padding-top:.5rem !important; + padding-bottom:.5rem !important +} +.py-3{ + padding-top:1rem !important; + padding-bottom:1rem !important +} +.py-4{ + padding-top:1.5rem !important; + padding-bottom:1.5rem !important +} +.py-5{ + padding-top:3rem !important; + padding-bottom:3rem !important +} +.pt-0{ + padding-top:0 !important +} +.pt-1{ + padding-top:.25rem !important +} +.pt-2{ + padding-top:.5rem !important +} +.pt-3{ + padding-top:1rem !important +} +.pt-4{ + padding-top:1.5rem !important +} +.pt-5{ + padding-top:3rem !important +} +.pr-0{ + padding-right:0 !important +} +.pr-1{ + padding-right:.25rem !important +} +.pr-2{ + padding-right:.5rem !important +} +.pr-3{ + padding-right:1rem !important +} +.pr-4{ + padding-right:1.5rem !important +} +.pr-5{ + padding-right:3rem !important +} +.pb-0{ + padding-bottom:0 !important +} +.pb-1{ + padding-bottom:.25rem !important +} +.pb-2{ + padding-bottom:.5rem !important +} +.pb-3{ + padding-bottom:1rem !important +} +.pb-4{ + padding-bottom:1.5rem !important +} +.pb-5{ + padding-bottom:3rem !important +} +.pl-0{ + padding-left:0 !important +} +.pl-1{ + padding-left:.25rem !important +} +.pl-2{ + padding-left:.5rem !important +} +.pl-3{ + padding-left:1rem !important +} +.pl-4{ + padding-left:1.5rem !important +} +.pl-5{ + padding-left:3rem !important +} +.vertical-align-top{ + vertical-align:top +} +.vertical-align-middle{ + vertical-align:middle +} +.vertical-align-bottom{ + vertical-align:bottom +} +.vertical-align-baseline{ + vertical-align:baseline +} +.vertical-align-text-top{ + vertical-align:text-top +} +.vertical-align-text-bottom{ + vertical-align:text-bottom +} +@media(min-width: 768px){ + .vertical-align-md-top{ + vertical-align:top + } + .vertical-align-md-middle{ + vertical-align:middle + } + .vertical-align-md-bottom{ + vertical-align:bottom + } + .vertical-align-md-baseline{ + vertical-align:baseline + } + .vertical-align-md-text-top{ + vertical-align:text-top + } + .vertical-align-md-text-bottom{ + vertical-align:text-bottom + } +} +.b-position-center-top{ + background-position:center top !important +} +.b-position-center{ + background-position:center center !important +} +.b-position-center-bottom{ + background-position:center bottom !important +} +.b-position-left-top{ + background-position:left top !important +} +.b-position-left-center{ + background-position:left center !important +} +.b-position-left-bottom{ + background-position:left bottom !important +} +.b-position-right-top{ + background-position:right top !important +} +.b-position-right-center{ + background-position:right center !important +} +.b-position-right-bottom{ + background-position:right bottom !important +} +@media screen and (-ms-high-contrast: active),(-ms-high-contrast: none){ + .h-20vh{ + height:216px !important + } + .h-25vh{ + height:270px !important + } + .h-33vh{ + height:359.964px !important + } + .h-50vh{ + height:540px !important + } + .h-66vh{ + height:720.036px !important + } + .h-75vh{ + height:810px !important + } + .h-80vh{ + height:864px !important + } + .h-100{ + height:100% !important + } + .h-auto{ + height:auto !important + } +} +@media screen and (-ms-high-contrast: active)and (min-width: 768px),(-ms-high-contrast: none)and (min-width: 768px){ + .h-md-20vh{ + height:216px !important + } + .h-md-25vh{ + height:270px !important + } + .h-md-33vh{ + height:359.964px !important + } + .h-md-50vh{ + height:540px !important + } + .h-md-66vh{ + height:720.036px !important + } + .h-md-75vh{ + height:810px !important + } + .h-md-80vh{ + height:864px !important + } + .h-md-100{ + height:100% !important + } + .h-md-auto{ + height:auto !important + } +} diff --git a/assets/css/prism.css b/assets/css/prism.css new file mode 100644 index 0000000..b6092ab --- /dev/null +++ b/assets/css/prism.css @@ -0,0 +1,226 @@ +/* PrismJS 1.20.0 +https://prismjs.com/download.html#themes=prism-tomorrow&languages=markup+css+clike+javascript+scss&plugins=line-highlight+line-numbers */ +/** + * prism.js tomorrow night eighties for JavaScript, CoffeeScript, CSS and HTML + * Based on https://github.com/chriskempson/tomorrow-theme + * @author Rose Pritchard + */ + +code[class*="language-"], +pre[class*="language-"] { + color: #ccc; + background: none; + font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace; + font-size: 1em; + text-align: left; + white-space: pre; + word-spacing: normal; + word-break: normal; + word-wrap: normal; + line-height: 1.5; + + -moz-tab-size: 4; + -o-tab-size: 4; + tab-size: 4; + + -webkit-hyphens: none; + -moz-hyphens: none; + -ms-hyphens: none; + hyphens: none; + +} + +/* Code blocks */ +pre[class*="language-"] { + padding: 1em; + margin: .5em 0; + overflow: auto; +} + +:not(pre) > code[class*="language-"], +pre[class*="language-"] { + background: #051725; +} + +/* Inline code */ +:not(pre) > code[class*="language-"] { + padding: .1em; + border-radius: .3em; + white-space: normal; +} + +.token.comment, +.token.block-comment, +.token.prolog, +.token.doctype, +.token.cdata { + color: #999; +} + +.token.punctuation { + color: #ccc; +} + +.token.tag, +.token.attr-name, +.token.namespace, +.token.deleted { + color: #e2777a; +} + +.token.function-name { + color: #6196cc; +} + +.token.boolean, +.token.number, +.token.function { + color: #f08d49; +} + +.token.property, +.token.class-name, +.token.constant, +.token.symbol { + color: #f8c555; +} + +.token.selector, +.token.important, +.token.atrule, +.token.keyword, +.token.builtin { + color: #cc99cd; +} + +.token.string, +.token.char, +.token.attr-value, +.token.regex, +.token.variable { + color: #7ec699; +} + +.token.operator, +.token.entity, +.token.url { + color: #67cdcc; +} + +.token.important, +.token.bold { + font-weight: bold; +} +.token.italic { + font-style: italic; +} + +.token.entity { + cursor: help; +} + +.token.inserted { + color: green; +} + +pre[data-line] { + position: relative; + padding: 1em 0 1em 3em; +} + +.line-highlight { + position: absolute; + left: 0; + right: 0; + padding: inherit 0; + margin-top: 1em; /* Same as .prism’s padding-top */ + + background: hsla(24, 20%, 50%,.08); + background: linear-gradient(to right, hsla(24, 20%, 50%,.1) 70%, hsla(24, 20%, 50%,0)); + + pointer-events: none; + + line-height: inherit; + white-space: pre; +} + + .line-highlight:before, + .line-highlight[data-end]:after { + content: attr(data-start); + position: absolute; + top: .4em; + left: .6em; + min-width: 1em; + padding: 0 .5em; + background-color: hsla(24, 20%, 50%,.4); + color: hsl(24, 20%, 95%); + font: bold 65%/1.5 sans-serif; + text-align: center; + vertical-align: .3em; + border-radius: 999px; + text-shadow: none; + box-shadow: 0 1px white; + } + + .line-highlight[data-end]:after { + content: attr(data-end); + top: auto; + bottom: .4em; + } + +.line-numbers .line-highlight:before, +.line-numbers .line-highlight:after { + content: none; +} + +pre[id].linkable-line-numbers span.line-numbers-rows { + pointer-events: all; +} +pre[id].linkable-line-numbers span.line-numbers-rows > span:before { + cursor: pointer; +} +pre[id].linkable-line-numbers span.line-numbers-rows > span:hover:before { + background-color: rgba(128, 128, 128, .2); +} + +pre[class*="language-"].line-numbers { + position: relative; + padding-left: 3.8em; + counter-reset: linenumber; +} + +pre[class*="language-"].line-numbers > code { + position: relative; + white-space: inherit; +} + +.line-numbers .line-numbers-rows { + position: absolute; + pointer-events: none; + top: 0; + font-size: 100%; + left: -3.8em; + width: 3em; /* works for line-numbers below 1000 lines */ + letter-spacing: -1px; + border-right: 1px solid #999; + + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + +} + + .line-numbers-rows > span { + display: block; + counter-increment: linenumber; + } + + .line-numbers-rows > span:before { + content: counter(linenumber); + color: #999; + display: block; + padding-right: 0.8em; + text-align: right; + } + diff --git a/assets/css/reset.css b/assets/css/reset.css new file mode 100644 index 0000000..789f37e --- /dev/null +++ b/assets/css/reset.css @@ -0,0 +1,305 @@ +*,*::before,*::after{ + box-sizing:border-box +} +body{ + margin:0; + font-family:system-ui,-apple-system,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji"; + font-size:1rem; + font-weight:400; + line-height:1.5; + color:#212529; + background-color:#fff; + -webkit-text-size-adjust:100%; + -webkit-tap-highlight-color:rgba(0,0,0,0) +} +[tabindex="-1"]:focus:not(:focus-visible){ + outline:0 !important +} +hr{ + margin:1rem 0; + color:inherit; + background-color:currentColor; + border:0; + opacity:.25 +} +hr:not([size]){ + height:1px +} +h1,h2,h3,h4,h5,h6{ + margin-top:0; + font-weight:500; + line-height:1.2 +} +h1{ + font-size:calc(1.375rem + 1.5vw) +} +@media(min-width: 1200px){ + h1{ + font-size:2.5rem + } +} +h2{ + font-size:calc(1.325rem + 0.9vw) +} +@media(min-width: 1200px){ + h2{ + font-size:2rem + } +} +h3{ + font-size:calc(1.3rem + 0.6vw) +} +@media(min-width: 1200px){ + h3{ + font-size:1.75rem + } +} +h4{ + font-size:calc(1.275rem + 0.3vw) +} +@media(min-width: 1200px){ + h4{ + font-size:1.5rem + } +} +h5{ + font-size:1.25rem +} +h6{ + font-size:1rem +} +p{ + margin-top:0; + margin-bottom:1rem +} +abbr[title],abbr[data-original-title]{ + text-decoration:underline; + -webkit-text-decoration:underline dotted; + text-decoration:underline dotted; + cursor:help; + -webkit-text-decoration-skip-ink:none; + text-decoration-skip-ink:none +} +address{ + margin-bottom:1rem; + font-style:normal; + line-height:inherit +} +ol,ul{ + padding-left:2rem +} +ol,ul,dl{ + margin-top:0; + margin-bottom:1rem +} +ol ol,ul ul,ol ul,ul ol{ + margin-bottom:0 +} +dt{ + font-weight:700 +} +dd{ + margin-bottom:.5rem; + margin-left:0 +} +blockquote{ + margin:0 0 1rem +} +b,strong{ + font-weight:bolder +} +small{ + font-size:.875em +} +mark{ + padding:.2em; + background-color:#fcf8e3 +} +sub,sup{ + position:relative; + font-size:.75em; + line-height:0; + vertical-align:baseline +} +sub{ + bottom:-0.25em +} +sup{ + top:-0.5em +} +a{ + color:#0d6efd; + text-decoration:underline +} +a:hover{ + color:#024dbc +} +a:not([href]):not([class]),a:not([href]):not([class]):hover{ + color:inherit; + text-decoration:none +} +pre,code,kbd,samp{ + font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace; + font-size:1em +} +pre{ + display:block; + margin-top:0; + margin-bottom:1rem; + overflow:auto; + font-size:.875em; + -ms-overflow-style:scrollbar +} +pre code{ + font-size:inherit; + color:inherit; + word-break:normal +} +code{ + font-size:.875em; + color:#d63384; + word-wrap:break-word +} +a>code{ + color:inherit +} +kbd{ + padding:.2rem .4rem; + font-size:.875em; + color:#fff; + background-color:#212529; + border-radius:.2rem +} +kbd kbd{ + padding:0; + font-size:1em; + font-weight:700 +} +figure{ + margin:0 0 1rem +} +img,svg{ + vertical-align:middle +} +table{ + caption-side:bottom; + border-collapse:collapse +} +caption{ + padding-top:.5rem; + padding-bottom:.5rem; + color:#6c757d; + text-align:left +} +th{ + text-align:inherit; + text-align:-webkit-match-parent +} +thead,tbody,tfoot,tr,td,th{ + border-color:inherit; + border-style:solid; + border-width:0 +} +label{ + display:inline-block +} +button{ + border-radius:0 +} +button:focus{ + outline:1px dotted; + outline:5px auto -webkit-focus-ring-color +} +input,button,select,optgroup,textarea{ + margin:0; + font-family:inherit; + font-size:inherit; + line-height:inherit +} +button,input{ + overflow:visible +} +button,select{ + text-transform:none +} +[role=button]{ + cursor:pointer +} +select{ + word-wrap:normal +} +[list]::-webkit-calendar-picker-indicator{ + display:none +} +button,[type=button],[type=reset],[type=submit]{ + -webkit-appearance:button +} +button:not(:disabled),[type=button]:not(:disabled),[type=reset]:not(:disabled),[type=submit]:not(:disabled){ + cursor:pointer +} +::-moz-focus-inner{ + padding:0; + border-style:none +} +textarea{ + resize:vertical +} +fieldset{ + min-width:0; + padding:0; + margin:0; + border:0 +} +legend{ + float:left; + width:100%; + padding:0; + margin-bottom:.5rem; + font-size:calc(1.275rem + 0.3vw); + line-height:inherit; + white-space:normal +} +@media(min-width: 1200px){ + legend{ + font-size:1.5rem + } +} +legend+*{ + clear:left +} +::-webkit-datetime-edit-fields-wrapper,::-webkit-datetime-edit-text,::-webkit-datetime-edit-minute,::-webkit-datetime-edit-hour-field,::-webkit-datetime-edit-day-field,::-webkit-datetime-edit-month-field,::-webkit-datetime-edit-year-field{ + padding:0 +} +::-webkit-inner-spin-button{ + height:auto +} +[type=search]{ + outline-offset:-2px; + -webkit-appearance:textfield +} +::-webkit-search-decoration{ + -webkit-appearance:none +} +::-webkit-color-swatch-wrapper{ + padding:0 +} +::-webkit-file-upload-button{ + font:inherit; + -webkit-appearance:button +} +output{ + display:inline-block +} +iframe{ + border:0 +} +summary{ + display:list-item; + cursor:pointer +} +progress{ + vertical-align:baseline +} +[hidden]{ + display:none !important +} \ No newline at end of file diff --git a/assets/css/spicr-theme.css b/assets/css/spicr-theme.css new file mode 100644 index 0000000..a92055f --- /dev/null +++ b/assets/css/spicr-theme.css @@ -0,0 +1,172 @@ +/* Spicr theme | thednp © 2020 | MIT-License */ + +.text-left{ + text-align:left +} +.text-center{ + text-align:center +} +.text-right{ + text-align:right +} +@media(min-width: 768px){ + .text-md-left{ + text-align:left + } + .text-md-center{ + text-align:center + } + .text-md-right{ + text-align:right + } +} +.center-block{ + display:block; + margin-left:auto; + margin-right:auto +} +.float-right{ + float:right !important +} +.float-left{ + float:left !important +} +.float-none{ + float:none !important +} +.font-weight-normal{ + font-weight:normal +} +.font-weight-bold{ + font-weight:bold +} + +#SpicrDemo{ +height:600px +} +.spicr .lead{ + margin:0; + font-weight:bold; + text-transform:uppercase; + color:#fff +} +.overlay{ + background:rgba(0,0,0,.45); + position:absolute; + top:0; + right:0; + width:100%; + height:100% +} +.spicr-carousel-navigation>*{ + vertical-align:text-bottom +} +@media(min-width: 479px){ + .spicr-control.long-shadows{ + transition:opacity .3s ease-in + } + .spicr-control.long-shadows .arrow-prev{ + margin-left:-280px; + padding:0px 5px 0px 250px; + transform:translate(-100%) + } + .spicr-control.long-shadows .arrow-next{ + margin-right:-280px; + padding:0px 250px 0px 5px; + transform:translate(100%) + } + .spicr-control.long-shadows .arrow-prev,.spicr-control.long-shadows .arrow-next{ + transition:all 1s ease-in + } + .spicr:hover .spicr-control.long-shadows .arrow-prev,.spicr:hover .spicr-control.long-shadows .arrow-next{ + transition-duration:.3s; + transition-timing-function:ease-out + } + .spicr-control.long-shadows:focus .arrow-prev,.spicr-control.long-shadows:focus .arrow-next{ + transform:translate(0%) + } + .spicr:hover .spicr-control.long-shadows .arrow-prev{ + transform:translate(0%) + } + .spicr:hover .spicr-control.long-shadows .arrow-next{ + transform:translate(0%) + } + .spicr-control.long-shadows .spicr-icon{ + width:40px; + height:40px + } + .spicr-control.long-shadows .arrow-next,.spicr-control.long-shadows .arrow-prev{ + border-radius:40px; + margin-top:-20px + } + .spicr-control.long-shadows:focus .arrow-prev{ + transform:translate(0%) + } + .spicr-control.long-shadows:focus .arrow-next{ + transform:translate(0%) + } + .spicr-control.long-shadows .arrow-prev,.spicr-control.long-shadows .arrow-next{ + display:block; + width:auto; + height:auto; + background-color:#000; + background-color:rgba(0,0,0,.3) + } +} +.spicr-carousel h4{ + margin-top:0 +} +.spicr-slider{ + font-size:calc(0.5rem + 1.5vw); + line-height:calc(0.6rem + 1.5vw) +} +.spicr-slider h1{ + font-size:calc(1.375rem + 1.5vw); + letter-spacing:-1px; + margin:0 +} +.spicr-slider h2{ + font-size:calc(1.375rem + 0.9vw); + letter-spacing:0; + margin:0 +} +.features-carousel .spicr-pages{ + top:.5rem +} +.features-carousel .spicr-pages li{ + line-height:1.5rem; + padding:.5rem 1rem +} +@media(min-width: 768px){ + .featurette-heading span{ + font-size:24px; + line-height:1; + letter-spacing:-1px + } + .spicr-slider{ + font-size:1rem; + line-height:1.8rem + } + .spicr-slider h1{ + font-size:42px; + line-height:1; + letter-spacing:-2px; + margin:0 0 1.5rem + } + .spicr-slider h2{ + font-size:36px; + line-height:1; + letter-spacing:-1px; + margin:0 0 1.5rem + } +} + +.background-top-left { background-position: top left !important} +.background-top-center { background-position: top center !important} +.background-top-right { background-position: top center !important} +.background-center-left { background-position: center left !important} +.background-center-center { background-position: center center !important} +.background-center-right { background-position: center right !important} +.background-bottom-left { background-position: bottom left !important} +.background-bottom-center { background-position: bottom center !important} +.background-bottom-right { background-position: bottom right !important} \ No newline at end of file diff --git a/assets/img/apple-touch-icon.png b/assets/img/apple-touch-icon.png new file mode 100644 index 0000000..ed1ba7e Binary files /dev/null and b/assets/img/apple-touch-icon.png differ diff --git a/assets/img/favicon.ico b/assets/img/favicon.ico new file mode 100644 index 0000000..c7f98a7 Binary files /dev/null and b/assets/img/favicon.ico differ diff --git a/assets/img/home.svg b/assets/img/home.svg new file mode 100644 index 0000000..dfe389b --- /dev/null +++ b/assets/img/home.svg @@ -0,0 +1,56 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/assets/img/img-blank.gif b/assets/img/img-blank.gif new file mode 100644 index 0000000..5891de4 Binary files /dev/null and b/assets/img/img-blank.gif differ diff --git a/assets/img/loader.gif b/assets/img/loader.gif new file mode 100644 index 0000000..335e00c Binary files /dev/null and b/assets/img/loader.gif differ diff --git a/assets/img/ms.svg b/assets/img/ms.svg new file mode 100644 index 0000000..88df967 --- /dev/null +++ b/assets/img/ms.svg @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + diff --git a/assets/img/rectangle.svg b/assets/img/rectangle.svg new file mode 100644 index 0000000..9efd23e --- /dev/null +++ b/assets/img/rectangle.svg @@ -0,0 +1,14 @@ + + + + + + + diff --git a/assets/img/social.svg b/assets/img/social.svg new file mode 100644 index 0000000..b9b079b --- /dev/null +++ b/assets/img/social.svg @@ -0,0 +1,34 @@ + + + + + + + + + + + + + diff --git a/assets/js/backgroundPosition.js b/assets/js/backgroundPosition.js new file mode 100644 index 0000000..ff08225 --- /dev/null +++ b/assets/js/backgroundPosition.js @@ -0,0 +1,11 @@ +/* BACKGROUND POSITION EXAMPLE */ +var bgPos = document.getElementById('bgPos'), + bgBox = bgPos.querySelector('.example-box'), + bgb = bgPos.querySelector('.btn'), + bpTween = KUTE.to(bgBox, {backgroundPosition: ['0%','50%']}, { yoyo: true, repeat: 1, duration: 1500, easing: 'easingCubicOut'}); + +bgb.addEventListener('click', function(e){ + e.preventDefault(); + !bpTween.playing && bpTween.start(); +},false); +/* BACKGROUND POSITION EXAMPLE */ diff --git a/assets/js/borderRadius.js b/assets/js/borderRadius.js new file mode 100644 index 0000000..6aa183e --- /dev/null +++ b/assets/js/borderRadius.js @@ -0,0 +1,12 @@ + +/* RADIUS EXAMPLES */ +var radiusBtn = document.getElementById('radiusBtn'); +var allRadius = KUTE.to('#allRadius',{borderRadius:80},{duration: 1500, easing:'easingCubicOut', repeat: 1, yoyo: true}); +var tlRadius = KUTE.to('#tlRadius',{borderTopLeftRadius:150},{duration: 1500, easing:'easingCubicOut', repeat: 1, yoyo: true}); +var trRadius = KUTE.to('#trRadius',{borderTopRightRadius:150},{duration: 1500, easing:'easingCubicOut', repeat: 1, yoyo: true}); +var blRadius = KUTE.to('#blRadius',{borderBottomLeftRadius:150},{duration: 1500, easing:'easingCubicOut', repeat: 1, yoyo: true}); +var brRadius = KUTE.to('#brRadius',{borderBottomRightRadius:150},{duration: 1500, easing:'easingCubicOut', repeat: 1, yoyo: true}); +radiusBtn.addEventListener('click',function(){ + if (!allRadius.playing) { allRadius.start(); tlRadius.start(); trRadius.start(); blRadius.start(); brRadius.start(); } +}, false); +/* RADIUS EXAMPLES */ \ No newline at end of file diff --git a/assets/js/boxModel.js b/assets/js/boxModel.js new file mode 100644 index 0000000..bf705d6 --- /dev/null +++ b/assets/js/boxModel.js @@ -0,0 +1,49 @@ +/* BOX MODEL EXAMPLE */ +var boxModel = document.getElementById('boxModel'), + btb = boxModel.querySelector('.btn'), + box = boxModel.querySelector('.example-box-model'); + +// built the tween objects +var bm1 = KUTE.to(box,{width:250},{ yoyo: true, repeat: 1, duration: 1500, onUpdate: onWidth}); +var bm2 = KUTE.to(box,{height:250},{ yoyo: true, repeat: 1, duration: 1500, onUpdate: onHeight}); +var bm3 = KUTE.to(box,{left:250},{ yoyo: true, repeat: 1, duration: 1500, onUpdate: onLeft}); +var bm4 = KUTE.to(box,{top:-250},{ yoyo: true, repeat: 1, duration: 1500, onUpdate: onTop, onComplete: onComplete}); + +var bm5 = KUTE.to(box,{marginTop:50},{ yoyo: true, repeat: 1, duration: 1500, onUpdate: onMarginTop}); +var bm6 = KUTE.to(box,{marginBottom:50},{ yoyo: true, repeat: 1, duration: 1500, onUpdate: onMarginBottom}); +var bm7 = KUTE.to(box,{paddingTop:15},{ yoyo: true, repeat: 1, duration: 1500, onUpdate: onPadding}); +var bm8 = KUTE.to(box,{marginTop:50,marginLeft:50,marginBottom:70},{ yoyo: true, repeat: 1, duration: 1500, onUpdate: onMargin, onComplete: onComplete}); + +// chain the bms +try{ + bm1.chain(bm2); + bm2.chain(bm3); + bm3.chain(bm4); + bm4.chain(bm5); + bm5.chain(bm6); + bm6.chain(bm7); + bm7.chain(bm8); +}catch(e){ + console.error(e+". TweenBase doesn\'t support chain method") +} + +//callback functions +function onWidth() { box.innerHTML = 'WIDTH
'+parseInt(box.offsetWidth)+'px'; } +function onHeight() { box.innerHTML = 'HEIGHT
'+parseInt(box.offsetHeight)+'px'; } +function onLeft() { box.innerHTML = 'LEFT
'+parseInt(box.offsetLeft)+'px'; } +function onTop() { box.innerHTML = 'TOP
'+parseInt(box.offsetTop)+'px'; } + +function onMarginTop() { box.innerHTML = parseInt(box.style.marginTop)+'px'+'
MARGIN'; } +function onMarginBottom() { box.innerHTML = 'MARGIN
'+parseInt(box.style.marginBottom)+'px'; } +function onPadding() { box.innerHTML = parseInt(box.style.paddingTop)+'px
PADDING'; } +function onMargin() { box.innerHTML = 'MARGIN
'+parseInt(box.style.marginTop)+'px'; } + +function onComplete() { box.innerHTML = 'BOX
 MODEL '; } + +btb.addEventListener('click', function(e){ + e.preventDefault(); + !bm1.playing && !bm2.playing && !bm3.playing && !bm4.playing + !bm5.playing && !bm6.playing && !bm7.playing && !bm8.playing + && bm1.start(); +},false); +/* BOX MODEL EXAMPLE */ \ No newline at end of file diff --git a/assets/js/clipProperty.js b/assets/js/clipProperty.js new file mode 100644 index 0000000..1af3688 --- /dev/null +++ b/assets/js/clipProperty.js @@ -0,0 +1,25 @@ + +/* CLIP EXAMPLE */ +var clipExample = document.getElementById('clip'), + clipElement = clipExample.querySelector('.example-box'), + clpbtn = clipExample.querySelector('.btn'); + +var clp1 = KUTE.to(clipElement, {clip: [0,20,150,0]}, {duration:500, easing: 'easingCubicOut'}); +var clp2 = KUTE.to(clipElement, {clip: [0,150,150,130]}, {duration:600, easing: 'easingCubicOut'}); +var clp3 = KUTE.to(clipElement, {clip: [0,150,20,0]}, {duration:800, easing: 'easingCubicOut'}); +var clp4 = KUTE.to(clipElement, {clip: [0,150,150,0]}, {duration:1200, easing: 'easingExponentialInOut'}); + +//chain some clps +try{ + clp1.chain(clp2); + clp2.chain(clp3); + clp3.chain(clp4); +}catch(e){ + console.error(e+". TweenBase doesn\'t support chain method") +} + +clpbtn.addEventListener('click', function(e){ + e.preventDefault(); + !clp1.playing && !clp2.playing && !clp3.playing && !clp4.playing && clp1.start(); +},false); +/* CLIP EXAMPLE */ diff --git a/assets/js/colorProperties.js b/assets/js/colorProperties.js new file mode 100644 index 0000000..225ed6b --- /dev/null +++ b/assets/js/colorProperties.js @@ -0,0 +1,39 @@ +/* COLORS EXAMPLE */ +var colBox = document.getElementById('colBox'), + colBoxElement = colBox.querySelector('.example-box'), + colbtn = colBox.querySelector('.btn'); + +var colTween1 = KUTE.to(colBoxElement, {color: '#9C27B0'}, {duration: 1000}); +var colTween2 = KUTE.to(colBoxElement, {backgroundColor: '#069'}, {duration: 1000}); +var colTween3 = KUTE.to(colBoxElement, {color: '#fff'}, {duration: 1000}); +var colTween4 = KUTE.to(colBoxElement, {backgroundColor: '#9C27B0'}, {duration: 1000}); +var colTween5 = KUTE.to(colBoxElement, {borderColor: '#069'}, {duration: 1000}); +var colTween6 = KUTE.to(colBoxElement, {borderTopColor: '#9C27B0'}, {duration: 1000}); +var colTween7 = KUTE.to(colBoxElement, {borderRightColor: '#9C27B0'}, {duration: 1000}); +var colTween8 = KUTE.to(colBoxElement, {borderBottomColor: '#9C27B0'}, {duration: 1000}); +var colTween9 = KUTE.to(colBoxElement, {borderLeftColor: '#9C27B0'}, {duration: 1000}); +var colTween10 = KUTE.to(colBoxElement, {outlineColor: '#9C27B0'}, {duration: 1000, repeat: 1, yoyo: true}); + + +try { + colTween1.chain(colTween2); + colTween2.chain(colTween3); + colTween3.chain(colTween4); + colTween4.chain(colTween5); + colTween5.chain(colTween6); + colTween6.chain(colTween7); + colTween7.chain(colTween8); + colTween8.chain(colTween9); + colTween9.chain(colTween10); +} catch(e){ + console.error(e+". TweenBase doesn\'t support chain method") +} + + +colbtn.addEventListener('click', function(e){ + e.preventDefault(); + !colTween1.playing && !colTween2.playing && !colTween3.playing && !colTween4.playing + !colTween5.playing && !colTween6.playing && !colTween7.playing && !colTween8.playing + !colTween9.playing && !colTween10.playing && colTween1.start(); +},false); +/* COLORS EXAMPLE */ \ No newline at end of file diff --git a/assets/js/filterEffects.js b/assets/js/filterEffects.js new file mode 100644 index 0000000..bffb294 --- /dev/null +++ b/assets/js/filterEffects.js @@ -0,0 +1,18 @@ + +/* FILTER EFFECTS EXAMPLES */ +var filterExamples = document.getElementById('filter-examples'), + filterBtn = filterExamples.querySelector('.btn'), + fe1 = filterExamples.getElementsByTagName('div')[0], + fe2 = filterExamples.getElementsByTagName('div')[1], + fe3 = filterExamples.getElementsByTagName('div')[2], + fe4 = filterExamples.getElementsByTagName('div')[3], + fe1Tween = KUTE.to(fe1, {filter :{ url: '#mySVGFilter', blur: 0.05, saturate: 10 }}, {easing: 'easingCubicOut', duration: 1500, repeat:1, yoyo: true}), + fe2Tween = KUTE.to(fe2, {filter :{ url: '#mySVGFilter', sepia: 50, invert: 80 }}, {easing: 'easingCubicOut', duration: 1500, repeat:1, yoyo: true}), + fe3Tween = KUTE.to(fe3, {filter :{ url: '#mySVGFilter', saturate: 150, brightness: 60 }}, {easing: 'easingCubicOut', duration: 1500, repeat:1, yoyo: true}), + fe4Tween = KUTE.to(fe4, {filter :{ url: '#mySVGFilter', opacity: 40, hueRotate: 45, dropShadow:[-10,-10,5,{r:0,g:0,b:0,a:1}] }}, {easing: 'easingCubicOut', duration: 1500, repeat:1, yoyo: true}); +filterBtn.addEventListener('click', function(){ + !fe1Tween.playing && fe1Tween.start(); + !fe2Tween.playing && fe2Tween.start(); + !fe3Tween.playing && fe3Tween.start(); + !fe4Tween.playing && fe4Tween.start(); +}, false); \ No newline at end of file diff --git a/assets/js/home.js b/assets/js/home.js new file mode 100644 index 0000000..f13b12a --- /dev/null +++ b/assets/js/home.js @@ -0,0 +1,14 @@ +var SpicrMainDemo = document.getElementById('SpicrDemo'); + +function initMainSpicr(){ + new Spicr(SpicrMainDemo); +} + +function loadCarouselMedia(){ + new dll(SpicrMainDemo,initMainSpicr) +} + +document.addEventListener('DOMContentLoaded', function loadWrapper(){ + loadCarouselMedia(); + document.removeEventListener('DOMContentLoaded', loadWrapper, false) +}, false); \ No newline at end of file diff --git a/assets/js/htmlAttributes.js b/assets/js/htmlAttributes.js new file mode 100644 index 0000000..0f07c21 --- /dev/null +++ b/assets/js/htmlAttributes.js @@ -0,0 +1,49 @@ +// radius attribute +// var radiusTween = KUTE.to('#circle', {attr: {r: '75px'} }, {repeat:1, yoyo: true, easing: 'easingCubicOut'}); +var radiusTween = KUTE.to('#circle', {attr: {r: '75%'} }, {repeat:1, yoyo: true, easing: 'easingCubicOut'}); + +// coordinates of the circle center +var coordinatesTween1 = KUTE.to('#circle', {attr: {cx:40,cy:40,fillOpacity:0.3}}, { duration: 300, easing: 'easingCubicOut'}); +var coordinatesTween2 = KUTE.to('#circle', {attr: {cx:110,cy:40}}, { duration: 400, easing: 'linear'}); +var coordinatesTween3 = KUTE.to('#circle', {attr: {cx:75,cy:75,fillOpacity:1}}, { easing: 'easingCubicOut'}); + +try{ + coordinatesTween1.chain(coordinatesTween2); + coordinatesTween2.chain(coordinatesTween3); + coordinatesTween3.chain(radiusTween); +}catch(e){ + console.error(e+". TweenBase doesn\'t support chain method") +} + + +var circleBtn = document.getElementById('circleBtn'); +circleBtn.addEventListener('click', function(){ + !coordinatesTween1.playing && !coordinatesTween2.playing && !coordinatesTween3.playing && !radiusTween.playing && coordinatesTween1.start(); +}); + + +// coordinates of gradient +var gradBtn = document.getElementById('gradBtn'); +var closingGradient = KUTE.to('#gradient',{attr: {x1:'49%', x2:'49%', y1:'49%', y2:'51%'}}, {easing: 'easingCubicInOut'}); +var rotatingGradient1 = KUTE.to('#gradient',{attr: {x1:'49%', x2:'51%', y1:'51%', y2:'51%'}}, {easing: 'easingCubicInOut'}); +var rotatingGradient2 = KUTE.to('#gradient',{attr: {x1:'0%', x2:'51%', y1:'100%', y2:'0%'}}, {easing: 'easingCubicInOut'}); +var openingGradient = KUTE.to('#gradient',{attr: {x1:'0%', x2:'0%', y1:'0%', y2:'100%'}}, {duration: 1500, easing: 'easingCubicInOut'}); + +try{ + closingGradient.chain(rotatingGradient1); + rotatingGradient1.chain(rotatingGradient2); + rotatingGradient2.chain(openingGradient); +}catch(e){ + console.error(e+". TweenBase doesn\'t support chain method") +} + +gradBtn.addEventListener('click', function(){ + !closingGradient.playing && !rotatingGradient1.playing && !rotatingGradient2.playing && !openingGradient.playing && closingGradient.start(); +}); + +// fill color +var fillBtn = document.getElementById('fillBtn'); +var fillAttribute = KUTE.to('#fill',{attr: {fill: 'red'}}, {duration: 1500, repeat: 1, yoyo: true }); +fillBtn.addEventListener('click', function(){ + !fillAttribute.playing && fillAttribute.start(); +}); \ No newline at end of file diff --git a/assets/js/opacityProperty.js b/assets/js/opacityProperty.js new file mode 100644 index 0000000..40b4555 --- /dev/null +++ b/assets/js/opacityProperty.js @@ -0,0 +1,23 @@ +/* OPACITY EXAMPLE */ +var opacityProperty = document.getElementById('opacityProperty'), + button = opacityProperty.getElementsByClassName('btn')[0], + heart = opacityProperty.getElementsByClassName('example-box')[0], + // fade out + fadeOutTween = KUTE.to(heart,{opacity:0},{duration:2000}), + // fade in + fadeInTween = KUTE.to(heart,{opacity:1},{duration:2000}), + + isHidden = true; + +button.addEventListener('click', function(e){ + e.preventDefault(); + if ( !isHidden && !fadeOutTween.playing && !fadeInTween.playing ) { + fadeOutTween.start(); + isHidden = !isHidden; + + } else if ( isHidden && !fadeOutTween.playing && !fadeInTween.playing ) { + fadeInTween.start(); + isHidden = !isHidden; + } +},false); +/* OPACITY EXAMPLE */ diff --git a/assets/js/perf-matrix.js b/assets/js/perf-matrix.js new file mode 100644 index 0000000..b4fcbb5 --- /dev/null +++ b/assets/js/perf-matrix.js @@ -0,0 +1,111 @@ +// testing grounds +"use strict"; + +var mobileType = '', + isMobile = { + Windows: function() { + var checkW = /IEMobile|Windows Mobile/i.test(navigator.userAgent); + mobileType += checkW ? 'Windows Phones.' : ''; + return checkW; + }, + Android: function() { + var checkA = /Android/i.test(navigator.userAgent); + mobileType += checkA ? 'Android Phones.' : ''; + return checkA; + }, + BlackBerry: function() { + var checkB = /BlackBerry/i.test(navigator.userAgent); + mobileType += checkB ? 'BlackBerry.' : ''; + return checkB; + }, + iOS: function() { + var checkI = /iPhone|iPad|iPod/i.test(navigator.userAgent); + mobileType += checkI ? 'Apple iPhone, iPad or iPod.' : ''; + return checkI; + }, + any: function() { + return ( isMobile.Windows() || isMobile.Android() || isMobile.BlackBerry() || isMobile.iOS() ); + } + }, + checkMOBS = isMobile.any(); + +// protect phones, older / low end devices +if (document.body.offsetWidth < 1200 || checkMOBS) { + var explain = ''; + explain += checkMOBS && mobileType !== '' ? ('For safety reasons, this page does not work with ' + mobileType) : ''; + explain += !checkMOBS && document.body.offsetWidth < 1200 && mobileType === '' ? 'For safety reasons this page does not work on your machine because it might be very old. In other cases the browser window size is not enough for the animation to work properly, so if that\'s the case, maximize the window, refresh and proceed with the tests.' : ''; + var warning = '
'; + warning +='

Warning!

'; + warning +='

This web page is only for high-end desktop computers.

'; + warning +='

We do not take any responsibility and we are not liable for any damage caused through use of this website, be it indirect, special, incidental or consequential damages to your devices.

'; + warning +='

'+explain+'

'; + warning +='
'; + document.body.innerHTML = warning; + throw new Error('This page is only for high-end desktop computers. ' + explain); +} + +// the variables +var infoContainer = document.getElementById('info'); +var container = document.getElementById('container'); +var tws = []; + +for (var i=0; i<21; i++){ + container.innerHTML += + '
' + +'
' + +'
' + +'
' + +'
' + +'
' + +'
' + +'
' + +'
' + +'
' + +'
' + +'
' + +'
' + +'
' + +'
' + +'
' + +'
' +} + +var collection = document.getElementsByClassName('cube'); +var lastIdx = collection.length-1; + +function complete(){ + infoContainer.style.display = 'block'; + container.style.display = 'none'; +} + +var engine = document.getElementById('kute'), + fromCSS = { rotate3d: [ 0, 0,0 ], perspective:600 }, + fromMX = { transform: { rotate3d: [ 0, 0,0 ], perspective:600 }}, + toCSS = { rotate3d: [ 360,0,0 ], perspective:600 }, + toMX = { transform: { rotate3d: [ 0,360,0 ], perspective:600 }}, + ops = { duration: 2000, repeat: 5 } + +// since our engines don't do sync, we make it our own here +if (!engine.src.includes('extra')) { + [].slice.call(collection).map((el,i) => { i===lastIdx && (ops.onComplete = complete); tws.push ( KUTE.fromTo(el,fromCSS,toCSS,ops) ) }) +} +if (engine.src.includes('extra')) { + [].slice.call(collection).map((el,i) => { i===lastIdx && (ops.onComplete = complete); tws.push ( KUTE.fromTo(el,fromMX,toMX,ops) ) }) +} + + +function startTest(){ + infoContainer.style.display = 'none'; + container.style.display = 'block' + + tws.length && !tws[0].playing && startKUTE(); +} + + +function startKUTE() { + var now = window.performance.now(), count = tws.length; + tws.forEach((t) => t.start(now)); +} + +// the start button handle +document.getElementById('start').addEventListener('click', startTest, false); diff --git a/assets/js/perf.js b/assets/js/perf.js new file mode 100644 index 0000000..af75360 --- /dev/null +++ b/assets/js/perf.js @@ -0,0 +1,213 @@ +// testing grounds +"use strict"; + +var mobileType = '', + isMobile = { + Windows: function() { + var checkW = /IEMobile|Windows Mobile/i.test(navigator.userAgent); + mobileType += checkW ? 'Windows Phones.' : ''; + return checkW; + }, + Android: function() { + var checkA = /Android/i.test(navigator.userAgent); + mobileType += checkA ? 'Android Phones.' : ''; + return checkA; + }, + BlackBerry: function() { + var checkB = /BlackBerry/i.test(navigator.userAgent); + mobileType += checkB ? 'BlackBerry.' : ''; + return checkB; + }, + iOS: function() { + var checkI = /iPhone|iPad|iPod/i.test(navigator.userAgent); + mobileType += checkI ? 'Apple iPhone, iPad or iPod.' : ''; + return checkI; + }, + any: function() { + return ( isMobile.Windows() || isMobile.Android() || isMobile.BlackBerry() || isMobile.iOS() ); + } + }, + checkMOBS = isMobile.any(); + +// protect phones, older / low end devices +if (document.body.offsetWidth < 1200 || checkMOBS) { + var explain = ''; + explain += checkMOBS && mobileType !== '' ? ('For safety reasons, this page does not work with ' + mobileType) : ''; + explain += !checkMOBS && document.body.offsetWidth < 1200 && mobileType === '' ? 'For safety reasons this page does not work on your machine because it might be very old. In other cases the browser window size is not enough for the animation to work properly, so if that\'s the case, maximize the window, refresh and proceed with the tests.' : ''; + var warning = '
'; + warning +='

Warning!

'; + warning +='

This web page is only for high-end desktop computers.

'; + warning +='

We do not take any responsibility and we are not liable for any damage caused through use of this website, be it indirect, special, incidental or consequential damages to your devices.

'; + warning +='

'+explain+'

'; + warning +='
'; + document.body.innerHTML = warning; + throw new Error('This page is only for high-end desktop computers. ' + explain); +} + +// generate a random number within a given range +function random(min, max) { + return Math.random() * (max - min) + min; +} + +// the variables +var container = document.getElementById('container'); +var tws = []; + +function complete(){ + document.getElementById('info').style.display = 'block'; + container.innerHTML = ''; + tws = []; + if (engine==='tween') { + stop() + } +} + +function updateLeft(obj){ + obj.div.style.left = obj.left +'px'; +} + +function updateTranslate(obj){ + obj.div.style.transform = 'translate3d('+ ((obj.x * 10) / 10 >>0) + 'px,0px,0px)'; +} + +function buildObjects(){ + var c = document.querySelector('[data-count]'), e = document.querySelector('[data-engine]'), r = document.querySelector('[data-repeat]'), + p = document.querySelector('[data-property]'), ct = c && document.querySelector('[data-count]').getAttribute('data-count'), + count = ct ? parseInt(ct) : null, + engine = e && document.querySelector('[data-engine]').getAttribute('data-engine') || null, + repeat = r && document.querySelector('[data-repeat]').getAttribute('data-repeat') || null, + property = p && document.querySelector('[data-property]').getAttribute('data-property') || null, + warning = document.createElement('DIV'); + + warning.className = 'text-warning padding lead'; + container.innerHTML = ''; + if (count && engine && property && repeat) { + if (engine === 'gsap') { + document.getElementById('info').style.display = 'none'; + } + + createTest(count,property,engine,repeat); + // since our engines don't do sync, we make it our own here + if (engine==='kute') { + document.getElementById('info').style.display = 'none'; + startKUTE(); + } + if (engine==='tween') { + document.getElementById('info').style.display = 'none'; + startTWEEN(); + } + } else { + + if (!count && !property && !repeat && !engine){ + warning.innerHTML = 'We are missing all the settings here.'; + } else { + warning.innerHTML = 'Now missing
'; + warning.innerHTML += !engine ? '- engine
' : ''; + warning.innerHTML += !property ? '- property
' : ''; + warning.innerHTML += !repeat ? '- repeat
' : ''; + warning.innerHTML += !count ? '- count
' : ''; + } + + container.appendChild(warning); + } +} + +function animate( time ) { + requestAnimationFrame( animate ); + TWEEN.update( time ); +} + +function stop(){ + cancelAnimationFrame(animate) +} + +function startKUTE() { + var now = window.performance.now(), count = tws.length; + for (var t =0; t'; + b.setAttribute('data-'+link.parentNode.parentNode.parentNode.id, link.id); + } + } +} \ No newline at end of file diff --git a/assets/js/prism.js b/assets/js/prism.js new file mode 100644 index 0000000..8d21c08 --- /dev/null +++ b/assets/js/prism.js @@ -0,0 +1,6 @@ +/* http://prismjs.com/download.html?themes=prism-okaidia&languages=markup+css+clike+javascript */ +var _self="undefined"!=typeof window?window:"undefined"!=typeof WorkerGlobalScope&&self instanceof WorkerGlobalScope?self:{},Prism=function(){var e=/\blang(?:uage)?-(?!\*)(\w+)\b/i,t=_self.Prism={util:{encode:function(e){return e instanceof n?new n(e.type,t.util.encode(e.content),e.alias):"Array"===t.util.type(e)?e.map(t.util.encode):e.replace(/&/g,"&").replace(/e.length)break e;if(!(d instanceof a)){u.lastIndex=0;var m=u.exec(d);if(m){c&&(f=m[1].length);var y=m.index-1+f,m=m[0].slice(f),v=m.length,k=y+v,b=d.slice(0,y+1),w=d.slice(k+1),P=[p,1];b&&P.push(b);var A=new a(i,g?t.tokenize(m,g):m,h);P.push(A),w&&P.push(w),Array.prototype.splice.apply(r,P)}}}}}return r},hooks:{all:{},add:function(e,n){var a=t.hooks.all;a[e]=a[e]||[],a[e].push(n)},run:function(e,n){var a=t.hooks.all[e];if(a&&a.length)for(var r,l=0;r=a[l++];)r(n)}}},n=t.Token=function(e,t,n){this.type=e,this.content=t,this.alias=n};if(n.stringify=function(e,a,r){if("string"==typeof e)return e;if("Array"===t.util.type(e))return e.map(function(t){return n.stringify(t,a,e)}).join("");var l={type:e.type,content:n.stringify(e.content,a,r),tag:"span",classes:["token",e.type],attributes:{},language:a,parent:r};if("comment"==l.type&&(l.attributes.spellcheck="true"),e.alias){var i="Array"===t.util.type(e.alias)?e.alias:[e.alias];Array.prototype.push.apply(l.classes,i)}t.hooks.run("wrap",l);var o="";for(var s in l.attributes)o+=(o?" ":"")+s+'="'+(l.attributes[s]||"")+'"';return"<"+l.tag+' class="'+l.classes.join(" ")+'" '+o+">"+l.content+""},!_self.document)return _self.addEventListener?(_self.addEventListener("message",function(e){var n=JSON.parse(e.data),a=n.language,r=n.code,l=n.immediateClose;_self.postMessage(t.highlight(r,t.languages[a],a)),l&&_self.close()},!1),_self.Prism):_self.Prism;var a=document.getElementsByTagName("script");return a=a[a.length-1],a&&(t.filename=a.src,document.addEventListener&&!a.hasAttribute("data-manual")&&document.addEventListener("DOMContentLoaded",t.highlightAll)),_self.Prism}();"undefined"!=typeof module&&module.exports&&(module.exports=Prism),"undefined"!=typeof global&&(global.Prism=Prism); +Prism.languages.markup={comment://,prolog:/<\?[\w\W]+?\?>/,doctype://,cdata://i,tag:{pattern:/<\/?[^\s>\/=.]+(?:\s+[^\s>\/=]+(?:=(?:("|')(?:\\\1|\\?(?!\1)[\w\W])*\1|[^\s'">=]+))?)*\s*\/?>/i,inside:{tag:{pattern:/^<\/?[^\s>\/]+/i,inside:{punctuation:/^<\/?/,namespace:/^[^\s>\/:]+:/}},"attr-value":{pattern:/=(?:('|")[\w\W]*?(\1)|[^\s>]+)/i,inside:{punctuation:/[=>"']/}},punctuation:/\/?>/,"attr-name":{pattern:/[^\s>\/]+/,inside:{namespace:/^[^\s>\/:]+:/}}}},entity:/&#?[\da-z]{1,8};/i},Prism.hooks.add("wrap",function(a){"entity"===a.type&&(a.attributes.title=a.content.replace(/&/,"&"))}),Prism.languages.xml=Prism.languages.markup,Prism.languages.html=Prism.languages.markup,Prism.languages.mathml=Prism.languages.markup,Prism.languages.svg=Prism.languages.markup; +Prism.languages.css={comment:/\/\*[\w\W]*?\*\//,atrule:{pattern:/@[\w-]+?.*?(;|(?=\s*\{))/i,inside:{rule:/@[\w-]+/}},url:/url\((?:(["'])(\\(?:\r\n|[\w\W])|(?!\1)[^\\\r\n])*\1|.*?)\)/i,selector:/[^\{\}\s][^\{\};]*?(?=\s*\{)/,string:/("|')(\\(?:\r\n|[\w\W])|(?!\1)[^\\\r\n])*\1/,property:/(\b|\B)[\w-]+(?=\s*:)/i,important:/\B!important\b/i,"function":/[-a-z0-9]+(?=\()/i,punctuation:/[(){};:]/},Prism.languages.css.atrule.inside.rest=Prism.util.clone(Prism.languages.css),Prism.languages.markup&&(Prism.languages.insertBefore("markup","tag",{style:{pattern:/()[\w\W]*?(?=<\/style>)/i,lookbehind:!0,inside:Prism.languages.css,alias:"language-css"}}),Prism.languages.insertBefore("inside","attr-value",{"style-attr":{pattern:/\s*style=("|').*?\1/i,inside:{"attr-name":{pattern:/^\s*style/i,inside:Prism.languages.markup.tag.inside},punctuation:/^\s*=\s*['"]|['"]\s*$/,"attr-value":{pattern:/.+/i,inside:Prism.languages.css}},alias:"language-css"}},Prism.languages.markup.tag)); +Prism.languages.clike={comment:[{pattern:/(^|[^\\])\/\*[\w\W]*?\*\//,lookbehind:!0},{pattern:/(^|[^\\:])\/\/.*/,lookbehind:!0}],string:/(["'])(\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/,"class-name":{pattern:/((?:\b(?:class|interface|extends|implements|trait|instanceof|new)\s+)|(?:catch\s+\())[a-z0-9_\.\\]+/i,lookbehind:!0,inside:{punctuation:/(\.|\\)/}},keyword:/\b(if|else|while|do|for|return|in|instanceof|function|new|try|throw|catch|finally|null|break|continue)\b/,"boolean":/\b(true|false)\b/,"function":/[a-z0-9_]+(?=\()/i,number:/\b-?(?:0x[\da-f]+|\d*\.?\d+(?:e[+-]?\d+)?)\b/i,operator:/--?|\+\+?|!=?=?|<=?|>=?|==?=?|&&?|\|\|?|\?|\*|\/|~|\^|%/,punctuation:/[{}[\];(),.:]/}; +Prism.languages.javascript=Prism.languages.extend("clike",{keyword:/\b(as|async|await|break|case|catch|class|const|continue|debugger|default|delete|do|else|enum|export|extends|false|finally|for|from|function|get|if|implements|import|in|instanceof|interface|let|new|null|of|package|private|protected|public|return|set|static|super|switch|this|throw|true|try|typeof|var|void|while|with|yield)\b/,number:/\b-?(0x[\dA-Fa-f]+|0b[01]+|0o[0-7]+|\d*\.?\d+([Ee][+-]?\d+)?|NaN|Infinity)\b/,"function":/[_$a-zA-Z\xA0-\uFFFF][_$a-zA-Z0-9\xA0-\uFFFF]*(?=\()/i}),Prism.languages.insertBefore("javascript","keyword",{regex:{pattern:/(^|[^/])\/(?!\/)(\[.+?]|\\.|[^/\\\r\n])+\/[gimyu]{0,5}(?=\s*($|[\r\n,.;})]))/,lookbehind:!0}}),Prism.languages.insertBefore("javascript","class-name",{"template-string":{pattern:/`(?:\\`|\\?[^`])*`/,inside:{interpolation:{pattern:/\$\{[^}]+\}/,inside:{"interpolation-punctuation":{pattern:/^\$\{|\}$/,alias:"punctuation"},rest:Prism.languages.javascript}},string:/[\s\S]+/}}}),Prism.languages.markup&&Prism.languages.insertBefore("markup","tag",{script:{pattern:/()[\w\W]*?(?=<\/script>)/i,lookbehind:!0,inside:Prism.languages.javascript,alias:"language-javascript"}}),Prism.languages.js=Prism.languages.javascript; diff --git a/assets/js/progress.js b/assets/js/progress.js new file mode 100644 index 0000000..36601fc --- /dev/null +++ b/assets/js/progress.js @@ -0,0 +1,16 @@ + +// invalidate for unsupported browsers +var isIE = new RegExp("MSIE ([0-9]{1,}[\.0-9]{0,})").exec(navigator.userAgent) !== null ? parseFloat( RegExp.$1 ) : false; +if (isIE&&isIE<9) { (function(){return; }()) } // return if SVG API is not supported + +// the range slider +var rangeSlider = document.querySelector('input[type="range"'); + +// basic morph, only fromTo and allFromTo should work +var morphTween = KUTE.fromTo('#rectangle', { path: '#rectangle' }, { path: '#star' }, { duration: 2500, /*repeat: Infinity, yoyo: true,*/ } ); + +var progressBar = new KUTE.ProgressBar(rangeSlider,morphTween) + +document.getElementById('rectangle').addEventListener('click',function(){ + !morphTween.playing && morphTween.start() +}) \ No newline at end of file diff --git a/assets/js/sampleComponent.js b/assets/js/sampleComponent.js new file mode 100644 index 0000000..c0eda27 --- /dev/null +++ b/assets/js/sampleComponent.js @@ -0,0 +1,82 @@ +// Component Util +// returns browser prefix +function getPrefix() { + const prefixes = ['Moz', 'moz', 'Webkit', 'webkit', 'O', 'o', 'Ms', 'ms']; + + let thePrefix; + for (let i = 0, pfl = prefixes.length; i < pfl; i++) { + if (`${prefixes[i]}Transform` in document.body.style) { thePrefix = prefixes[i]; break; } + } + return thePrefix; +} + +// returns prefixed property | property +function trueProperty(property) { + const prefixRequired = (!(property in document.body.style)) ? true : false; // is prefix required for property | prefix + + const prefix = getPrefix(); + return prefixRequired ? prefix + (property.charAt(0).toUpperCase() + property.slice(1)) : property; +} + +// some old browsers like to use preffixed properties +var transformProperty = trueProperty('transform'); + +// value 0 means that browser doesn't support any transform +// value 1 means that browser supports at least 2D transforms +var browserVersion = transformProperty in document.body.style ? 1 : 0 + +// Component usual imports +var numbers = KUTE.Interpolate.numbers; +var getInlineStyle = KUTE.Process.getInlineStyle; +var scope = window._KUTE; + +// Component Functions +function getMove(tweenProp,value){ + var currentTransform = getInlineStyle(this.element); + var left = this.element.style.left; + var top = this.element.style.top; + var x = browserVersion && currentTransform.translate ? currentTransform.translate[0] + : isFinite(left*1) ? left + : defaultValues.move[0]; + var y = browserVersion && currentTransform.translate ? currentTransform.translate[1] + : isFinite(top*1) ? top + : defaultValues.move[1]; + return [x,y] +} +function prepareMove(tweenProp,value){ + var x = isFinite(value*1) ? parseInt(value) : parseInt(value[0]) || 0; + var y = parseInt(value[1]) || 0; + return [ x, y ] +} +function onStartMove(tweenProp,value){ + if (!scope[tweenProp] && this.valuesEnd[tweenProp]) { + if (browserVersion){ + scope[tweenProp] = function (elem, a, b, v) { + elem.style[transformProperty] = 'translate('+numbers(a[0],b[0],v)+'px,'+numbers(a[1],b[1],v)+'px)'; + }; + } else { + scope[tweenProp] = function (elem, a, b, v) { + if (a[0]||b[0]) { + elem.style.left = numbers(a[0],b[0],v)+'px'; + } + if (a[1]||b[1]) { + elem.style.top = numbers(a[1],b[1],v)+'px'; + } + }; + } + } +} + +// the component object +var crossBrowserMoveOptions = { + component: 'crossBrowserMove', + property: 'move', + defaultValue: [0,0], + Interpolate: numbers, + functions: { + prepareStart: getMove, + prepareProperty: prepareMove, + onStart: onStartMove + } +}; +KUTE.Components.crossBrowserMove = new KUTE.Animation(crossBrowserMoveOptions) \ No newline at end of file diff --git a/assets/js/scripts.js b/assets/js/scripts.js new file mode 100644 index 0000000..c2d13c3 --- /dev/null +++ b/assets/js/scripts.js @@ -0,0 +1,31 @@ +// common demo JS +function getRandomInt(min, max) { + return Math.floor(Math.random() * (max - min + 1)) + min; +} + +// toggles utility +var toggles = document.querySelectorAll('[data-function="toggle"]'); + +function closeToggles(el){ + el.classList.remove('open'); +} + +function classToggles(el){ + el.classList.toggle('open'); +} + +document.addEventListener('click', function(e){ + if (e && !e.target.classList) {return;} + + var target = e.target.parentNode.tagName === 'LI' || e.target.parentNode.classList && e.target.parentNode.classList.contains('btn-group') ? e.target : e.target.parentNode, + parent = target.parentNode; + for (var i = 0, l = toggles.length; i') + ''; +var charsObject = heading.getElementsByTagName('SPAN'), l = charsObject.length; + + +// built the tween objects +var tps = KUTE.allFromTo(charsObject, + { fontSize:50, letterSpacing: 0, color: '#333'}, + { fontSize:80, letterSpacing: 10, color: 'red'}, + { offset: 75, duration: 250, repeat: 1, yoyo:true, repeatDelay: 150, easing: 'easingCubicOut'}); + + +button.addEventListener('click', function(e){ + e.preventDefault(); + if ( !tps.playing() ) { + tps.start(); + } +},false); +/* TEXT PROPERTIES EXAMPLE */ \ No newline at end of file diff --git a/assets/js/textWrite.js b/assets/js/textWrite.js new file mode 100644 index 0000000..2ceb616 --- /dev/null +++ b/assets/js/textWrite.js @@ -0,0 +1,61 @@ +// number increment +var numText = document.getElementById('numText'), + numBtn = document.getElementById('numBtn'), + numTween = KUTE.to(numText, {number: 1550}, {duration: 3000, easing: 'easingCubicOut'}); + numBtn.addEventListener('click', function(){ + + if (!numTween.playing) { + if (numText.innerHTML === '1550') { numTween.valuesEnd['number'] = 0; } else { numTween.valuesEnd['number'] = 1550; } + numTween.start(); + } +}, false); + +// write text +var headText = document.getElementById('headText'), + headBtn = document.getElementById('headBtn'), + initText = headText.innerHTML, + whichTw = false, + nextText = "This is a super simple write text demo.", + headTween = KUTE.to(headText, {text: nextText}, {textChars: 'alpha', duration: 5000, easing: 'easingBounceOut'}), + headTween1 = KUTE.to(headText, {text: initText}, {textChars: 'alpha', duration: 5000, easing: 'easingBounceOut'}); +headBtn.addEventListener('click', function(){ + !whichTw && !headTween.playing && !headTween1.playing && (headTween.start(), whichTw = !whichTw); + whichTw && !headTween.playing && !headTween1.playing && (headTween1.start(), whichTw = !whichTw); +}, false); + + +// improved write text +var textTweenBTN = document.getElementById('textExampleBtn'); +var textTarget = document.getElementById('textExample'); +var textOriginal = textTarget.innerHTML; +var anotherText = 'This text has a link to homepage inside.'; +var options = {duration: 'auto', textChars: 'alphanumeric'} + +textTweenBTN.addEventListener('click', function(){ + var newContent = textTarget.innerHTML === textOriginal ? anotherText : textOriginal; + !textTarget.playing && KUTE.Util.createTextTweens(textTarget,newContent,options).start() +}) + + +// combo wombo +var comText = document.getElementById('comText'), + comNum = document.getElementById('comNum'), + comBtn = document.getElementById('comBtn'), + comTween11 = KUTE.to(comNum, {number: 2500}, {duration: 2000, easing: 'easingCubicOut'}), + comTween12 = KUTE.to(comText, {text: "People following on Github."}, { textChars: 'symbols', duration: 3000, easing: 'easingCubicInOut'}), + comTween21 = KUTE.to(comNum, {number: 7500}, {delay: 3000, duration: 2000, easing: 'easingCubicInOut'}), + comTween22 = KUTE.to(comText, {text: "More crazy tricks coming soon."}, {textChars: 'all', delay: 2000, duration: 3000, easing: 'easingCubicInOut'}); + +try{ + comTween11.chain(comTween21); + comTween12.chain(comTween22); +}catch(e){ + console.error(`${e} TweenBase doesn't support chain method`) +} + +comBtn.addEventListener('click', function(){ + if (!comTween11.playing && !comTween21.playing && !comTween12.playing && !comTween22.playing) { + comTween11.start(); + comTween12.start(); + } +}, false); \ No newline at end of file diff --git a/assets/js/transformFunctions.js b/assets/js/transformFunctions.js new file mode 100644 index 0000000..1b3e2ac --- /dev/null +++ b/assets/js/transformFunctions.js @@ -0,0 +1,136 @@ + +/* TRANSFORMS EXAMPLES */ +var translateExamples = document.getElementById('translate-examples'), + translateBtn = translateExamples.querySelector('.btn'), + tr2d = translateExamples.getElementsByTagName('div')[0], + trx = translateExamples.getElementsByTagName('div')[1], + trry = translateExamples.getElementsByTagName('div')[2], + trz = translateExamples.getElementsByTagName('div')[3], + tr2dTween = KUTE.to(tr2d, {translate:[170,170]}, {easing:'easingCubicOut', yoyo:true, repeat: 1, duration:1500}), + trxTween = KUTE.to(trx, {translateX:-170}, {easing:'easingCubicOut', yoyo:true, repeat: 1, duration:1500}), + trryTween = KUTE.to(trry, {translate3d:[0,170,0]}, {easing:'easingCubicOut', yoyo:true, repeat: 1, duration:1500}), + trzTween = KUTE.to(trz, {perspective:200, translate3d:[0,0,-100]}, {easing:'easingCubicOut', yoyo:true, repeat: 1, duration:1500}); + +translateBtn.addEventListener('click', function(){ + !tr2dTween.playing && tr2dTween.start(); + !trxTween.playing && trxTween.start(); + !trryTween.playing && trryTween.start(); + !trzTween.playing && trzTween.start(); +}, false); + +var rotExamples = document.getElementById('rotExamples'), + rotBtn = rotExamples.querySelector('.btn'), + r2d = rotExamples.querySelectorAll('div')[0], + rx = rotExamples.querySelectorAll('div')[1], + ry = rotExamples.querySelectorAll('div')[2], + rz = rotExamples.querySelectorAll('div')[3], + r2dTween = KUTE.to(r2d, {rotate:-720}, {easing:'easingCircularInOut', yoyo:true, repeat: 1, duration:1500}), + rxTween = KUTE.to(rx, {rotateX:180}, {easing:'linear', yoyo:true, repeat: 1, duration:1500}), + ryTween = KUTE.to(ry, {perspective:200, rotate3d:[0,-180,0],scale:1.2}, {easing:'easingCubicInOut', yoyo:true, repeat: 1, duration:1500}), + rzTween = KUTE.to(rz, {rotateZ:360}, {easing:'easingBackOut', yoyo:true, repeat: 1, duration:1500}); + +rotBtn.addEventListener('click', function(){ + !r2dTween.playing && r2dTween.start(); + !rxTween.playing && rxTween.start(); + !ryTween.playing && ryTween.start(); + !rzTween.playing && rzTween.start(); +}, false); + +var skewExamples = document.getElementById('skewExamples'), + skewBtn = skewExamples.querySelector('.btn'), + sx = skewExamples.querySelectorAll('div')[0], + sy = skewExamples.querySelectorAll('div')[1], + sxTween = KUTE.to(sx, {skewX:-25}, {easing:'easingCircularInOut', yoyo:true, repeat: 1, duration:1500}), + syTween = KUTE.to(sy, {skew:[0,25]}, {easing:'easingCircularInOut', yoyo:true, repeat: 1, duration:1500}); +skewBtn.addEventListener('click', function(){ + !sxTween.playing && sxTween.start(); + !syTween.playing && syTween.start(); +}, false); + +var mixTransforms = document.getElementById('mixTransforms'), + mixBtn = mixTransforms.querySelector('.btn'), + mt1 = mixTransforms.querySelectorAll('div')[0], + mt2 = mixTransforms.querySelectorAll('div')[1], + // pp = KUTE.Util.trueProperty('perspective'), + // tfp = KUTE.Util.trueProperty('transform'), + // tfo = KUTE.Util.trueProperty('transformOrigin'), + mt1Tween = KUTE.to(mt1, {perspective:200,translateX:300,rotateX:360,rotateY:15,rotateZ:5}, { easing:'easingCubicInOut', yoyo:true, repeat: 1, duration:1500}), + mt2Tween = KUTE.to(mt2, {translateX:300,rotateX:360,rotateY:15,rotateZ:5}, { easing:'easingCubicInOut', yoyo:true, repeat: 1, duration:1500}); + +mixBtn.addEventListener('click', function(){ + !mt1Tween.playing && mt1Tween.start(); + !mt2Tween.playing && mt2Tween.start(); +}, false); + +/* TRANSFORMS EXAMPLES */ + + +/* CHAINED TWEENS EXAMPLE */ +var chainedTweens = document.getElementById('chainedTweens'), + el1 = chainedTweens.querySelectorAll('.example-item')[0], + el2 = chainedTweens.querySelectorAll('.example-item')[1], + el3 = chainedTweens.querySelectorAll('.example-item')[2], + el4 = chainedTweens.querySelectorAll('.example-item')[3], + ctb = chainedTweens.querySelector('.btn'); + +// built the tween objects for element1 +var tween11 = KUTE.fromTo(el1, { perspective:400,translateX:0, rotateX: 0}, {perspective:400,translateX:150, rotateX: 25}, {duration: 2000}); +var tween12 = KUTE.fromTo(el1, { perspective:400,translateY:0, rotateY: 0}, {perspective:400,translateY:20, rotateY: 15}, {duration: 2000}); +var tween13 = KUTE.fromTo(el1, { perspective:400,translate3d:[150,20,0], rotate3d:[25,15,0]}, {perspective:400,translate3d:[0,0,0], rotate3d:[0,0,0]}, {duration: 3000}); + +// chain tweens +try { + tween11.chain(tween12); + tween12.chain(tween13); +} catch(e) { + console.warn(e+". TweenBase doesn\'t support chain method") +} + +// built the tween objects for element2 +var tween21 = KUTE.fromTo(el2, { perspective:400,translateX:0, translateY:0, rotateX: 0, rotateY:0 }, {perspective:400,translateX:150, translateY:0, rotateX: 25, rotateY:0}, {duration: 2000}); +var tween22 = KUTE.fromTo(el2, { perspective:400,translateX:150, translateY:0, rotateX: 25, rotateY: 0}, {perspective:400,translateX:150, translateY:20, rotateX: 25, rotateY: 15}, {duration: 2000}); +var tween23 = KUTE.fromTo(el2, { perspective:400,translate3d:[150,20,0], rotateX: 25, rotateY:15}, {perspective:400,translate3d:[0,0,0], rotateX: 0, rotateY:0}, {duration: 3000}); + +// chain tweens +try{ + tween21.chain(tween22); + tween22.chain(tween23); +}catch(e){ + console.warn(e+". TweenBase doesn\'t support chain method") +} + +// built the tween objects for element3 +var tween31 = KUTE.to(el3,{ perspective:400,translateX:150, rotateX:25}, {duration: 2000}); +var tween32 = KUTE.to(el3,{ perspective:400,translateX:150,translateY:20, rotateY:15}, {duration: 2000}); +var tween33 = KUTE.to(el3,{ perspective:400,translateX:0, translateY:0, rotateX: 0, rotateY:0}, {duration: 3000}); + +// chain tweens +try{ + tween31.chain(tween32); + tween32.chain(tween33); +}catch(e){ + console.warn(e+". TweenBase doesn\'t support chain method") +} + +// built the tween objects for element4 +var tween41 = KUTE.to(el4,{ perspective:400, translate3d:[150,0,0], rotate3d: [25,0,0]}, {duration: 2000}); +var tween42 = KUTE.to(el4,{ perspective:400, translate3d:[150,20,0], rotate3d:[25,15,0]}, {duration: 2000}); +var tween43 = KUTE.to(el4,{ perspective:400, translate3d:[0,0,0], rotate3d: [0,0,0]}, {duration: 3000}); + +// chain tweens +try{ + tween41.chain(tween42); + tween42.chain(tween43); +}catch(e){ + console.warn(e+". TweenBase doesn\'t support chain method") +} + + +ctb.addEventListener('click',function(e){ + e.preventDefault(); + !tween11.playing && !tween12.playing && !tween13.playing && tween11.start(); + !tween21.playing && !tween22.playing && !tween23.playing && tween21.start(); + !tween31.playing && !tween32.playing && !tween33.playing && tween31.start(); + !tween41.playing && !tween42.playing && !tween43.playing && tween41.start(); +},false); +/* CHAINED TWEENS EXAMPLE */ \ No newline at end of file diff --git a/assets/js/transformMatrix.js b/assets/js/transformMatrix.js new file mode 100644 index 0000000..77afc5b --- /dev/null +++ b/assets/js/transformMatrix.js @@ -0,0 +1,92 @@ + +/* MATRIX TRANSFORMS EXAMPLES */ +/* using parent perspective */ +var matrixExamples = document.getElementById('matrix-examples'), + matrixBtn = matrixExamples.querySelector('.btn'), + mx1 = matrixExamples.getElementsByTagName('div')[0], + mx2 = matrixExamples.getElementsByTagName('div')[1], + mx3 = matrixExamples.getElementsByTagName('div')[2], + mx4 = matrixExamples.getElementsByTagName('div')[3], + mx1Tween = KUTE.to(mx1, {transform: { translate3d:[-50,-50,-50]} }, {easing:'easingCubicOut', yoyo:true, repeat: 1, duration:1500}), + mx2Tween = KUTE.to(mx2, {transform: { perspective: 100, translate3d:[-50,-50,-50], rotateX:180 } }, {easing:'easingCubicOut', yoyo:true, repeat: 1, duration:1500}), + mx3Tween = KUTE.to(mx3, {transform: { translate3d:[-50,-50,-50], skew:[-15,-15] } }, { easing:'easingCubicOut', yoyo:true, repeat: 1, duration:1500}), // matrix(1, 45, 45, 1, 0, -170) + mx4Tween = KUTE.to(mx4, {transform: { translate3d:[-50,-50,-50], rotate3d:[0,-360,0], scaleX: 0.5 } }, { easing:'easingCubicOut', yoyo:true, repeat: 1, duration:1500}); + +matrixBtn.addEventListener('click', function(){ + !mx1Tween.playing && mx1Tween.start(); + !mx2Tween.playing && mx2Tween.start(); + !mx3Tween.playing && mx3Tween.start(); + !mx4Tween.playing && mx4Tween.start(); +}, false); + + +/* CHAINED TWEENS EXAMPLE */ +var chainedTweens = document.getElementById('chainedTweens'), + el1 = chainedTweens.querySelectorAll('.example-item')[0], + el2 = chainedTweens.querySelectorAll('.example-item')[1], + el3 = chainedTweens.querySelectorAll('.example-item')[2], + el4 = chainedTweens.querySelectorAll('.example-item')[3], + ctb = chainedTweens.querySelector('.btn'); + +// built the tween objects for element1 +var tween11 = KUTE.fromTo(el1, { transform: {perspective:400,translateX:0, rotateX: 0}}, {transform: {perspective:400,translateX:150, rotateX: 25}}, {duration: 2000}); +var tween12 = KUTE.fromTo(el1, { transform: {perspective:400,translateY:0, rotateY: 0}}, {transform: {perspective:400,translateY:20, rotateY: 15}}, {duration: 2000}); +var tween13 = KUTE.fromTo(el1, { transform: {perspective:400,translate3d:[150,20,0], rotate3d:[25,15,0]}}, {transform: {perspective:400,translate3d:[0,0,0], rotate3d:[0,0,0]}}, {duration: 3000}); + +// chain tweens +try { + tween11.chain(tween12); + tween12.chain(tween13); +} catch(e) { + console.warn(e+". TweenBase doesn\'t support chain method") +} + +// built the tween objects for element2 +var tween21 = KUTE.fromTo(el2, { transform: {perspective:400,translateX:0, translateY:0, rotateX: 0, rotateY:0 }}, {transform: {perspective:400,translateX:150, translateY:0, rotateX: 25, rotateY:0}}, {duration: 2000}); +var tween22 = KUTE.fromTo(el2, { transform: {perspective:400,translateX:150, translateY:0, rotateX: 25, rotateY: 0}}, {transform: {perspective:400,translateX:150, translateY:20, rotateX: 25, rotateY: 15}}, {duration: 2000}); +var tween23 = KUTE.fromTo(el2, { transform: {perspective:400,translate3d:[150,20,0], rotateX: 25, rotateY:15}}, {transform: {perspective:400,translate3d:[0,0,0], rotateX: 0, rotateY:0}}, {duration: 3000}); + +// chain tweens +try{ + tween21.chain(tween22); + tween22.chain(tween23); +}catch(e){ + console.warn(e+". TweenBase doesn\'t support chain method") +} + +// built the tween objects for element3 +var tween31 = KUTE.to(el3,{ transform: {perspective:400,translateX:150, rotateX:25}}, {duration: 2000}); +var tween32 = KUTE.to(el3,{ transform: {perspective:400,translateX:150,translateY:20, rotateY:15}}, {duration: 2000}); +var tween33 = KUTE.to(el3,{ transform: {perspective:400,translateX:0, translateY:0, rotateX: 0, rotateY:0}}, {duration: 3000}); + +// chain tweens +try{ + tween31.chain(tween32); + tween32.chain(tween33); +}catch(e){ + console.warn(e+". TweenBase doesn\'t support chain method") +} + +// built the tween objects for element4 +var tween41 = KUTE.to(el4,{ transform: {perspective:400, translate3d:[150,0,0], rotate3d: [25,0,0]}}, {duration: 2000}); +var tween42 = KUTE.to(el4,{ transform: {perspective:400, translate3d:[150,20,0], rotate3d:[25,15,0]}}, {duration: 2000}); +var tween43 = KUTE.to(el4,{ transform: {perspective:400, translate3d:[0,0,0], rotate3d: [0,0,0]}}, {duration: 3000}); + +// chain tweens +try{ + tween41.chain(tween42); + tween42.chain(tween43); +}catch(e){ + console.warn(e+". TweenBase doesn\'t support chain method") +} + + +ctb.addEventListener('click',function(e){ + e.preventDefault(); + !tween11.playing && !tween12.playing && !tween13.playing && tween11.start(); + !tween21.playing && !tween22.playing && !tween23.playing && tween21.start(); + !tween31.playing && !tween32.playing && !tween33.playing && tween31.start(); + !tween41.playing && !tween42.playing && !tween43.playing && tween41.start(); +},false); +/* CHAINED TWEENS EXAMPLE */ + diff --git a/assets/js/tween.min.js b/assets/js/tween.min.js new file mode 100644 index 0000000..a0fb5e7 --- /dev/null +++ b/assets/js/tween.min.js @@ -0,0 +1,2 @@ +// tween.js MIT License +(function(global,factory){typeof exports==='object'&&typeof module!=='undefined'?module.exports=factory():typeof define==='function'&&define.amd?define(factory):(global.TWEEN=factory())}(this,(function(){'use strict';var version='18.4.2';var _Group=function(){this._tweens={};this._tweensAddedDuringUpdate={}};_Group.prototype={getAll:function(){return Object.keys(this._tweens).map(function(tweenId){return this._tweens[tweenId]}.bind(this))},removeAll:function(){this._tweens={}},add:function(tween){this._tweens[tween.getId()]=tween;this._tweensAddedDuringUpdate[tween.getId()]=tween},remove:function(tween){delete this._tweens[tween.getId()];delete this._tweensAddedDuringUpdate[tween.getId()]},update:function(time,preserve){var tweenIds=Object.keys(this._tweens);if(tweenIds.length===0){return false}time=time!==undefined?time:TWEEN.now();while(tweenIds.length>0){this._tweensAddedDuringUpdate={};for(var i=0;i1)?1:elapsed;value=this._easingFunction(elapsed);for(property in this._valuesEnd){if(this._valuesStart[property]===undefined){continue}var start=this._valuesStart[property]||0;var end=this._valuesEnd[property];if(end instanceof Array){this._object[property]=this._interpolationFunction(end,value)}else{if(typeof(end)==='string'){if(end.charAt(0)==='+'||end.charAt(0)==='-'){end=start+parseFloat(end)}else{end=parseFloat(end)}}if(typeof(end)==='number'){this._object[property]=start+(end-start)*value}}}if(this._onUpdateCallback!==null){this._onUpdateCallback(this._object,elapsed)}if(elapsed===1){if(this._repeat>0){if(isFinite(this._repeat)){this._repeat-=1}for(property in this._valuesStartRepeat){if(typeof(this._valuesEnd[property])==='string'){this._valuesStartRepeat[property]=this._valuesStartRepeat[property]+parseFloat(this._valuesEnd[property])}if(this._yoyo){var tmp=this._valuesStartRepeat[property];this._valuesStartRepeat[property]=this._valuesEnd[property];this._valuesEnd[property]=tmp}this._valuesStart[property]=this._valuesStartRepeat[property]}if(this._yoyo){this._reversed=!this._reversed}if(this._repeatDelayTime!==undefined){this._startTime=time+this._repeatDelayTime}else{this._startTime=time+this._delayTime}if(this._onRepeatCallback!==null){this._onRepeatCallback(this._object)}return true}else{if(this._onCompleteCallback!==null){this._onCompleteCallback(this._object)}for(var i=0,numChainedTweens=this._chainedTweens.length;i1){return fn(v[m],v[m-1],m-f)}return fn(v[i],v[i+1>m?m:i+1],f-i)},Bezier:function(v,k){var b=0;var n=v.length-1;var pw=Math.pow;var bn=TWEEN.Interpolation.Utils.Bernstein;for(var i=0;i<=n;i+=1){b+=pw(1-k,n-i)*pw(k,i)*v[i]*bn(n,i)}return b},CatmullRom:function(v,k){var m=v.length-1;var f=m*k;var i=Math.floor(f);var fn=TWEEN.Interpolation.Utils.CatmullRom;if(v[0]===v[m]){if(k<0){i=Math.floor(f=m*(1+k))}return fn(v[(i-1+m)%m],v[i],v[(i+1)%m],v[(i+2)%m],f-i)}else{if(k<0){return v[0]-(fn(v[0],v[0],v[1],v[1],-f)-v[0])}if(k>1){return v[m]-(fn(v[m],v[m],v[m-1],v[m-1],f-m)-v[m])}return fn(v[i?i-1:0],v[i],v[m1;i-=1){s*=i}a[n]=s;return s}})(),CatmullRom:function(p0,p1,p2,p3,t){var v0=(p2-p0)*0.5;var v1=(p3-p1)*0.5;var t2=t*t;var t3=t*t2;return(2*p1-2*p2+v0+v1)*t3+(-3*p1+3*p2-2*v0-v1)*t2+v0*t+p1}}};TWEEN.version=version;return TWEEN}))); \ No newline at end of file diff --git a/backgroundPosition.html b/backgroundPosition.html new file mode 100644 index 0000000..fb99057 --- /dev/null +++ b/backgroundPosition.html @@ -0,0 +1,157 @@ + + + + + + + + + + + + + + KUTE.js Background Position + + + + + + + + + + + + + + +
+ + + +
+

Background Position

+

The component that animates the CSS property controling the background-position property of a target element.

+
+ +
+
+
+
+

Overview

+

Animate the position of the background image, simple and effective.

+
+
+

The KUTE.js Background Position component enables animation for the CSS backgroundPosition property on most browsers.

+

It can handle an entire set of input values [x,y], '0% 50%' and even 'left center'. The component always updates the values of the position + property via % suffix for simplicity reasons and even if px or any other unit is supported.

+

While we might not have much use for this kind of animation, some older browsers will love to show you something if that is the case.

+
+
+
+
+ +
+ +

Example

+ +

Here a couple of possible tween objects:

+ +
// provide the exact values for interpolation
+let bgPosTween = KUTE.to('selector1',{backgroundPosition:[0,50]}).start();
+
+// provide the coordinates
+let bgPosTween = KUTE.to('selector1',{backgroundPosition:"0% 50%"}).start();
+
+// or provide the position names
+let bgPosTween = KUTE.to('selector1',{backgroundPosition:"left center"}).start();
+
+ +
+
+ +
+ Start +
+
+ +

Notes

+
    +
  • Unfortunatelly this property also has no access at the sub-pixel level, animations are as good as it gets, despite the fact that the % suffix is used.
  • +
  • There are thankfully replacements for this forgotten property that strangelly supports CSS3 transitions, you can simply use all kinds of SVG masks and filters + and the HTML Attributes component for much more animation potential and supreme animation quality.
  • +
  • This component is bundled with the demo/src/kute-extra.js file.
  • + +
+
+ + + + +
+ + + + + + + + + + + + + + + diff --git a/borderRadius.html b/borderRadius.html new file mode 100644 index 0000000..4b962e2 --- /dev/null +++ b/borderRadius.html @@ -0,0 +1,172 @@ + + + + + + + + + + + + + + + KUTE.js Border Radius + + + + + + + + + + + + + +
+ + + +
+

Border Radius

+

The component that animates the CSS properties that control the radius of the corners of a target element.

+
+ +
+
+
+
+

Overview

+

Animate the radius for all corners or a specific one for a target element.

+
+
+

The KUTE.js Border Radius component provides support for the CSS3 border-radius property and all corner variations.

+

The component accepts any measurement unit but the best results in terms of visual presentation are when you use %, em or any other + subpixel units.

+

Even if you don't provide any unit at all, the component will work it out with px.

+

For a quick reference, here are the properties supported:

+
+
+
+
+ +
+ +

Border Radius Properties

+
    +
  • borderRadius allows you to animate the border-radius on all corners for a given element.
  • +
  • borderTopLeftRadius allows you to animate the border-top-left-radius for a given element.
  • +
  • borderTopRightRadius allows you to animate the border-top-right-radius for a given element.
  • +
  • borderBottomLeftRadius allows you to animate the border-bottom-left-radiusfor a given element.
  • +
  • borderBottomRightRadius allows you to animate the border-bottom-right-radiusfor a given element.
  • +
+ + +

Examples

+

OK let's have a look at some sample tween objects and a quick demo:

+ +
KUTE.to('selector1',{borderRadius:'100%'}).start();
+KUTE.to('selector2',{borderTopLeftRadius:'100%'}).start();
+KUTE.to('selector3',{borderTopRightRadius:'5em'}).start();
+KUTE.to('selector4',{borderBottomLeftRadius:50}).start();
+KUTE.to('selector5',{borderBottomRightRadius:'20px'}).start();
+
+ +
+
ALL
+
TL
+
TR
+
BL
+
BR
+ +
+ Start +
+
+ +

Notes

+
    +
  • A quick reminder here is that the component does not support radius shorthand values, EG border-radius: 50px 20px.
  • +
  • The component does not use vendor property preffixes anymore, the major browsers don't need for quite some time now. If you want to support + legacy browsers, you still have the utilities available.
  • +
  • Early implementations from any browser that have been deprecated are also not supported.
  • +
  • This component is bundled with demo/src/kute-extra.js file.
  • + +
+
+ + + + + +
+ + + + + + + + + + + + + + + + + diff --git a/boxModel.html b/boxModel.html new file mode 100644 index 0000000..fb43caf --- /dev/null +++ b/boxModel.html @@ -0,0 +1,179 @@ + + + + + + + + + + + + + + + KUTE.js Box Model + + + + + + + + + + + + + +
+ + + + +
+

Box Model

+

The component that animates most of the CSS box model properties of a target element on all browsers.

+
+ +
+
+
+
+

Overview

+

Animate the width, height, borderWidth or spacing for a target element on all browsers.

+
+
+

The KUTE.js Box Model component provides support for all box-model properties and all their variations.

+

Unlike other components, this one only works with px measurement unit, simply because these properties have no control at subpixel level. This means that even if you use % + as suffix, the computed values are still pixel based in all browsers.

+

Because modern browsers shine with transform animations and box model properties generally come with performance penalties and other animation + jank, they can be used as fallback for legacy browsers, for your very special clients of course.

+
+
+
+
+ +
+ +

Box Model Properties

+
    +
  • left, top, right and bottom are position based properties for movement on + vertical and / or horizontal axis. These properties require that the element to animate uses position: absolute/relative styling as well as it's parent element requires + position:relative. These properties can be used as fallback for browsers with no support for translate properties such as IE8.
  • +
  • width, height, minWidth, minHeight, maxWidth, + maxHeight are properties that allow you to animate the size of an element on horizontal and / or vertical axis. These properties can be used on images as fallback for + scale on IE8 again, as well as for other purposes.
  • +
  • padding, margin, paddingTop, paddingBottom, paddingLeft, + paddingRight, marginTop, marginBottom, marginLeft and + marginRight are properties that allow you to animate the spacing of an element inside (via padding) and outside (via margin).
  • +
  • borderWidth, borderTopWidth, borderRightWidth, borderBottomWidth are + borderLeftWidth are properties that allow you to animate the border of an element either on all sides at once or each side separatelly.
  • +
  • outlineWidth property allows you to animate the outline-width of an element.
  • +
+

The properties marked with different color, namely left, top, width and height are part of a lighter + version of the component called baseBoxModel.js, since they're the most used and probably most needed in just about every KUTE.js distribution.

+ +

Examples

+

OK let's have a look at some sample tween objects and a quick demo:

+ +
let tween1 = KUTE.to('selector1',{width:200})
+let tween2 = KUTE.to('selector1',{height:300})
+let tween3 = KUTE.to('selector1',{left:250})
+let tween4 = KUTE.to('selector1',{top:100})
+let tween5 = KUTE.to('selector1',{marginTop:200})
+let tween6 = KUTE.to('selector1',{marginBottom:50})
+let tween7 = KUTE.to('selector1',{padding:30})
+let tween8 = KUTE.to('selector1',{margin:'5px'})
+
+ +

We're gonna chain these tweens and start the animation.

+
+
BOX
 MODEL 
+ +
+ Start +
+
+ + +

Notes

+
    +
  • Shorthand notations such as margin: "20px 50px" or any other property are not supported.
  • +
  • Most box-model properties (except top, left, bottom and right) are layout modifiers and will not + produce the best visual experience mainly because they force re-paint on all page layout and they don't support animation under the pixel level.
  • +
  • The baseBoxModel component is featured in all distributions, while the full component is bundled with demo/src/kute-extra.js file.
  • +
+ +
+ + + + +
+ + + + + + + + + + + + + + + + diff --git a/clipProperty.html b/clipProperty.html new file mode 100644 index 0000000..fe6bf78 --- /dev/null +++ b/clipProperty.html @@ -0,0 +1,153 @@ + + + + + + + + + + + + + + KUTE.js Clip Property + + + + + + + + + + + + + +
+ + + +
+

Clip Property

+

The component that animates the CSS clip property of a target element on most browsers.

+
+ +
+
+
+
+

Overview

+

Animate the clip property of a target element when certain conditions are met.

+
+
+

The KUTE.js Clip Property component enables animation for the CSS clip property on most browsers.

+

What exactly does it do? Well you can animate the visible rectangular shape of an element that is set to position:absolute. + If conditions are not met (more conditions apply, see notes below), the component will update the target element, but the effect is not visible.

+

Generally you can set the CSS rule for this property like this clip: rect(top,right,bottom,left) which forms a rectangular masking shape above + a target element making parts of it invisible.

+

While the CSS clip property has been deprecated, it can still be used to emulate a type of scale/reveal animation for legacy browsers in certain cases.

+

This component is bundled with the demo/src/kute-extra.js file.

+
+
+
+
+ + +
+ +

Example

+

A possible tween object using the property:

+ +
KUTE.to('selector',{clip:[0,150,100,0]}).start();
+ +

And a quick example here could look like this:

+ +
+
+ +
+ Start +
+
+ +

Notes

+
    +
  • The component will produce no effect for target elements that have overflow:visible style rule.
  • +
  • Also target elements without position:absolute CSS rule will produce no effect.
  • +
  • This property will only work with px unit for the rectangular mask, which is unfortunate.
  • +
  • Don't stop here, there are thankfully replacements for this property, you can simply use all kinds of SVG masks and filters in combination + with the HTML Attributes component for much more animation potential and for no compromise on animation quality.
  • +
  • This component is bundled with the demo/src/kute-extra.js file.
  • +
+ +
+ + + + +
+ + + + + + + + + + + + + diff --git a/colorProperties.html b/colorProperties.html new file mode 100644 index 0000000..efea12f --- /dev/null +++ b/colorProperties.html @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + KUTE.js Color Properties + + + + + + + + + + + + + +
+ + + +
+

Color Properties

+

The component that animates CSS color properties for Element targets on most browsers.

+
+ +
+
+
+
+

Overview

+

Animate color properties for a target element and updates its CSS style via RGB.

+
+
+

The KUTE.js Color Properties component provides support for all color properties and all their variations on most browsers.

+

While with previous versions we used to have a keepHex option to always use the HEX format of the output color, modern browsers outright ignore the option and always + deliver colors in RGB format, probably for performance reasons.

+ +

The component supports input values such as HEX, RGB and RGBA for all color properties and most browsers should also work with + web safe colors, eg. color: 'red'.

+ +

For a quick reference, here are all the supported properties:

+
+
+
+
+ +
+ +

Supported Properties

+
    +
  • color allows you to animate the color for a target text element. Eg. color: '#ff0000'.
  • +
  • backgroundColor allows you to animate the background-color for a target element. Eg. backgroundColor: 'rgb(202,150,20)'.
  • +
  • outlineColor allows you to animate the outline-color for a target element. Eg. outline-color: 'cyan'.
  • +
  • borderColor allows you to animate the border-color on all sides for a target element. Eg. borderColor: 'rgba(250,100,20,0.5)'.
  • +
  • borderTopColor, borderRightColor, borderBottomColor and borderLeftColor properties allow + you to animate the color of the border on each side of a target element. Eg. borderTopColor: 'rgb(0,66,99)'.
  • +
+ +

Examples

+

OK let's have a look at some sample tween objects and a quick demo:

+ +
KUTE.to('selector1',{color:'rgb(0,66,99)'}).start();
+KUTE.to('selector1',{backgroundColor:'#069'}).start();
+KUTE.to('selector1',{borderColor:'turquoise'}).start(); // IE9+
+
+ +
+
Colors
+ +
+ Start +
+
+ +

Notes

+
    +
  • The component will NOT work with SVGElement targets and their specific color attributes like fill or stroke, for that you can use the + HTML Attributes component.
  • +
  • To simplify your workflow, the value processing can also work with web safe colors like steelblue or darkorange.
  • +
  • You can also use RGB or RGBA, but the last one is not supported on legacy browsers and it should fallback to RGB.
  • +
  • Some properties like borderColor and its variations or outlineColor won't have any visible effect if no border or outline style is applied to + your target element.
  • +
  • This component is bundled with the standard kute.js distribution file and the demo/src/kute-extra.js file.
  • + +
+ +
+ + + + +
+ + + + + + + + + + + + + + + + diff --git a/dist/kute.full.min.js b/dist/kute.full.min.js deleted file mode 100644 index e16eb93..0000000 --- a/dist/kute.full.min.js +++ /dev/null @@ -1,2 +0,0 @@ -// kute.full.min.js | by dnp_theme | License - MIT -var KUTE=KUTE||function(){var t=[];return{getAll:function(){return t},removeAll:function(){t=[]},add:function(n){t.push(n)},remove:function(n){var r=t.indexOf(n);-1!==r&&t.splice(r,1)},update:function(n){if(0===t.length)return!1;var r=0;for(n=void 0!==n?n:window.performance.now();rt)return!0;l===!1&&(null!==c&&c.call(n),l=!0);var e=(t-u)/i;e=e>1?1:e;var g=s(e);for(a in o){var h=r[a]||0,I=o[a];"string"==typeof I&&(I=h+parseFloat(I,10)),"number"==typeof I&&(n[a]=h+(I-h)*g)}return null!==p&&p.call(n,g),1==e?(null!==f&&f.call(n),!1):!0}},KUTE.Easing={Linear:{None:function(t){return t}},Quadratic:{In:function(t){return t*t},Out:function(t){return t*(2-t)},InOut:function(t){return(t*=2)<1?.5*t*t:-.5*(--t*(t-2)-1)}},Cubic:{In:function(t){return t*t*t},Out:function(t){return--t*t*t+1},InOut:function(t){return(t*=2)<1?.5*t*t*t:.5*((t-=2)*t*t+2)}},Quartic:{In:function(t){return t*t*t*t},Out:function(t){return 1- --t*t*t*t},InOut:function(t){return(t*=2)<1?.5*t*t*t*t:-.5*((t-=2)*t*t*t-2)}},Quintic:{In:function(t){return t*t*t*t*t},Out:function(t){return--t*t*t*t*t+1},InOut:function(t){return(t*=2)<1?.5*t*t*t*t*t:.5*((t-=2)*t*t*t*t+2)}},Sinusoidal:{In:function(t){return 1-Math.cos(t*Math.PI/2)},Out:function(t){return Math.sin(t*Math.PI/2)},InOut:function(t){return.5*(1-Math.cos(Math.PI*t))}},Exponential:{In:function(t){return 0===t?0:Math.pow(1024,t-1)},Out:function(t){return 1===t?1:1-Math.pow(2,-10*t)},InOut:function(t){return 0===t?0:1===t?1:(t*=2)<1?.5*Math.pow(1024,t-1):.5*(-Math.pow(2,-10*(t-1))+2)}},Circular:{In:function(t){return 1-Math.sqrt(1-t*t)},Out:function(t){return Math.sqrt(1- --t*t)},InOut:function(t){return(t*=2)<1?-.5*(Math.sqrt(1-t*t)-1):.5*(Math.sqrt(1-(t-=2)*t)+1)}},Elastic:{In:function(t){var n,r=.1,o=.4;return 0===t?0:1===t?1:(!r||1>r?(r=1,n=o/4):n=o*Math.asin(1/r)/(2*Math.PI),-(r*Math.pow(2,10*(t-=1))*Math.sin(2*(t-n)*Math.PI/o)))},Out:function(t){var n,r=.1,o=.4;return 0===t?0:1===t?1:(!r||1>r?(r=1,n=o/4):n=o*Math.asin(1/r)/(2*Math.PI),r*Math.pow(2,-10*t)*Math.sin(2*(t-n)*Math.PI/o)+1)},InOut:function(t){var n,r=.1,o=.4;return 0===t?0:1===t?1:(!r||1>r?(r=1,n=o/4):n=o*Math.asin(1/r)/(2*Math.PI),(t*=2)<1?-.5*r*Math.pow(2,10*(t-=1))*Math.sin(2*(t-n)*Math.PI/o):r*Math.pow(2,-10*(t-=1))*Math.sin(2*(t-n)*Math.PI/o)*.5+1)}},Back:{In:function(t){var n=1.70158;return t*t*((n+1)*t-n)},Out:function(t){var n=1.70158;return--t*t*((n+1)*t+n)+1},InOut:function(t){var n=2.5949095;return(t*=2)<1?.5*t*t*((n+1)*t-n):.5*((t-=2)*t*((n+1)*t+n)+2)}},Bounce:{In:function(t){return 1-KUTE.Easing.Bounce.Out(1-t)},Out:function(t){return 1/2.75>t?7.5625*t*t:2/2.75>t?7.5625*(t-=1.5/2.75)*t+.75:2.5/2.75>t?7.5625*(t-=2.25/2.75)*t+.9375:7.5625*(t-=2.625/2.75)*t+.984375},InOut:function(t){return.5>t?.5*KUTE.Easing.Bounce.In(2*t):.5*KUTE.Easing.Bounce.Out(2*t-1)+.5}}},document.addEventListener("mousewheel",preventScroll,!1),document.addEventListener("touchstart",preventScroll,!1);function preventScroll(t){var n=document.body.getAttribute("data-tweening");n&&"scroll"===n&&t.preventDefault()} diff --git a/dist/kute.jquery.js b/dist/kute.jquery.js deleted file mode 100644 index 3ecaee5..0000000 --- a/dist/kute.jquery.js +++ /dev/null @@ -1,10 +0,0 @@ -// KUTE jQuery Plugin for kute.js | by dnp_theme | License - MIT -// $('selector').Kute(options); - -(function($) { - $.fn.Kute = function( options ) { - return this.each(function(){ - new KUTE.Animate( this, options ); - }); - }; -})(jQuery); diff --git a/dist/kute.min.js b/dist/kute.min.js deleted file mode 100644 index 7226418..0000000 --- a/dist/kute.min.js +++ /dev/null @@ -1,2 +0,0 @@ -// kute.min.js | by dnp_theme | License - MIT -var KUTE=KUTE||function(){var t=[];return{getAll:function(){return t},removeAll:function(){t=[]},add:function(n){t.push(n)},remove:function(n){var i=t.indexOf(n);-1!==i&&t.splice(i,1)},update:function(n){if(0===t.length)return!1;var i=0;for(n=void 0!==n?n:window.performance.now();it)return!0;l===!1&&(null!==c&&c.call(n),l=!0);var o=(t-u)/r;o=o>1?1:o;var p=s(o);for(e in a){var E=i[e]||0,I=a[e];"string"==typeof I&&(I=E+parseFloat(I,10)),"number"==typeof I&&(n[e]=E+(I-E)*p)}return null!==f&&f.call(n,p),1==o?(null!==h&&h.call(n),!1):!0}},KUTE.Easing={Linear:{None:function(t){return t}},Quadratic:{In:function(t){return t*t},Out:function(t){return t*(2-t)},InOut:function(t){return(t*=2)<1?.5*t*t:-.5*(--t*(t-2)-1)}},Cubic:{In:function(t){return t*t*t},Out:function(t){return--t*t*t+1},InOut:function(t){return(t*=2)<1?.5*t*t*t:.5*((t-=2)*t*t+2)}},Quartic:{In:function(t){return t*t*t*t},Out:function(t){return 1- --t*t*t*t},InOut:function(t){return(t*=2)<1?.5*t*t*t*t:-.5*((t-=2)*t*t*t-2)}},Quintic:{In:function(t){return t*t*t*t*t},Out:function(t){return--t*t*t*t*t+1},InOut:function(t){return(t*=2)<1?.5*t*t*t*t*t:.5*((t-=2)*t*t*t*t+2)}},Sinusoidal:{In:function(t){return 1-Math.cos(t*Math.PI/2)},Out:function(t){return Math.sin(t*Math.PI/2)},InOut:function(t){return.5*(1-Math.cos(Math.PI*t))}},Exponential:{In:function(t){return 0===t?0:Math.pow(1024,t-1)},Out:function(t){return 1===t?1:1-Math.pow(2,-10*t)},InOut:function(t){return 0===t?0:1===t?1:(t*=2)<1?.5*Math.pow(1024,t-1):.5*(-Math.pow(2,-10*(t-1))+2)}},Circular:{In:function(t){return 1-Math.sqrt(1-t*t)},Out:function(t){return Math.sqrt(1- --t*t)},InOut:function(t){return(t*=2)<1?-.5*(Math.sqrt(1-t*t)-1):.5*(Math.sqrt(1-(t-=2)*t)+1)}},Elastic:{In:function(t){var n,i=.1,a=.4;return 0===t?0:1===t?1:(!i||1>i?(i=1,n=a/4):n=a*Math.asin(1/i)/(2*Math.PI),-(i*Math.pow(2,10*(t-=1))*Math.sin(2*(t-n)*Math.PI/a)))},Out:function(t){var n,i=.1,a=.4;return 0===t?0:1===t?1:(!i||1>i?(i=1,n=a/4):n=a*Math.asin(1/i)/(2*Math.PI),i*Math.pow(2,-10*t)*Math.sin(2*(t-n)*Math.PI/a)+1)},InOut:function(t){var n,i=.1,a=.4;return 0===t?0:1===t?1:(!i||1>i?(i=1,n=a/4):n=a*Math.asin(1/i)/(2*Math.PI),(t*=2)<1?-.5*i*Math.pow(2,10*(t-=1))*Math.sin(2*(t-n)*Math.PI/a):i*Math.pow(2,-10*(t-=1))*Math.sin(2*(t-n)*Math.PI/a)*.5+1)}},Back:{In:function(t){var n=1.70158;return t*t*((n+1)*t-n)},Out:function(t){var n=1.70158;return--t*t*((n+1)*t+n)+1},InOut:function(t){var n=2.5949095;return(t*=2)<1?.5*t*t*((n+1)*t-n):.5*((t-=2)*t*((n+1)*t+n)+2)}},Bounce:{In:function(t){return 1-KUTE.Easing.Bounce.Out(1-t)},Out:function(t){return 1/2.75>t?7.5625*t*t:2/2.75>t?7.5625*(t-=1.5/2.75)*t+.75:2.5/2.75>t?7.5625*(t-=2.25/2.75)*t+.9375:7.5625*(t-=2.625/2.75)*t+.984375},InOut:function(t){return.5>t?.5*KUTE.Easing.Bounce.In(2*t):.5*KUTE.Easing.Bounce.Out(2*t-1)+.5}}},document.addEventListener("mousewheel",preventScroll,!1),document.addEventListener("touchstart",preventScroll,!1);function preventScroll(t){var n=document.body.getAttribute("data-tweening");n&&"scroll"===n&&t.preventDefault()} diff --git a/filterEffects.html b/filterEffects.html new file mode 100644 index 0000000..18fb47a --- /dev/null +++ b/filterEffects.html @@ -0,0 +1,190 @@ + + + + + + + + + + + + + + KUTE.js Filter Effects + + + + + + + + + + + + + + +
+ + + +
+

Filter Effects

+

The component that animates the CSS3 filter property for specific Element targets on modern browsers.

+
+ +
+
+
+
+

Overview

+

Animate filter functions for a target Element and deliver the best possible animation.

+
+
+

The KUTE.js Filter Effects component provides support for the CSS3 filter property on modern browsers.

+

The component covers all filter functions, except that the url() function cannot be animated, however the component will + try and keep it's value into the target element styling at all times.

+

Similar to the Transform Functions and the Transform Matrix components, + this component will try and keep same order of the filter functions, regardless of the input order, and the reason is that chained animations can + have some unwanted janky artifact effects.

+

Most values are in [0-Infinity] range and the presentation is delivered with % suffixed values, except blur() which uses px + as unit/suffix.

+
+
+
+
+ +
+

Filter Functions

+
    +
  • url() function links an element to an SVG filter, the function is supported to keep it's value into the target element's style in case it's set + initially or you want to set it yourself.
  • +
  • opacity() can animate the opacity for a target element in the 0-100% range. Default is 100%.
  • +
  • blur() can animate the blur for a target element in the 0-Infinity range. Default is 0px and the unit is always px.
  • +
  • saturate() can animate the color saturation for a target element in the 0-Infinity range. Default is 100%.
  • +
  • grayscale() can animate the color desaturation for a target element in the 0-100% range. Default is 0%.
  • +
  • brightness() can animate the brightness for a target element in the 0-Infinity range. Default is 100%.
  • +
  • contrast() can animate the contrast for a target element in the 0-Infinity range. Default is 100%.
  • +
  • sepia() can animate the sepia filter for a target element in the 0-100% range. Default is 0%.
  • +
  • invert() can animate the color inversion for a target element in the 0-100% range. Default is 0%.
  • +
  • hueRotate() can animate the color hue rotation for a target element in the 0-Infinity range. Default is 0deg.
  • +
  • dropShadow() can animate the shadow and all related parameters for a target element. Default is [0,0,0,'black']
  • +
+ +

Examples

+

Let's have a look at some sample tween objects and a quick example:

+ +
let fe1 = KUTE.to('selector1', {filter :{ blur: 5 }})
+let fe2 = KUTE.to('selector2', {filter :{ sepia: 50, invert: 80 }})
+let fe3 = KUTE.to('selector3', {filter :{ saturate: 150, brightness: 90 }})
+let fe4 = KUTE.to('selector4', {filter :{ url: '#mySVGFilter', opacity: 40, dropShadow:[5,5,5,'olive'] }})
+
+ +
+ + + + + + + + + + + + + + +
FE1
+
FE2
+
FE3
+
FE4
+ +
+ Start +
+
+

Notes

+
    +
  • The CSS filter property is supported on all major browsers nowadays, but it's better to check and + double check.
  • +
  • This component can be a great addition to all SVG related components, especially because the dropShadow provides a better experience than + boxShadow, as shown here.
  • +
  • Since this component can work with the url() function the way it does, the HTML Attributes component will + complement greatly for even more animation potential.
  • +
  • Similar to the HTML Attributes component, this one can also deal with specific function namespace, for instance hue-rotate and + hueRotate.
  • +
  • This component is bundled with the demo/src/kute-extra.js file.
  • +
+
+ + + + +
+ + + + + + + + + + + + + + diff --git a/htmlAttributes.html b/htmlAttributes.html new file mode 100644 index 0000000..089375a --- /dev/null +++ b/htmlAttributes.html @@ -0,0 +1,233 @@ + + + + + + + + + + + + + + KUTE.js HTML Attributes + + + + + + + + + + + + + +
+ + + +
+

HTML Attributes

+

The component that animates color attributes or any single value presentation attribute of a target element on most browsers.

+
+ +
+
+
+
+

Overview

+

Animate a wide variety of presetantion attributes of a target element.

+
+
+

The KUTE.js HTML Attributes component enables animation for any numeric presentation attribute, with or without a measurement unit / suffix as well as specific color attributes.

+

The component can be a great asset for creating complex animations in combination with the SVG components as we'll see in the following examples.

+

The component doesn't support attributes with multiple values like stroke-dasharray, viewBox or transform for specific reasons. To animate the stroke related attributes, the + SVG Draw component is the tool for you, while for transform you have the SVG Transform component and the + Transform Matrix component.

+

Despite the "limitations" of this component, you have access to just about any SVGElement or Element + presentation attribute available.

+
+
+
+
+ +
+

General Usage

+ +
// basic notation for unitless attributes
+var myAttrTween = KUTE.to('selector', {attr: {attributeName: 75}});
+
+// OR for attributes that are ALWAYS suffixed / have a measurement unit
+var mySuffAttrTween = KUTE.to('selector', {attr:{attributeName: '15%'}});
+
+ +

Attributes Namespace

+

The HTML Attributes component can handle all possible single value presentation attributes with both dashed string and camel-case notation. Let's have a look at a sample notation so you can + get the idea:

+ +
// dashed attribute notation
+var myDashedAttrStringTween = KUTE.to('selector', {attr: {'stroke-width': 75}});
+
+// non-dashed attribute notation
+var myNonDashedAttrStringTween = KUTE.to('selector', {attr:{strokeWidth: '15px'}});
+
+ +

The strokeWidth attribute is very interesting because it can work with px, % or with no unit/suffix. In this case, and in any context, the component will always work with the + attribute's current value suffix to eliminate any possible janky animation.

+ +

Examples

+

Color Attributes

+

The HTML Attributes component can also animate color attributes: fill, stroke and stopColor. If the elements are affected by their CSS counterparts, the effect + is not visible, you need to make sure that doesn't happen.

+ +
// some fill rgb, rgba, hex
+var fillTween = KUTE.to('#element-to-fill', {attr: { fill: 'red' }});
+
+// some stopColor or 'stop-color'
+var stopColorTween = KUTE.to('#element-to-do-stop-color', {attr: {stopColor: 'rgb(0,66,99)'}});
+
+ +
+ + + + + +
+ Start +
+
+

If in this example the fill attribute value would reference a gradient, then rgba(0,0,0,0) is used. Keep in mind that the component will not work with combined + fill values like url(#pattern) rgba(), you are better of only using the url(#pattern) and use other attributes to control directly the animation of that + linked pattern, just like in the last example below.

+ +

Unitless Attributes

+

In the next example, let's play with the attributes of a <circle> element: radius and center coordinates.

+ +
// radius attribute
+var radiusTween = KUTE.to('#circle', {attr: {r: 75}});
+
+// coordinates of the circle center
+var coordinatesTween = KUTE.to('#circle', {attr:{cx:0,cy:0}});
+
+ +

A quick demo with the above:

+ +
+ + + + +
+ Start +
+
+ +

Suffixed Attributes

+

Similar to the example on circle attributes, we can also animate the gradient positions but this time with a specific to gradients suffix, and the component will make sure + to always include the suffix for you, as in this example the % unit is found in the current value and used as unit for the DOM update:

+ +
// gradient positions to middle
+var closingGradient = KUTE.to('#gradient', {attr: {x1:'49%', x2:'49%', y1:'49%', y2:'49%'}});
+
+// gradient positions rotated
+var rotatingGradient = KUTE.to('#gradient', {attr: {x1:'49%', x2:'51%', y1:'51%', y2:'51%'}});
+
+ +
+ + + + + + + + + + +
+ Start +
+
+ +

Notes

+
    +
  • The power of this little gem comes from the fact that it can work with internally undefined/unknown attributes, as well as with attributes that are not yet present in the W3 draft. As long as you provide valid values specific + to the attribute, the component will assign an update function and will always double check for the current value to determine if it needs a suffix or if the attribute name needs adjustments + (EG: fillOpacity becomes fill-opacity).
  • +
  • This component is a great addition to complement any SVG related component as well as a great complement to Filter Effects component.
  • +
  • Remember to check your elements markup for changes, your animation might not be visible because equivalent CSS is used.
  • +
  • This component is bundled with the standard kute.js distribution file and the demo/src/kute-extra.js file.
  • +
+
+ + + +
+ + + + + + + + + + + + diff --git a/index.html b/index.html new file mode 100644 index 0000000..c6032b8 --- /dev/null +++ b/index.html @@ -0,0 +1,292 @@ + + + + + + + + + + + + + + KUTE.js | JavaScript Animation Engine + + + + + + + + + + + + + + +
+ + + +
+
+
+
+
+
+
+
+
+
+

Welcome to KUTE.js!

+
+
+

The JavaScript animation engine of the future

+
+
+

The magic behind Spicr, the result of hard work, + the bridge between old and new, the dream and inspiration.

+
+
+

+ Download + Github + NPM + CDN +

+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

Forward Looking

+

The modern JavaScript powering the core functionality, the modular architecture, the solid and complete components are all geared towards the future of web development.

+
+
+
+
+
+
+

Knowing the Past

+

While KUTE.js was re-developed to support mainly modern browsers, it also leaves the door open to old browsers with its legacy components, tools and polyfills.

+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

ES6+ JavaScript

+
+
+

Inside the sources you will find fast & modern JavaScript code with solid build tools. Everything and anything can be done with SVGElement, + HTML attributes, CSS transform, etc.

+
+
+
+
+
+
+
+
+

Cubic Bezier Easing

+
+
+

While the library includes Robert Penner's easing functions, KUTE.js also includes its own + CubicBezier Easing class to provide fast and accurate easing for your animation.

+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

What's not to love about it?

+

Yeah, the open source, modular and fully featured animation engine for the modern web, fully documented and MIT Licensed.

+

Any question? Find answers here, or post them here!

+
+
+
+
+
+ +
    +
  1. +
  2. +
  3. +
  4. +
+ + + + + + + + + + + + + + +
+ +
+
+
+
+

Overview

+

If you're looking for that spicy tool to kickstart your animations, developed with latest technology and modular design, you will love KUTE.js.

+
+
+ +

The JavaScript animation engine reimagined for the evolving modern web, the library that keeps track on the changing standards, the modular tool + to enable creativity.

+

Built on modern ES6+ JavaScript Standard, packed with utilities, build tools and a wide range of supported properties, KUTE.js is now a fully featured animation engine + you can use to create complex animations, with properties or elements that cannot be animated with CSS3 transitions or other animation engines, or attributes that aren't + even drafted in the specification yet.

+ +

The JavaScript animation engine that never stops evolving just like we never stop learning. Instead of becoming more bloated, we make it more modular, + instead of compromising we chose innovating.

+

While KUTE.js doesn't activelly support legacy browsers, it provides a wide range of tools and utilities you can use to create a fallback animation for every browser + and property.

+

The demo pages use a dedicated polyfill for IE10+ browsers, but today we have polyfill services and a wide range of + browser detection options to handle legacy browsers, our focus here is the highest performance on modern browsers.

+

By the way, KUTE.js is really fast.

+
+
+ +
+
+ +
+ +
+
+

ES6+ JavaScript

+

The entire codebase reworked on the latest standards with flexible rollup based build tools. Most classes are extensible via the ES6 extend + or your usual prototype, depending on the class.

+
+
+

Lots of Components

+

All your previously supported properties and plugins have been split into components for more modularity. New additions have been added for a broader scope. + The official distribution only includes most popular components.

+
+
+

Familiar Syntax

+

You can create your tween objects with a familiar syntax with your trusted options, the callback system and other component related options.

+
+
+

Tools and Options

+

Along the wide range of options, certain components provide tools for further control over the outcome. Also there's a progress + bar somewhere.

+
+
+

Well Documented

+

Each component demo page comes packed with tons of guides and tips on how to optimize the performance and visual presentation.

+
+
+

MIT License

+

The library is released under the MIT License.

+
+ +
+
+ + + + +
+ + + + + + + + + + + + + + + diff --git a/kute.full.js b/kute.full.js deleted file mode 100644 index 8e10ab5..0000000 --- a/kute.full.js +++ /dev/null @@ -1,811 +0,0 @@ -// kute.full.js - The Light Tweening Engine | by dnp_theme -// http://themeforest.net/user/dnp_theme -// License - MIT - - -// KUTE MAIN OBJECT -var KUTE = KUTE || ( function () { - var _tweens = []; - return { - getAll: function () { - return _tweens; - }, - removeAll: function () { - _tweens = []; - }, - add: function ( tween ) { - _tweens.push( tween ); - }, - remove: function ( tween ) { - var i = _tweens.indexOf( tween ); - if ( i !== -1 ) { - _tweens.splice( i, 1 ); - } - }, - update: function ( time ) { - if ( _tweens.length === 0 ) return false; - var i = 0; - time = time !== undefined ? time : window.performance.now(); - while ( i < _tweens.length ) { - if ( _tweens[ i ].update( time ) ) { - i++; - } else { - _tweens.splice( i, 1 ); - } - } - return true; - } - }; -} )(); - -KUTE.Animate = function( object, options ) { - - //element to animate - var el = typeof object === 'object' ? object : document.querySelector(object); - - //get true scroll container and current scroll - var bd = document.body, - htm = document.getElementsByTagName('HTML')[0], - sct = /webkit/i.test(navigator.userAgent) || document.compatMode == 'BackCompat' ? bd : htm, - crs = window.pageYOffset || sct.scrollTop; - - //determine if we're on IE or IE8 - var isIE = document.documentElement.classList.contains('ie'); - var isIE8 = document.documentElement.classList.contains('ie8'); - - //get element current style - var css = el.currentStyle || window.getComputedStyle(el); - - // default values - var ops = { - from : { - opacity : 1, // integer - width : '', // integer/px/% - height : '', // integer/px/% - color : '', //hex/rgb - backgroundColor : '', //hex/rgb - position : {top:'',right:'',bottom:'',left:''}, // integer/% - backgroundPosition: {x:'',y:''}, // integer/%/string[left,center,bottom,etc] - translate : {x:0, y:0, z:0}, // integer only - rotate : {x:0, y:0, z:0}, // integer only - scale : 1, // integer only - scroll : crs, // integer only - }, - to : { - opacity : '', - width : '', - height : '', - color : '', - backgroundColor : '', - position : {top:'',right:'',bottom:'',left:''}, - backgroundPosition: {x: '', y: ''}, - translate : {x:'', y:'', z:''}, - rotate : {x:'', y:'', z:''}, - scale : '', - scroll : '', - }, - easing : KUTE.Easing.Linear.None, //pe('linear') - delay : 0, - duration : 500, - start : null, // run function when tween starts - finish : null, // run function when tween finishes - special : null // run function while tween runing - }; - - //override the default values with option values - for (var x in options) { - if(typeof(options[x]) === 'object'){ - for (var y in options[x]){ - ops[x][y] = options[x][y]; - } - }else{ - ops[x] = options[x]; - } - } - - //create shorthand for all properties - var ofo = ops.from.opacity; - var ofw = ops.from.width; - var ofh = ops.from.height; - var ofc = ops.from.color; - var ofbc = ops.from.backgroundColor; - var oft = ops.from.position.top; - var ofr = ops.from.position.right; - var ofb = ops.from.position.bottom; - var ofl = ops.from.position.left; - var ofbx = ops.from.backgroundPosition.x; - var ofby = ops.from.backgroundPosition.y; - var oftx = ops.from.translate.x; - var ofty = ops.from.translate.y; - var oftz = ops.from.translate.z; - var ofrx = ops.from.rotate.x; - var ofry = ops.from.rotate.y; - var ofrz = ops.from.rotate.z; - var ofs = ops.from.scale; - var ofsc = ops.from.scroll; - - var oto = ops.to.opacity; - var otw = ops.to.width; - var oth = ops.to.height; - var otc = ops.to.color; - var otbc = ops.to.backgroundColor; - var ott = ops.to.position.top; - var otr = ops.to.position.right; - var otb = ops.to.position.bottom; - var otl = ops.to.position.left; - var otbx = ops.to.backgroundPosition.x; - var otby = ops.to.backgroundPosition.y; - var ottx = ops.to.translate.x; - var otty = ops.to.translate.y; - var ottz = ops.to.translate.z; - var otrx = ops.to.rotate.x; - var otry = ops.to.rotate.y; - var otrz = ops.to.rotate.z; - var ots = ops.to.scale; - var otsc = ops.to.scroll; - - //process easing - var pes = typeof ops.easing === 'string' ? pe(ops.easing) : ops.easing; - - //from/initial values - var icor = (cv(ofc) ? parseInt(pc(ofc)[0]) : '') || parseInt(pc(truC(css.color))[0]); - var icog = (cv(ofc) ? parseInt(pc(ofc)[1]) : '') || parseInt(pc(truC(css.color))[1]); - var icob = (cv(ofc) ? parseInt(pc(ofc)[2]) : '') || parseInt(pc(truC(css.color))[2]); - - var ibcr = (cv(ofbc) ? parseInt(pc(ofbc)[0]) : '') || parseInt(pc(truC(css.backgroundColor))[0]); - var ibcg = (cv(ofbc) ? parseInt(pc(ofbc)[1]) : '') || parseInt(pc(truC(css.backgroundColor))[1]); - var ibcb = (cv(ofbc) ? parseInt(pc(ofbc)[2]) : '') || parseInt(pc(truC(css.backgroundColor))[2]); - - var iwi = cv(ofw) ? truD(ofw)[0] : truD( css.width )[0]; - var ihe = cv(ofh) ? truD(ofh)[0] : truD( css.height )[0]; - - var ito = cv(oft) ? truD(oft)[0] : ''; - var iri = cv(ofr) ? truD(ofr)[0] : ''; - var ibo = cv(ofb) ? truD(ofb)[0] : ''; - var ile = cv(ofl) ? truD(ofl)[0] : ''; - - var ibx, iby, bx, by; - if ( cv( otbx ) || cv( otby ) ) { - ibx = cv( ofbx ) ? truX(ofbx) : bPos(el)[0]; - iby = cv( ofby ) ? truY(ofby) : bPos(el)[1]; - } else { - ibx = ''; - iby = ''; - } - - var tr3d,tx,ty,tz,itx,ity,itz; - if ( cv( ottx ) || cv( otty ) || cv( ottz ) ) { - itx = cv(oftx) ? truD(oftx)[0] : 0; - ity = cv(ofty) ? truD(ofty)[0] : 0; - itz = cv(oftz) ? truD(oftz)[0] : 0; - } else { - itx = ''; ity = ''; itz = ''; - } - - var irx = cv(ofrx) ? parseInt(ofrx) :''; //always deg - var iry = cv(ofry) ? parseInt(ofry) :''; - var irz = cv(ofrz) ? parseInt(ofrz) :''; - - var isa = parseFloat(ofs); // scale can be float - var iop = parseFloat(ofo); // opacity - var isc = parseInt(ofsc); // scroll - - - //target values - var cor = cv(otc) ? parseInt(pc(otc)[0]) : ''; - var cog = cv(otc) ? parseInt(pc(otc)[1]) : ''; - var cob = cv(otc) ? parseInt(pc(otc)[2]) : ''; - - var bcr = cv(otbc) ? parseInt(pc(otbc)[0]) : ''; - var bcg = cv(otbc) ? parseInt(pc(otbc)[1]) : ''; - var bcb = cv(otbc) ? parseInt(pc(otbc)[2]) : ''; - - var wi = cv( otw ) ? truD(otw)[0] : ''; - var he = cv( oth ) ? truD(oth)[0] : ''; - - var top = cv(ott) ? truD(ott)[0] : ''; - var ri = cv(otr) ? truD(otr)[0] : ''; - var bo = cv(otb) ? truD(otb)[0] : ''; - var le = cv(otl) ? truD(otl)[0] : ''; - - if ( cv( otbx ) || cv( otby ) ) { - bx = cv( otbx ) ? truX(otbx) : ibx; - by = cv( otby ) ? truY(otby) : iby; - } else { - bx = ''; - by = ''; - } - - if ( cv( ottx ) || cv( otty ) || cv( ottz ) ) { // translate 3d - tx = cv( ottx ) ? truD(ottx)[0] : 0; - ty = cv( otty ) ? truD(otty)[0] : 0; - tz = cv( ottz ) ? truD(ottz)[0] : 0; - } else { - tx = ''; ty = ''; tz = ''; - } - - var rx = cv( otrx ) ? parseInt(otrx) : ''; // rotate - var ry = cv( otry ) ? parseInt(otry) : ''; - var rz = cv( otrz ) ? parseInt(otrz) : ''; - - var sa = cv( ots ) ? parseFloat(ots) : ''; // scale values below 1 need to be reformated - var op = cv( oto ) ? parseFloat(oto) : ''; // opacity - var sc = cv( otsc ) ? parseInt(otsc) : ''; // scroll - - //check unit - var wiu = cv( wi ) ? truD(otw)[1] : ''; - var heu = cv( he ) ? truD(oth)[1] : ''; - - var tou = cv( ott ) ? truD(ott)[1] : ''; - var riu = cv( otr ) ? truD(otr)[1] : ''; - var bou = cv( otb ) ? truD(otb)[1] : ''; - var leu = cv( otl ) ? truD(otl)[1] : ''; - - var txu = cv( tx ) ? truD(ottx)[1] : ''; - var tyu = cv( ty ) ? truD(otty)[1] : ''; - var tzu = cv( tz ) ? truD(ottz)[1] : ''; - - animateTween(); - - var from = { w: iwi, h: ihe, t: ito, r: iri, b: ibo, l: ile, colr: icor, colg: icog, colb: icob, bgr: ibcr, bgg: ibcg, bgb: ibcb, bgX: ibx, bgY: iby, scale: isa, trX: itx, trY: ity, trZ: itz, roX: irx, roY: iry, roZ: irz, opacity: iop, scroll: isc }; - var target = { w: wi, h: he, t: top, r: ri, b: bo, l: le, colr: cor, colg: cog, colb: cob, bgr: bcr, bgg: bcg, bgb: bcb, bgX: bx, bgY: by, scale: sa, trX: tx, trY: ty, trZ: tz, roX: rx, roY: ry, roZ: rz, opacity: op, scroll: sc }; - - return new KUTE.Tween( from ) - .to( target, ops.duration ) - .delay( ops.delay ) - .easing( pes ) - .onStart( runStart ) - .onUpdate( - function () { - - //color and background-color - if ( cv(cor) ) { el.style.color = rth( parseInt(this.colr),parseInt(this.colg),parseInt(this.colb) ); } - if ( cv(bcr) ) { el.style.backgroundColor = rth( parseInt(this.bgr),parseInt(this.bgg),parseInt(this.bgb)); } - - //translate3d - if ( cv(tx) || cv(ty) || cv(tz) ) { - tr3d = 'translate3d(' + ((this.trX + txu) || 0) + ',' + ((this.trY + tyu) || 0) + ',' + ((this.trZ + tzu) || 0) + ')'; - } else { tr3d = ''; } - - var roxt = cv(rx) ? ' rotateX(' + this.roX + 'deg)' : ''; - var royt = cv(ry) ? ' rotateY(' + this.roY + 'deg)' : ''; - var rozt = cv(rz) ? ' rotateZ(' + this.roZ + 'deg)' : ''; - - //scale - var sca = cv(sa) ? ' scale(' + this.scale + ')' : ''; - //do a zoom for IE8 - if (isIE8 && cv(sa)) { - el.style.zoom = this.scale; - } - //sum all transform - var transform = sca + tr3d + roxt + royt + rozt; - var perspective = parseInt(css.perspective)||''; - if ( cv(transform) ) { tr(transform,perspective) } - - //dimensions - if ( cv(wi) ) { el.style.width = this.w + wiu; } - if ( cv(he) ) { el.style.height = this.h + heu; } - - //positioning - if ( cv(top) ) { el.style.top = this.t + tou; } - if ( cv(ri ) ) { el.style.right = this.r + riu; } - if ( cv(bo ) ) { el.style.bottom = this.b + bou; } - if ( cv(le ) ) { el.style.left = this.l + leu; } - - // scrolling - if ( cv(sc) ) { sct.scrollTop = this.scroll; } - - //background position - if ( cv(bx) || cv(by) ) { - var bXX = this.bgX; - var bYY = this.bgY; - el.style.backgroundPosition = bXX.toString()+'% '+bYY.toString()+'%'; - } - - //opacity - if ( cv(op) ) { el.style.opacity = (this.opacity).toFixed(2); } - //do a filter opacity for IE8 - if (isIE8 && cv(op)) { - el.style.filter = "alpha(opacity=" + parseInt(100 * this.opacity) + ")" - } - - //run special function onUpdate - if ( ops.special && typeof ops.special === "function") { ops.special(); } - } - ) - .onComplete( runFinished ) - .start(); - - function animateTween(time) { - requestAnimationFrame( animateTween ); - KUTE.update(time); - } - - //callback when tween is finished - function runFinished() { - if ( ops.finish && typeof ops.finish === "function") { - ops.finish(); - } - if ( cv(otsc) ) { - document.body.removeAttribute('data-tweening') - } - } - - //callback when tween just started - function runStart() { - if ( ops.start && typeof ops.start === "function") { - ops.start(); - } - //fix the scrolling being interrupted via mousewheel - if ( cv(otsc) ) { - if ( !document.body.getAttribute('data-tweening') && document.body.getAttribute('data-tweening') !== 'scroll' ) - document.body.setAttribute('data-tweening','scroll'); - } - } - - /* Process values utils - ----------------------------*/ - - //process easing 31 - function pe(e) { - if ( e === 'linear' ) return KUTE.Easing.Linear.None; - if ( e === 'quadraticIn' ) return KUTE.Easing.Quadratic.In; - if ( e === 'quadraticOut' ) return KUTE.Easing.Quadratic.Out; - if ( e === 'quadraticInOut' ) return KUTE.Easing.Quadratic.InOut; - if ( e === 'cubicIn' ) return KUTE.Easing.Cubic.In; - if ( e === 'cubicOut' ) return KUTE.Easing.Cubic.Out; - if ( e === 'cubicInOut' ) return KUTE.Easing.Cubic.InOut; - if ( e === 'quarticIn' ) return KUTE.Easing.Quartic.In; - if ( e === 'quarticOut' ) return KUTE.Easing.Quartic.Out; - if ( e === 'quarticInOut' ) return KUTE.Easing.Quartic.InOut; - if ( e === 'quinticIn' ) return KUTE.Easing.Quintic.In; - if ( e === 'quinticOut' ) return KUTE.Easing.Quintic.Out; - if ( e === 'quinticInOut' ) return KUTE.Easing.Quintic.InOut; - if ( e === 'sinusoidalIn' ) return KUTE.Easing.Sinusoidal.In; - if ( e === 'sinusoidalOut' ) return KUTE.Easing.Sinusoidal.Out; - if ( e === 'sinusoidalInOut' ) return KUTE.Easing.Sinusoidal.InOut; - if ( e === 'exponentialIn' ) return KUTE.Easing.Exponential.In; - if ( e === 'exponentialOut' ) return KUTE.Easing.Exponential.Out; - if ( e === 'exponentialInOut' ) return KUTE.Easing.Exponential.InOut; - if ( e === 'circularIn' ) return KUTE.Easing.Circular.In; - if ( e === 'circularOut' ) return KUTE.Easing.Circular.Out; - if ( e === 'circularInOut' ) return KUTE.Easing.Circular.InOut; - if ( e === 'elasticIn' ) return KUTE.Easing.Elastic.In; - if ( e === 'elasticOut' ) return KUTE.Easing.Elastic.Out; - if ( e === 'elasticInOut' ) return KUTE.Easing.Elastic.InOut; - if ( e === 'backIn' ) return KUTE.Easing.Back.In; - if ( e === 'backOut' ) return KUTE.Easing.Back.Out; - if ( e === 'backInOut' ) return KUTE.Easing.Back.InOut; - if ( e === 'bounceIn' ) return KUTE.Easing.Bounce.In; - if ( e === 'bounceOut' ) return KUTE.Easing.Bounce.Out; - if ( e === 'bounceInOut' ) return KUTE.Easing.Bounce.InOut; - //default - return KUTE.Easing.Exponential.InOut; - } - - // value checker - function cv(v) { - if ( v !== undefined && v !== '' && v !== 'NaN' ) return true; - } - - // get true w/h - function truD(d){ - var v,u; - if (/px/i.test(d)) { - u = 'px'; v = parseInt( d ); - } else if (/%/i.test(d)) { - u = '%'; v = parseInt( d ); - } else { - v = d; u = 'px'; - } - return [v,u]; - } - - // get background position true values - function truX(x) { - if ( x == 'left' ) { - return 0; - } else if ( x == 'center' ) { - return 50; - } else if ( x == 'right' ) { - return 100; - } else { - return parseInt( x ); - } - } - function truY(y) { - if ( y == 'top' ) { - return 0; - } else if ( y == 'center' ) { - return 50; - } else if ( y == 'bottom' ) { - return 100; - } else { - return parseInt( y ); - } - } - - // get current background position - function bPos(elem) { - var sty = css.backgroundPosition,x,y; - var pos = sty.split(" "); - x = truX(pos[0]); - if ( cv(pos[1]) ) { - y = truY(pos[1]); - } else { - y = 0; - } - return [ x, y ]; - } - - // convert transparent to rgba() - function truC(c) { - if ( c === 'transparent' ) { - return c.replace('transparent','rgba(0,0,0,0)'); - } else if ( cv(c) ) { - return c; - } - } - - // process color - function pc(c) { - if ( cv(c) && /#/i.test(c) ) { return [htr(c).r,htr(c).g,htr(c).b]; } else { return c.replace(/[^\d,]/g, '').split(','); } - } - - // transform rgb to hex or vice-versa - function rth(r, g, b) { - return "#" + ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1); - } - function htr(hex) { - // Expand shorthand form (e.g. "03F") to full form (e.g. "0033FF") - var shr = /^#?([a-f\d])([a-f\d])([a-f\d])$/i; - hex = hex.replace(shr, function(m, r, g, b) { - return r + r + g + g + b + b; - }); - - var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex); - return result ? { - r: parseInt(result[1], 16), - g: parseInt(result[2], 16), - b: parseInt(result[3], 16) - } : null; - } - - // process transform - function tr(p,pp) { - el.style.webkitTransform = p; - el.style.MozTransform = p; - el.style.msTransform = (cv(pp)?'perspective('+pp+'px) ':'') + p; - el.style.Transform = p; - } -}; - -KUTE.Tween = function ( object ) { - - var _object = object; - var _valuesStart = {}; - var _valuesEnd = {}; - var _valuesStartRepeat = {}; - var _duration = 700; - var _isPlaying = false; - var _delayTime = 0; - var _startTime = null; - var _easingFunction = KUTE.Easing.Linear.None; - var _onStartCallback = null; - var _onStartCallbackFired = false; - var _onUpdateCallback = null; - var _onCompleteCallback = null; - var _onStopCallback = null; - - // Set all starting values present on the target object - for ( var field in object ) { - _valuesStart[ field ] = parseFloat(object[field], 10); - } - - this.to = function ( properties, duration ) { - - if ( duration !== undefined ) { - _duration = duration; - } - - _valuesEnd = properties; - return this; - }; - - this.start = function ( time ) { - - KUTE.add( this ); - _isPlaying = true; - _onStartCallbackFired = false; - _startTime = time !== undefined ? time : window.performance.now(); - _startTime += _delayTime; - - for ( var property in _valuesEnd ) { - // check if an Array was provided as property value - if ( _valuesEnd[ property ] instanceof Array ) { - if ( _valuesEnd[ property ].length === 0 ) { - continue; - } - - // create a local copy of the Array with the start value at the front - _valuesEnd[ property ] = [ _object[ property ] ].concat( _valuesEnd[ property ] ); - } - - if( ( _valuesEnd[ property ] instanceof Array ) === false ) { - _valuesEnd[ property ] *= 1.0; // Ensures we're using numbers, not strings - } - - _valuesStart[ property ] = _object[ property ]; - - if( ( _valuesStart[ property ] instanceof Array ) === false ) { - _valuesStart[ property ] *= 1.0; // Ensures we're using numbers, not strings - } - } - return this; - }; - - this.stop = function () { - if ( !_isPlaying ) { - return this; - } - - KUTE.remove( this ); - _isPlaying = false; - - if ( _onStopCallback !== null ) { - _onStopCallback.call( _object ); - } - - return this; - - }; - - this.delay = function ( amount ) { - _delayTime = amount; - return this; - }; - - this.easing = function ( easing ) { - _easingFunction = easing; - return this; - }; - - this.onStart = function ( callback ) { - _onStartCallback = callback; - return this; - }; - - this.onUpdate = function ( callback ) { - _onUpdateCallback = callback; - return this; - }; - - this.onComplete = function ( callback ) { - _onCompleteCallback = callback; - return this; - }; - - this.onStop = function ( callback ) { - _onStopCallback = callback; - return this; - }; - - this.update = function ( time ) { - var property; - - if ( time < _startTime ) { - return true; - } - - if ( _onStartCallbackFired === false ) { - if ( _onStartCallback !== null ) { - _onStartCallback.call( _object ); - } - _onStartCallbackFired = true; - } - - var elapsed = ( time - _startTime ) / _duration; - elapsed = elapsed > 1 ? 1 : elapsed; - var value = _easingFunction( elapsed ); - - for ( property in _valuesEnd ) { - - var start = _valuesStart[ property ] || 0; - var end = _valuesEnd[ property ]; - - // Parses relative end values with start as base (e.g.: +10, -3) - if ( typeof(end) === "string" ) { - end = start + parseFloat(end, 10); - } - - // protect against non numeric properties. - if ( typeof(end) === "number" ) { - _object[ property ] = start + ( end - start ) * value; - } - } - - if ( _onUpdateCallback !== null ) { - _onUpdateCallback.call( _object, value ); - } - - if ( elapsed == 1 ) { - - if ( _onCompleteCallback !== null ) { - _onCompleteCallback.call( _object ); - } - return false; - } - return true; - }; -}; - -KUTE.Easing = { - Linear: { - None: function ( k ) { - return k; - } - }, - Quadratic: { - In: function ( k ) { - return k * k; - }, - - Out: function ( k ) { - return k * ( 2 - k ); - }, - - InOut: function ( k ) { - if ( ( k *= 2 ) < 1 ) return 0.5 * k * k; - return - 0.5 * ( --k * ( k - 2 ) - 1 ); - } - }, - Cubic: { - In: function ( k ) { - return k * k * k; - }, - Out: function ( k ) { - return --k * k * k + 1; - }, - InOut: function ( k ) { - if ( ( k *= 2 ) < 1 ) return 0.5 * k * k * k; - return 0.5 * ( ( k -= 2 ) * k * k + 2 ); - } - }, - Quartic: { - In: function ( k ) { - return k * k * k * k; - }, - Out: function ( k ) { - return 1 - ( --k * k * k * k ); - }, - InOut: function ( k ) { - if ( ( k *= 2 ) < 1) return 0.5 * k * k * k * k; - return - 0.5 * ( ( k -= 2 ) * k * k * k - 2 ); - } - }, - Quintic: { - In: function ( k ) { - return k * k * k * k * k; - }, - - Out: function ( k ) { - return --k * k * k * k * k + 1; - }, - - InOut: function ( k ) { - if ( ( k *= 2 ) < 1 ) return 0.5 * k * k * k * k * k; - return 0.5 * ( ( k -= 2 ) * k * k * k * k + 2 ); - } - }, - Sinusoidal: { - In: function ( k ) { - return 1 - Math.cos( k * Math.PI / 2 ); - }, - Out: function ( k ) { - return Math.sin( k * Math.PI / 2 ); - }, - InOut: function ( k ) { - return 0.5 * ( 1 - Math.cos( Math.PI * k ) ); - } - }, - - Exponential: { - In: function ( k ) { - return k === 0 ? 0 : Math.pow( 1024, k - 1 ); - }, - Out: function ( k ) { - return k === 1 ? 1 : 1 - Math.pow( 2, - 10 * k ); - }, - InOut: function ( k ) { - if ( k === 0 ) return 0; - if ( k === 1 ) return 1; - if ( ( k *= 2 ) < 1 ) return 0.5 * Math.pow( 1024, k - 1 ); - return 0.5 * ( - Math.pow( 2, - 10 * ( k - 1 ) ) + 2 ); - } - }, - Circular: { - In: function ( k ) { - return 1 - Math.sqrt( 1 - k * k ); - }, - Out: function ( k ) { - return Math.sqrt( 1 - ( --k * k ) ); - }, - InOut: function ( k ) { - if ( ( k *= 2 ) < 1) return - 0.5 * ( Math.sqrt( 1 - k * k) - 1); - return 0.5 * ( Math.sqrt( 1 - ( k -= 2) * k) + 1); - } - }, - Elastic: { - In: function ( k ) { - var s, a = 0.1, p = 0.4; - if ( k === 0 ) return 0; - if ( k === 1 ) return 1; - if ( !a || a < 1 ) { a = 1; s = p / 4; } - else s = p * Math.asin( 1 / a ) / ( 2 * Math.PI ); - return - ( a * Math.pow( 2, 10 * ( k -= 1 ) ) * Math.sin( ( k - s ) * ( 2 * Math.PI ) / p ) ); - }, - Out: function ( k ) { - var s, a = 0.1, p = 0.4; - if ( k === 0 ) return 0; - if ( k === 1 ) return 1; - if ( !a || a < 1 ) { a = 1; s = p / 4; } - else s = p * Math.asin( 1 / a ) / ( 2 * Math.PI ); - return ( a * Math.pow( 2, - 10 * k) * Math.sin( ( k - s ) * ( 2 * Math.PI ) / p ) + 1 ); - }, - InOut: function ( k ) { - var s, a = 0.1, p = 0.4; - if ( k === 0 ) return 0; - if ( k === 1 ) return 1; - if ( !a || a < 1 ) { a = 1; s = p / 4; } - else s = p * Math.asin( 1 / a ) / ( 2 * Math.PI ); - if ( ( k *= 2 ) < 1 ) return - 0.5 * ( a * Math.pow( 2, 10 * ( k -= 1 ) ) * Math.sin( ( k - s ) * ( 2 * Math.PI ) / p ) ); - return a * Math.pow( 2, -10 * ( k -= 1 ) ) * Math.sin( ( k - s ) * ( 2 * Math.PI ) / p ) * 0.5 + 1; - } - }, - Back: { - In: function ( k ) { - var s = 1.70158; - return k * k * ( ( s + 1 ) * k - s ); - }, - Out: function ( k ) { - var s = 1.70158; - return --k * k * ( ( s + 1 ) * k + s ) + 1; - }, - InOut: function ( k ) { - var s = 1.70158 * 1.525; - if ( ( k *= 2 ) < 1 ) return 0.5 * ( k * k * ( ( s + 1 ) * k - s ) ); - return 0.5 * ( ( k -= 2 ) * k * ( ( s + 1 ) * k + s ) + 2 ); - } - }, - Bounce: { - In: function ( k ) { - return 1 - KUTE.Easing.Bounce.Out( 1 - k ); - }, - Out: function ( k ) { - if ( k < ( 1 / 2.75 ) ) { - return 7.5625 * k * k; - } else if ( k < ( 2 / 2.75 ) ) { - return 7.5625 * ( k -= ( 1.5 / 2.75 ) ) * k + 0.75; - } else if ( k < ( 2.5 / 2.75 ) ) { - return 7.5625 * ( k -= ( 2.25 / 2.75 ) ) * k + 0.9375; - } else { - return 7.5625 * ( k -= ( 2.625 / 2.75 ) ) * k + 0.984375; - } - }, - InOut: function ( k ) { - if ( k < 0.5 ) return KUTE.Easing.Bounce.In( k * 2 ) * 0.5; - return KUTE.Easing.Bounce.Out( k * 2 - 1 ) * 0.5 + 0.5; - } - } -}; - - -// prevent mousewheel or touch events while tweening scroll -document.addEventListener('mousewheel', preventScroll, false); -document.addEventListener('touchstart', preventScroll, false); -function preventScroll(e){ - var data = document.body.getAttribute('data-tweening'); - if ( data && data === 'scroll' ) { - e.preventDefault(); - } -}; diff --git a/kute.js b/kute.js deleted file mode 100644 index 6ab5fe4..0000000 --- a/kute.js +++ /dev/null @@ -1,676 +0,0 @@ -// kute.js - The Light Tweening Engine | by dnp_theme -// http://themeforest.net/user/dnp_theme -// License - MIT - -// KUTE MAIN OBJECT -var KUTE = KUTE || ( function () { - var _tweens = []; - return { - getAll: function () { - return _tweens; - }, - removeAll: function () { - _tweens = []; - }, - add: function ( tween ) { - _tweens.push( tween ); - }, - remove: function ( tween ) { - var i = _tweens.indexOf( tween ); - if ( i !== -1 ) { - _tweens.splice( i, 1 ); - } - }, - update: function ( time ) { - if ( _tweens.length === 0 ) return false; - var i = 0; - time = time !== undefined ? time : window.performance.now(); - while ( i < _tweens.length ) { - if ( _tweens[ i ].update( time ) ) { - i++; - } else { - _tweens.splice( i, 1 ); - } - } - return true; - } - }; -} )(); - -KUTE.Animate = function( object, options ) { - - //element to animate - var el = typeof object === 'object' ? object : document.querySelector(object); - - //get true scroll container and current scroll - var bd = document.body, - htm = document.getElementsByTagName('HTML')[0], - sct = /webkit/i.test(navigator.userAgent) || document.compatMode == 'BackCompat' ? bd : htm, - crs = window.pageYOffset || sct.scrollTop; - - //determine if we're on IE or IE8 - var isIE = document.documentElement.classList.contains('ie'); - var isIE8 = document.documentElement.classList.contains('ie8'); - - //get element current style - var css = el.currentStyle || window.getComputedStyle(el); - - // default values - var ops = { - from : { - opacity : 1, // integer - width : '', // integer/px/% - height : '', // integer/px/% - position : {top:'', left:''}, // integer/% - translate : {x:0, y:0, z:0}, // integer only - rotate : {x:0, y:0, z:0}, // integer only - scale : 1, // integer only - scroll : crs, // integer only - }, - to : { - opacity : '', - width : '', - height : '', - position : {top:'',left:''}, - translate : {x:'', y:'', z:''}, - rotate : {x:'', y:'', z:''}, - scale : '', - scroll : '', - }, - easing : KUTE.Easing.Linear.None, //pe('linear') - delay : 0, - duration : 500, - start : null, // run function when tween starts - finish : null, // run function when tween finishes - special : null // run function while tween runing - }; - - //override the default values with option values - for (var x in options) { - if(typeof(options[x]) === 'object'){ - for (var y in options[x]){ - ops[x][y] = options[x][y]; - } - }else{ - ops[x] = options[x]; - } - } - - //create shorthand for all properties - var ofo = ops.from.opacity; - var ofw = ops.from.width; - var ofh = ops.from.height; - var oft = ops.from.position.top; - var ofl = ops.from.position.left; - var oftx = ops.from.translate.x; - var ofty = ops.from.translate.y; - var oftz = ops.from.translate.z; - var ofrx = ops.from.rotate.x; - var ofry = ops.from.rotate.y; - var ofrz = ops.from.rotate.z; - var ofs = ops.from.scale; - var ofsc = ops.from.scroll; - - var oto = ops.to.opacity; - var otw = ops.to.width; - var oth = ops.to.height; - var ott = ops.to.position.top; - var otl = ops.to.position.left; - var ottx = ops.to.translate.x; - var otty = ops.to.translate.y; - var ottz = ops.to.translate.z; - var otrx = ops.to.rotate.x; - var otry = ops.to.rotate.y; - var otrz = ops.to.rotate.z; - var ots = ops.to.scale; - var otsc = ops.to.scroll; - - - //process easing - var pes = typeof ops.easing === 'string' ? pe(ops.easing) : ops.easing; - - //from/initial values - var iwi = cv(ofw) ? truD(ofw)[0] : truD( css.width )[0]; // width - var ihe = cv(ofh) ? truD(ofh)[0] : truD( css.height )[0]; // height - - var ito = cv(oft) ? truD(oft)[0] : ''; // move - var ile = cv(ofl) ? truD(ofl)[0] : ''; - - var tr3d,tx,ty,tz,itx,ity,itz; // translate - if ( cv( ottx ) || cv( otty ) || cv( ottz ) ) { - itx = cv(oftx) ? truD(oftx)[0] : 0; - ity = cv(ofty) ? truD(ofty)[0] : 0; - itz = cv(oftz) ? truD(oftz)[0] : 0; - } else { - itx = ''; ity = ''; itz = ''; - } - - var irx = cv(ofrx) ? parseInt(ofrx) :''; //always deg - var iry = cv(ofry) ? parseInt(ofry) :''; - var irz = cv(ofrz) ? parseInt(ofrz) :''; - - var isa = parseFloat(ofs); // scale can be float - var iop = parseFloat(ofo); // opacity - var isc = parseInt(ofsc); // scroll - - - //target values - var wi = cv( otw ) ? truD(otw)[0] : ''; // width - var he = cv( oth ) ? truD(oth)[0] : ''; // height - - var top = cv(ott) ? truD(ott)[0] : ''; // pos top - var le = cv(otl) ? truD(otl)[0] : ''; //pos left - - if ( cv( ottx ) || cv( otty ) || cv( ottz ) ) { // translate 3d - tx = cv( ottx ) ? truD(ottx)[0] : 0; - ty = cv( otty ) ? truD(otty)[0] : 0; - tz = cv( ottz ) ? truD(ottz)[0] : 0; - } else { - tx = ''; ty = ''; tz = ''; - } - - var rx = cv( otrx ) ? parseInt(otrx) : ''; // rotate - var ry = cv( otry ) ? parseInt(otry) : ''; - var rz = cv( otrz ) ? parseInt(otrz) : ''; - - var sa = cv( ots ) ? parseFloat(ots) : ''; // scale values below 1 need to be reformated - var op = cv( oto ) ? parseFloat(oto) : ''; // opacity - var sc = cv( otsc ) ? parseInt(otsc) : ''; // scroll - - //check measurement unit - var wiu = cv( wi ) ? truD(otw)[1] : ''; // width - var heu = cv( he ) ? truD(oth)[1] : ''; // height - - var tou = cv( ott ) ? truD(ott)[1] : ''; // pos top - var leu = cv( otl ) ? truD(otl)[1] : ''; // pos left - - var txu = cv( tx ) ? truD(ottx)[1] : ''; // translate - var tyu = cv( ty ) ? truD(otty)[1] : ''; - var tzu = cv( tz ) ? truD(ottz)[1] : ''; - - animateTween(); - - var from = { w: iwi, h: ihe, t: ito, l: ile, scale: isa, trX: itx, trY: ity, trZ: itz, roX: irx, roY: iry, roZ: irz, opacity: iop, scroll: isc }; - var target = { w: wi, h: he, t: top, l: le, scale: sa, trX: tx, trY: ty, trZ: tz, roX: rx, roY: ry, roZ: rz, opacity: op, scroll: sc }; - - return new KUTE.Tween( from ) - .to( target, ops.duration ) - .delay( ops.delay ) - .easing( pes ) - .onStart( runStart ) - .onUpdate( - function () { - - //translate3d - if ( cv(tx) || cv(ty) || cv(tz) ) { - tr3d = 'translate3d(' + ((this.trX + txu) || 0) + ',' + ( (this.trY + tyu) || 0) + ',' + ( (this.trZ + tzu) || 0) + ')'; - } else { tr3d = ''; } - - //rotate - var roxt = cv(rx) ? ' rotateX(' + this.roX + 'deg)' : ''; - var royt = cv(ry) ? ' rotateY(' + this.roY + 'deg)' : ''; - var rozt = cv(rz) ? ' rotateZ(' + this.roZ + 'deg)' : ''; - - //scale - var sca = cv(sa) ? ' scale(' + this.scale + ')' : ''; - - //do a zoom for IE8 - if (isIE8 && cv(sa)) { - el.style.zoom = this.scale; - } - - //sum all transform - var perspective = parseInt(css.perspective)||''; - var transform = sca + tr3d + roxt + royt + rozt; - if ( cv(transform) ) { tr(transform,perspective) } - - - //dimensions width / height - if ( cv(wi) ) { el.style.width = this.w + wiu; } - if ( cv(he) ) { el.style.height = this.h + heu; } - - //position - if ( cv(top) ) { el.style.top = this.t + tou; } - if ( cv(le ) ) { el.style.left = this.l + leu; } - - // scrolling - if ( cv(sc) ) { sct.scrollTop = this.scroll; } - - //opacity - if ( cv(op) ) { el.style.opacity = (this.opacity).toFixed(2); } - - //do a filter opacity for IE8 - if (isIE8 && cv(op)) { - el.style.filter = "alpha(opacity=" + parseInt(100 * this.opacity) + ")" - } - - //run special function onUpdate - if ( ops.special && typeof ops.special === "function") { ops.special(); } - } - ) - .onComplete( runFinished ) - .start(); - - function animateTween(time) { - requestAnimationFrame( animateTween ); - KUTE.update(time); - } - - //callback when tween is finished - function runFinished() { - if ( ops.finish && typeof ops.finish === "function") { - ops.finish(); - } - if ( cv(otsc) ) { - document.body.removeAttribute('data-tweening') - } - } - - //callback when tween just started - function runStart() { - if ( ops.start && typeof ops.start === "function") { - ops.start(); - } - //fix the scrolling being interrupted via mousewheel - if ( cv(otsc) ) { - if ( !document.body.getAttribute('data-tweening') && document.body.getAttribute('data-tweening') !== 'scroll' ) - document.body.setAttribute('data-tweening','scroll'); - } - } - - /* Process values utils - ----------------------------*/ - - //process easing 31 - function pe(e) { - if ( e === 'linear' ) return KUTE.Easing.Linear.None; - if ( e === 'quadraticIn' ) return KUTE.Easing.Quadratic.In; - if ( e === 'quadraticOut' ) return KUTE.Easing.Quadratic.Out; - if ( e === 'quadraticInOut' ) return KUTE.Easing.Quadratic.InOut; - if ( e === 'cubicIn' ) return KUTE.Easing.Cubic.In; - if ( e === 'cubicOut' ) return KUTE.Easing.Cubic.Out; - if ( e === 'cubicInOut' ) return KUTE.Easing.Cubic.InOut; - if ( e === 'quarticIn' ) return KUTE.Easing.Quartic.In; - if ( e === 'quarticOut' ) return KUTE.Easing.Quartic.Out; - if ( e === 'quarticInOut' ) return KUTE.Easing.Quartic.InOut; - if ( e === 'quinticIn' ) return KUTE.Easing.Quintic.In; - if ( e === 'quinticOut' ) return KUTE.Easing.Quintic.Out; - if ( e === 'quinticInOut' ) return KUTE.Easing.Quintic.InOut; - if ( e === 'sinusoidalIn' ) return KUTE.Easing.Sinusoidal.In; - if ( e === 'sinusoidalOut' ) return KUTE.Easing.Sinusoidal.Out; - if ( e === 'sinusoidalInOut' ) return KUTE.Easing.Sinusoidal.InOut; - if ( e === 'exponentialIn' ) return KUTE.Easing.Exponential.In; - if ( e === 'exponentialOut' ) return KUTE.Easing.Exponential.Out; - if ( e === 'exponentialInOut' ) return KUTE.Easing.Exponential.InOut; - if ( e === 'circularIn' ) return KUTE.Easing.Circular.In; - if ( e === 'circularOut' ) return KUTE.Easing.Circular.Out; - if ( e === 'circularInOut' ) return KUTE.Easing.Circular.InOut; - if ( e === 'elasticIn' ) return KUTE.Easing.Elastic.In; - if ( e === 'elasticOut' ) return KUTE.Easing.Elastic.Out; - if ( e === 'elasticInOut' ) return KUTE.Easing.Elastic.InOut; - if ( e === 'backIn' ) return KUTE.Easing.Back.In; - if ( e === 'backOut' ) return KUTE.Easing.Back.Out; - if ( e === 'backInOut' ) return KUTE.Easing.Back.InOut; - if ( e === 'bounceIn' ) return KUTE.Easing.Bounce.In; - if ( e === 'bounceOut' ) return KUTE.Easing.Bounce.Out; - if ( e === 'bounceInOut' ) return KUTE.Easing.Bounce.InOut; - //default - return KUTE.Easing.Exponential.InOut; - } - - // value checker - function cv(v) { - if ( v !== undefined && v !== '' && v !== 'NaN' ) return true; - } - - // get true w/h - function truD(d){ - var v,u; - if (/px/i.test(d)) { - u = 'px'; v = parseInt( d ); - } else if (/%/i.test(d)) { - u = '%'; v = parseInt( d ); - } else { - v = d; u = 'px'; - } - return [v,u]; - } - - // process transform - function tr(p,pp) { - el.style.webkitTransform = p; - el.style.MozTransform = p; - el.style.msTransform = (cv(pp)?'perspective('+pp+'px) ':'') + p; - el.style.Transform = p; - } -}; - -KUTE.Tween = function ( object ) { - - var _object = object; - var _valuesStart = {}; - var _valuesEnd = {}; - var _valuesStartRepeat = {}; - var _duration = 700; - var _isPlaying = false; - var _delayTime = 0; - var _startTime = null; - var _easingFunction = KUTE.Easing.Linear.None; - var _onStartCallback = null; - var _onStartCallbackFired = false; - var _onUpdateCallback = null; - var _onCompleteCallback = null; - var _onStopCallback = null; - - // Set all starting values present on the target object - for ( var field in object ) { - _valuesStart[ field ] = parseFloat(object[field], 10); - } - - this.to = function ( properties, duration ) { - - if ( duration !== undefined ) { - _duration = duration; - } - - _valuesEnd = properties; - return this; - }; - - this.start = function ( time ) { - - KUTE.add( this ); - _isPlaying = true; - _onStartCallbackFired = false; - _startTime = time !== undefined ? time : window.performance.now(); - _startTime += _delayTime; - - for ( var property in _valuesEnd ) { - // check if an Array was provided as property value - if ( _valuesEnd[ property ] instanceof Array ) { - if ( _valuesEnd[ property ].length === 0 ) { - continue; - } - - // create a local copy of the Array with the start value at the front - _valuesEnd[ property ] = [ _object[ property ] ].concat( _valuesEnd[ property ] ); - } - - if( ( _valuesEnd[ property ] instanceof Array ) === false ) { - _valuesEnd[ property ] *= 1.0; // Ensures we're using numbers, not strings - } - - _valuesStart[ property ] = _object[ property ]; - - if( ( _valuesStart[ property ] instanceof Array ) === false ) { - _valuesStart[ property ] *= 1.0; // Ensures we're using numbers, not strings - } - } - return this; - }; - - this.stop = function () { - if ( !_isPlaying ) { - return this; - } - - KUTE.remove( this ); - _isPlaying = false; - - if ( _onStopCallback !== null ) { - _onStopCallback.call( _object ); - } - - return this; - - }; - - this.delay = function ( amount ) { - _delayTime = amount; - return this; - }; - - this.easing = function ( easing ) { - _easingFunction = easing; - return this; - }; - - this.onStart = function ( callback ) { - _onStartCallback = callback; - return this; - }; - - this.onUpdate = function ( callback ) { - _onUpdateCallback = callback; - return this; - }; - - this.onComplete = function ( callback ) { - _onCompleteCallback = callback; - return this; - }; - - this.onStop = function ( callback ) { - _onStopCallback = callback; - return this; - }; - - this.update = function ( time ) { - var property; - - if ( time < _startTime ) { - return true; - } - - if ( _onStartCallbackFired === false ) { - if ( _onStartCallback !== null ) { - _onStartCallback.call( _object ); - } - _onStartCallbackFired = true; - } - - var elapsed = ( time - _startTime ) / _duration; - elapsed = elapsed > 1 ? 1 : elapsed; - var value = _easingFunction( elapsed ); - - for ( property in _valuesEnd ) { - - var start = _valuesStart[ property ] || 0; - var end = _valuesEnd[ property ]; - - // Parses relative end values with start as base (e.g.: +10, -3) - if ( typeof(end) === "string" ) { - end = start + parseFloat(end, 10); - } - - // protect against non numeric properties. - if ( typeof(end) === "number" ) { - _object[ property ] = start + ( end - start ) * value; - } - } - - if ( _onUpdateCallback !== null ) { - _onUpdateCallback.call( _object, value ); - } - - if ( elapsed == 1 ) { - - if ( _onCompleteCallback !== null ) { - _onCompleteCallback.call( _object ); - } - return false; - } - return true; - }; -}; - -KUTE.Easing = { - Linear: { - None: function ( k ) { - return k; - } - }, - Quadratic: { - In: function ( k ) { - return k * k; - }, - - Out: function ( k ) { - return k * ( 2 - k ); - }, - - InOut: function ( k ) { - if ( ( k *= 2 ) < 1 ) return 0.5 * k * k; - return - 0.5 * ( --k * ( k - 2 ) - 1 ); - } - }, - Cubic: { - In: function ( k ) { - return k * k * k; - }, - Out: function ( k ) { - return --k * k * k + 1; - }, - InOut: function ( k ) { - if ( ( k *= 2 ) < 1 ) return 0.5 * k * k * k; - return 0.5 * ( ( k -= 2 ) * k * k + 2 ); - } - }, - Quartic: { - In: function ( k ) { - return k * k * k * k; - }, - Out: function ( k ) { - return 1 - ( --k * k * k * k ); - }, - InOut: function ( k ) { - if ( ( k *= 2 ) < 1) return 0.5 * k * k * k * k; - return - 0.5 * ( ( k -= 2 ) * k * k * k - 2 ); - } - }, - Quintic: { - In: function ( k ) { - return k * k * k * k * k; - }, - - Out: function ( k ) { - return --k * k * k * k * k + 1; - }, - - InOut: function ( k ) { - if ( ( k *= 2 ) < 1 ) return 0.5 * k * k * k * k * k; - return 0.5 * ( ( k -= 2 ) * k * k * k * k + 2 ); - } - }, - Sinusoidal: { - In: function ( k ) { - return 1 - Math.cos( k * Math.PI / 2 ); - }, - Out: function ( k ) { - return Math.sin( k * Math.PI / 2 ); - }, - InOut: function ( k ) { - return 0.5 * ( 1 - Math.cos( Math.PI * k ) ); - } - }, - - Exponential: { - In: function ( k ) { - return k === 0 ? 0 : Math.pow( 1024, k - 1 ); - }, - Out: function ( k ) { - return k === 1 ? 1 : 1 - Math.pow( 2, - 10 * k ); - }, - InOut: function ( k ) { - if ( k === 0 ) return 0; - if ( k === 1 ) return 1; - if ( ( k *= 2 ) < 1 ) return 0.5 * Math.pow( 1024, k - 1 ); - return 0.5 * ( - Math.pow( 2, - 10 * ( k - 1 ) ) + 2 ); - } - }, - Circular: { - In: function ( k ) { - return 1 - Math.sqrt( 1 - k * k ); - }, - Out: function ( k ) { - return Math.sqrt( 1 - ( --k * k ) ); - }, - InOut: function ( k ) { - if ( ( k *= 2 ) < 1) return - 0.5 * ( Math.sqrt( 1 - k * k) - 1); - return 0.5 * ( Math.sqrt( 1 - ( k -= 2) * k) + 1); - } - }, - Elastic: { - In: function ( k ) { - var s, a = 0.1, p = 0.4; - if ( k === 0 ) return 0; - if ( k === 1 ) return 1; - if ( !a || a < 1 ) { a = 1; s = p / 4; } - else s = p * Math.asin( 1 / a ) / ( 2 * Math.PI ); - return - ( a * Math.pow( 2, 10 * ( k -= 1 ) ) * Math.sin( ( k - s ) * ( 2 * Math.PI ) / p ) ); - }, - Out: function ( k ) { - var s, a = 0.1, p = 0.4; - if ( k === 0 ) return 0; - if ( k === 1 ) return 1; - if ( !a || a < 1 ) { a = 1; s = p / 4; } - else s = p * Math.asin( 1 / a ) / ( 2 * Math.PI ); - return ( a * Math.pow( 2, - 10 * k) * Math.sin( ( k - s ) * ( 2 * Math.PI ) / p ) + 1 ); - }, - InOut: function ( k ) { - var s, a = 0.1, p = 0.4; - if ( k === 0 ) return 0; - if ( k === 1 ) return 1; - if ( !a || a < 1 ) { a = 1; s = p / 4; } - else s = p * Math.asin( 1 / a ) / ( 2 * Math.PI ); - if ( ( k *= 2 ) < 1 ) return - 0.5 * ( a * Math.pow( 2, 10 * ( k -= 1 ) ) * Math.sin( ( k - s ) * ( 2 * Math.PI ) / p ) ); - return a * Math.pow( 2, -10 * ( k -= 1 ) ) * Math.sin( ( k - s ) * ( 2 * Math.PI ) / p ) * 0.5 + 1; - } - }, - Back: { - In: function ( k ) { - var s = 1.70158; - return k * k * ( ( s + 1 ) * k - s ); - }, - Out: function ( k ) { - var s = 1.70158; - return --k * k * ( ( s + 1 ) * k + s ) + 1; - }, - InOut: function ( k ) { - var s = 1.70158 * 1.525; - if ( ( k *= 2 ) < 1 ) return 0.5 * ( k * k * ( ( s + 1 ) * k - s ) ); - return 0.5 * ( ( k -= 2 ) * k * ( ( s + 1 ) * k + s ) + 2 ); - } - }, - Bounce: { - In: function ( k ) { - return 1 - KUTE.Easing.Bounce.Out( 1 - k ); - }, - Out: function ( k ) { - if ( k < ( 1 / 2.75 ) ) { - return 7.5625 * k * k; - } else if ( k < ( 2 / 2.75 ) ) { - return 7.5625 * ( k -= ( 1.5 / 2.75 ) ) * k + 0.75; - } else if ( k < ( 2.5 / 2.75 ) ) { - return 7.5625 * ( k -= ( 2.25 / 2.75 ) ) * k + 0.9375; - } else { - return 7.5625 * ( k -= ( 2.625 / 2.75 ) ) * k + 0.984375; - } - }, - InOut: function ( k ) { - if ( k < 0.5 ) return KUTE.Easing.Bounce.In( k * 2 ) * 0.5; - return KUTE.Easing.Bounce.Out( k * 2 - 1 ) * 0.5 + 0.5; - } - } -}; - - -// prevent mousewheel or touch events while tweening scroll -document.addEventListener('mousewheel', preventScroll, false); -document.addEventListener('touchstart', preventScroll, false); -function preventScroll(e){ - var data = document.body.getAttribute('data-tweening'); - if ( data && data === 'scroll' ) { - e.preventDefault(); - } -}; diff --git a/opacityProperty.html b/opacityProperty.html new file mode 100644 index 0000000..60fba38 --- /dev/null +++ b/opacityProperty.html @@ -0,0 +1,153 @@ + + + + + + + + + + + + + + KUTE.js Opacity Property + + + + + + + + + + + + + + +
+ + + +
+

Opacity Property

+

The component that animates the CSS opacity property of a target Element on most browsers.

+
+ +
+
+
+
+

Overview

+

Animate the opacity property of a target element.

+
+
+

The KUTE.js Opacity Property component enables animation for the opacity CSS property of an Element target on most browsers.

+

In most cases, the best presentatation can be offered with a nice and smooth fade animation, with opacity going from 0% all the way up to to 100%.

+ +

While some components like HTML Attributes and Filter Effects do provide some + similar functionality for specific Element types, this component covers all types of elements and is supported on a wide range of modern + and legacy browsers alike.

+
+
+
+
+ +
+ +

Example

+ +
// fade out
+let fadeOutTween = KUTE.to('selector1',{opacity:0}).start()
+
+// fade in
+let fadeInTween = KUTE.to('selector1',{opacity:1}).start()
+
+ +
+
+ + + +
+ +
+ +

Notes

+
    +
  • This demo should work with IE9+ browsers.
  • +
  • Support for the specific IE8 filter:alpha(opacity=50) have been dropped.
  • +
  • Early implementations with vendor preffixes such as -o-opacity, -moz-opacity or -ms-opacity are not supported.
  • +
  • The component is an essential part in ALL KUTE.js distributions.
  • +
+
+ + + + +
+ + + + + + + + + + + + + + diff --git a/performance-matrix.html b/performance-matrix.html new file mode 100644 index 0000000..5448109 --- /dev/null +++ b/performance-matrix.html @@ -0,0 +1,212 @@ + + + + + + + + + + + + + + KUTE.js | Transform Matrix Performance Testing Page + + + + + +
+ +
+ +
+ +
+

These tests are only for modern browsers. In Webkit browsers like Google Chrome and Opera you can enable the FPS metter in developer tools, here's how.

+

Please know that a local copy of this page will outperform the live site demo on Google Chrome, the reason is unknown.

+ + +
+ +
+ + + + + + + + + + + + diff --git a/performance-transform.html b/performance-transform.html new file mode 100644 index 0000000..0b4b697 --- /dev/null +++ b/performance-transform.html @@ -0,0 +1,213 @@ + + + + + + + + + + + + + +KUTE.js | Regular Transform Performance Testing Page + + + + + +
+ + +
+ +
+ +
+ +

These tests are only for modern browsers. In Webkit browsers like Google Chrome and Opera you can enable the FPS metter in developer tools, here's how.

+

Please know that a local copy of this page will outperform the live site demo on Google Chrome, the reason is unknown.

+ +
+ +
+ + + + + + + + + + diff --git a/performance.html b/performance.html new file mode 100644 index 0000000..d91aeb4 --- /dev/null +++ b/performance.html @@ -0,0 +1,244 @@ + + + + + + + + + + + + + +KUTE.js | Performance Testing Page + + + + + + + +
+ +
+ + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+

These tests are only for modern browsers. In Webkit browsers like Google Chrome and Opera you can enable the FPS metter in developer tools, here's how.

+

Please know that a local copy of this page will outperform the live site demo on Google Chrome, the reason is unknown.

+ + +
+ +
+ + + + + + + + + + + + + + + + + + diff --git a/progress.html b/progress.html new file mode 100644 index 0000000..1d08be7 --- /dev/null +++ b/progress.html @@ -0,0 +1,219 @@ + + + + + + + + + + + + + + + + KUTE.js Using Update Functions | Javascript Animation Engine + + + + + + + + + + + + + + +
+ + + +
+

Tween Progress Control

+

The handy tool to manually update a tween via an Input slider.

+
+ +
+
+
+
+

Overview

+

Create a tween object and link it to a range slider Input. Some stuff happening.

+
+
+

The Progress Bar can be a handy tool that enables you to manually update your tween animation, just in case there is a little + detail you want to make it right.

+

KUTE.js object exposes all required methods in order for it to work, so why not try to do something fun? How about control tween progress? So let's make a quick tool:

+
    +
  • We need an <input type="range" min="0" max="1" step="0.00001" /> with these exact min, max and step attributes
  • +
  • Now we need a tween object, let's just do a svg morph for instance, but make sure you use KUTE.fromTo() method, the others don't prepare start values for the tween object
  • +
  • We also need to make sure nothing controls the progress except the range input, so don't use start() or pause() methods at all, as well as repeat and / or yoyo options
  • +
  • Next we attach an input event handler to update the tween progress by using the KUTE.update function, which is the step function triggered on every requestAnimationFrame tick
  • +
+
+
+ +
+
+ +
+ + +

A very basic code sample will look like this:

+ + +
// the range slider
+var rangeSlider = document.querySelector('input[type="range"');
+
+// basic morph, only fromTo and allFromTo should work
+var morphTween = KUTE.to('#rectangle', { path: '#star' } );  
+
+// initialize a progressBar for your tween
+var progressBar = new KUTE.ProgressBar(rangeSlider,morphTween)
+
+// also start animation when Element is clicked
+document.getElementById('rectangle').addEventListener('click',function(){
+!morphTween.playing && morphTween.start()
+})
+
+ +

And now let's see the code in action:

+
+
+ + 0% +
+ + + + +
+ +

We might argue that we want to use other methods in combination with this method, or use this method while animations are running, but there are other libraries out there that can do that already. This example here is just to showcase KUTE.js can do this too.

+

Note that this tool is not included in the official distribution file.

+ +
+ + + + +
+ + + + + + + + + + + + + diff --git a/scrollProperty.html b/scrollProperty.html new file mode 100644 index 0000000..77e16dc --- /dev/null +++ b/scrollProperty.html @@ -0,0 +1,219 @@ + + + + + + + + + + + + + + KUTE.js Scroll Property + + + + + + + + + + + + + + + +
+ + + +
+

Scroll Property

+

The component that animates the vertical scrolling of a target Element as well as the <window> on most browsers.

+
+ +
+
+
+
+

Overview

+

The fully reworked component for vertical scrolling animation is an essential part of KUTE.js.

+
+
+

The KUTE.js Scroll Property component enables animation for the vertical scroll of a target Element or the + <window> on most browsers.

+

The vertical scrollling animation is a popular choice to spice up the in-page navigation, and most websites use a Back To Top button for + scrolling all the way back to top. For this reason, our component doesn't support horizontal scrolling animation.

+

The component also uses passive event option for best possible performance along with other improvements like removed the need to use + additional CSS or the use of data-scrolling attribute on the <body> element.

+

On animation start, the component will lock down all possible pointer events of the animation target to + avoid any unwanted animation glitches.

+
+
+
+
+ +
+ +

Example

+ +
// all websites to top button
+KUTE.to(window,{scroll:0}).start()
+
+// scroll to a certain element
+KUTE.to(window,{scroll: document.getElementById('myElement').offsetTop }).start()
+
+// scroll to top an overflowing target element
+KUTE.to('#myElement',{scroll: 0 }).start()
+
+ +
+
+

KUTE.js Scroll Property

+

Leverage agile frameworks to provide a robust synopsis for high level overviews. Iterative approaches to corporate strategy + foster collaborative thinking to further the overall value proposition. Organically grow the holistic world view of disruptive + innovation via workplace diversity and empowerment.

+ +

Bring to the table win-win survival strategies to ensure proactive domination. At the end of the day, going forward, a new normal + that has evolved from generation X is on the runway heading towards a streamlined cloud solution. User generated content in + real-time will have multiple touchpoints for offshoring.

+ +

Capitalize on low hanging fruit to identify a ballpark value added activity to beta test. Override the digital divide with + additional clickthroughs from DevOps. Nanotechnology immersion along the information highway will close the loop on focusing + solely on the bottom line.

+ +

Second Sample Heading

+ +

Podcasting operational change management inside of workflows to establish a framework. Taking seamless key performance indicators + offline to maximise the long tail. Keeping your eye on the ball while performing a deep dive on the start-up mentality to derive + convergence on cross-platform integration.

+ +

Collaboratively administrate empowered markets via plug-and-play networks. Dynamically procrastinate B2C users after installed + base benefits. Dramatically visualize customer directed convergence without revolutionary ROI.

+
+ +
+ +
+
+

scroll-behavior: smooth

+

Leverage agile frameworks to provide a robust synopsis for high level overviews. Iterative approaches to corporate strategy + foster collaborative thinking to further the overall value proposition. Organically grow the holistic world view of disruptive + innovation via workplace diversity and empowerment.

+ +

Bring to the table win-win survival strategies to ensure proactive domination. At the end of the day, going forward, a new normal + that has evolved from generation X is on the runway heading towards a streamlined cloud solution. User generated content in + real-time will have multiple touchpoints for offshoring.

+ +

Capitalize on low hanging fruit to identify a ballpark value added activity to beta test. Override the digital divide with + additional clickthroughs from DevOps. Nanotechnology immersion along the information highway will close the loop on focusing + solely on the bottom line.

+ +

Second Sample Heading

+ +

Podcasting operational change management inside of workflows to establish a framework. Taking seamless key performance indicators + offline to maximise the long tail. Keeping your eye on the ball while performing a deep dive on the start-up mentality to derive + convergence on cross-platform integration.

+ +

Collaboratively administrate empowered markets via plug-and-play networks. Dynamically procrastinate B2C users after installed + base benefits. Dramatically visualize customer directed convergence without revolutionary ROI.

+
+
+ Bottom +
+
+ Top +
+
+

The above shows a comparison of the Scroll Property component with the scroll-behavior: smooth CSS; while this browser + feature shows excellent performance it lacks the flexibility and support for legacy browsers.

+ +

Notes

+
    +
  • The Scroll Property component will lock down any pointer event such as click, touch, or any other event of the target on + animation start to prevent any kind of animation glitches, so make sure to filter your tweens, perhaps you can take a look at the + scrollProperty.js sample code.
  • +
  • The scroll animation is not as smooth as with transform animations, it has no access at sub-pixel level, but you can play around + with various easing functions and durations to find the best possible outcome.
  • +
  • All pages in this documentation have a <a>Back to top</a> button at the bottom, just in case you didn't notice, but + only on this page scrollProperty component is used.
  • +
  • The component is only bundled with the demo/src/kute-extra.js file and not in the official build. That is thanks to + scroll-behavior, but you can include this component in your custom builds to enable scrolling for legacy browsers.
  • +
+ +
+ + + + +
+ + + + + + + + + + + + + + diff --git a/shadowProperties.html b/shadowProperties.html new file mode 100644 index 0000000..ba14f32 --- /dev/null +++ b/shadowProperties.html @@ -0,0 +1,193 @@ + + + + + + + + + + + + + + KUTE.js Shadow Properties + + + + + + + + + + + + + + +
+ + + +
+

Shadow Properties

+

The component that animates shadow properties of a specific target element on most browsers.

+
+ +
+
+
+
+

Overview

+

Animate the shadow properties of a target element.

+
+
+

The KUTE.js Shadow Properties component enables animation for the text-shadow CSS property of text elements + as well as the box-shadow property of any element on most browsers.

+

The functionality was developed while writing a guide on how to extend KUTE.js a couple of years ago and is now a fully featured component.

+

The component uses px as unit for the shadow parameters, can animate the color of the shadow and can also handle the inset shadow parameter + of the boxShadow property.

+

OK let's have a look at a couple of quick examples:

+
+
+
+
+ +
+ +

Box Shadow

+ +
// tween to a string value
+var myBSTween1 = KUTE.to('selector', {boxShadow: '10px 10px #069'}).start();
+
+// or a fromTo with string and array, hex and rgb
+var myBSTween2 = KUTE.fromTo(
+'selector',                         // target
+{boxShadow: [0, 0, 0, '#069']},     // from
+{boxShadow: '5px 5px rgb(0,0,0)'})  // to
+.start();
+
+// maybe you want to animate an inset boxShadow?
+var myBSTween3 = KUTE.fromTo(
+'selector',                                // target
+{boxShadow: [5, 5, 0, '#069', 'inset']},   // from
+{boxShadow: '0px 0px rgb(0,0,0)'})         // to
+.start();
+
+ +
+
+
+ Start +
+
+ +

Text Shadow

+ +
// tween to a string value
+var myTSTween1 = KUTE.to('selector', {textShadow: '10px 10px #069'}).start();
+
+// or a fromTo with string and array, hex and rgb
+var myTSTween2 = KUTE.fromTo(
+'selector',                          // target
+{textShadow: [0, 0, 0, '#069']},     // from
+{textShadow: '5px 5px rgb(0,0,0)'})  // to
+.start();
+
+ +
+
Sample Text
+ +
+ Start +
+
+ +

Notes

+
    +
  • The component will NOT handle multiple shadows per target at the same time.
  • +
  • The component features a solid value processing script, it can handle a great deal of combinations of input values.
  • +
  • I highly recommend that you check the boxShadow.js for more insight.
  • +
  • This component is bundled with the demo/src/kute-extra.js file.
  • +
+ +
+ + + + +
+ + + + + + + + + + + + + + + diff --git a/src/kute-base.js b/src/kute-base.js new file mode 100644 index 0000000..cbc1834 --- /dev/null +++ b/src/kute-base.js @@ -0,0 +1,908 @@ +/*! +* KUTE.js Base v2.2.2 (http://thednp.github.io/kute.js) +* Copyright 2015-2021 © thednp +* Licensed under MIT (https://github.com/thednp/kute.js/blob/master/LICENSE) +*/ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : + typeof define === 'function' && define.amd ? define(factory) : + (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.KUTE = factory()); +})(this, (function () { 'use strict'; + + /** + * The KUTE.js Execution Context + */ + var KEC = {}; + + var Tweens = []; + + var gl0bal; + + if (typeof global !== 'undefined') { gl0bal = global; } + else if (typeof window !== 'undefined') { gl0bal = window.self; } + else { gl0bal = {}; } + + var globalObject = gl0bal; + + // KUTE.js INTERPOLATE FUNCTIONS + // ============================= + var interpolate = {}; + + // schedule property specific function on animation start + // link property update function to KUTE.js execution context + var onStart = {}; + + // Include a performance.now polyfill. + // source https://github.com/tweenjs/tween.js/blob/master/src/Now.ts + var performanceNow; + + // In node.js, use process.hrtime. + // eslint-disable-next-line + // @ts-ignore + if (typeof self === 'undefined' && typeof process !== 'undefined' && process.hrtime) { + performanceNow = function () { + // eslint-disable-next-line + // @ts-ignore + var time = process.hrtime(); + + // Convert [seconds, nanoseconds] to milliseconds. + return time[0] * 1000 + time[1] / 1000000; + }; + } else if (typeof self !== 'undefined' && self.performance !== undefined && self.performance.now !== undefined) { + // In a browser, use self.performance.now if it is available. + // This must be bound, because directly assigning this function + // leads to an invocation exception in Chrome. + performanceNow = self.performance.now.bind(self.performance); + } else if (typeof Date !== 'undefined' && Date.now) { + // Use Date.now if it is available. + performanceNow = Date.now; + } else { + // Otherwise, use 'new Date().getTime()'. + performanceNow = function () { return new Date().getTime(); }; + } + + var now = performanceNow; + + var Time = {}; + Time.now = now; + + // eslint-disable-next-line import/no-mutable-exports -- impossible to satisfy + var Tick = 0; + + /** + * + * @param {number | Date} time + */ + var Ticker = function (time) { + var i = 0; + while (i < Tweens.length) { + if (Tweens[i].update(time)) { + i += 1; + } else { + Tweens.splice(i, 1); + } + } + Tick = requestAnimationFrame(Ticker); + }; + + // stop requesting animation frame + function stop() { + setTimeout(function () { // re-added for #81 + if (!Tweens.length && Tick) { + cancelAnimationFrame(Tick); + Tick = null; + Object.keys(onStart).forEach(function (obj) { + if (typeof (onStart[obj]) === 'function') { + if (KEC[obj]) { delete KEC[obj]; } + } else { + Object.keys(onStart[obj]).forEach(function (prop) { + if (KEC[prop]) { delete KEC[prop]; } + }); + } + }); + + Object.keys(interpolate).forEach(function (i) { + if (KEC[i]) { delete KEC[i]; } + }); + } + }, 64); + } + + // render update functions + // ======================= + var Render = { + Tick: Tick, Ticker: Ticker, Tweens: Tweens, Time: Time, + }; + Object.keys(Render).forEach(function (blob) { + if (!KEC[blob]) { + KEC[blob] = blob === 'Time' ? Time.now : Render[blob]; + } + }); + + globalObject._KUTE = KEC; + + var defaultOptions = { + duration: 700, + delay: 0, + easing: 'linear', + repeat: 0, + repeatDelay: 0, + yoyo: false, + resetStart: false, + offset: 0, + }; + + // link properties to interpolate functions + var linkProperty = {}; + + // schedule property specific function on animation complete + var onComplete = {}; + + var Objects = { + defaultOptions: defaultOptions, + linkProperty: linkProperty, + onStart: onStart, + onComplete: onComplete, + }; + + // util - a general object for utils like rgbToHex, processEasing + var Util = {}; + + var connect = {}; + /** @type {KUTE.TweenBase | KUTE.Tween | KUTE.TweenExtra} */ + connect.tween = null; + connect.processEasing = null; + + // Select Robert Penner's Easing Functions + // updated for ESLint + var Easing = { + /** @type {KUTE.easingFunction} */ + linear: function (t) { return t; }, + /** @type {KUTE.easingFunction} */ + easingQuadraticIn: function (t) { return t * t; }, + /** @type {KUTE.easingFunction} */ + easingQuadraticOut: function (t) { return t * (2 - t); }, + /** @type {KUTE.easingFunction} */ + easingQuadraticInOut: function (t) { return (t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t); }, + /** @type {KUTE.easingFunction} */ + easingCubicIn: function (t) { return t * t * t; }, + /** @type {KUTE.easingFunction} */ + easingCubicOut: function (t0) { var t = t0 - 1; return t * t * t + 1; }, + /** @type {KUTE.easingFunction} */ + easingCubicInOut: function (t) { return (t < 0.5 ? 4 * t * t * t : (t - 1) * (2 * t - 2) * (2 * t - 2) + 1); }, + /** @type {KUTE.easingFunction} */ + easingCircularIn: function (t) { return -(Math.sqrt(1 - (t * t)) - 1); }, + /** @type {KUTE.easingFunction} */ + easingCircularOut: function (t0) { var t = t0 - 1; return Math.sqrt(1 - t * t); }, + /** @type {KUTE.easingFunction} */ + easingCircularInOut: function (t0) { + var t = t0 * 2; + if (t < 1) { return -0.5 * (Math.sqrt(1 - t * t) - 1); } + t -= 2; return 0.5 * (Math.sqrt(1 - t * t) + 1); + }, + /** @type {KUTE.easingFunction} */ + easingBackIn: function (t) { var s = 1.70158; return t * t * ((s + 1) * t - s); }, + /** @type {KUTE.easingFunction} */ + easingBackOut: function (t0) { + var s = 1.70158; + var t = t0 - 1; + return t * t * ((s + 1) * t + s) + 1; + }, + /** @type {KUTE.easingFunction} */ + easingBackInOut: function (t0) { + var s = 1.70158 * 1.525; + var t = t0 * 2; + if (t < 1) { return 0.5 * (t * t * ((s + 1) * t - s)); } + t -= 2; return 0.5 * (t * t * ((s + 1) * t + s) + 2); + }, + }; + + /** + * Returns a valid `easingFunction`. + * + * @param {KUTE.easingFunction | string} fn function name or constructor name + * @returns {KUTE.easingFunction} a valid easing function + */ + function processEasing(fn) { + if (typeof fn === 'function') { + return fn; + } if (typeof Easing[fn] === 'function') { + return Easing[fn]; // regular Robert Penner Easing Functions + } + return Easing.linear; + } + + connect.processEasing = processEasing; + + /** + * KUTE.add(Tween) + * + * @param {KUTE.Tween} tw a new tween to add + */ + var add = function (tw) { return Tweens.push(tw); }; + + /** + * KUTE.remove(Tween) + * + * @param {KUTE.Tween} tw a new tween to add + */ + var remove = function (tw) { + var i = Tweens.indexOf(tw); + if (i !== -1) { Tweens.splice(i, 1); } + }; + + /** + * KUTE.add(Tween) + * + * @return {KUTE.Tween[]} tw a new tween to add + */ + var getAll = function () { return Tweens; }; + + /** + * KUTE.removeAll() + */ + var removeAll = function () { Tweens.length = 0; }; + + // all supported properties + var supportedProperties = {}; + + /** + * linkInterpolation + * @this {KUTE.Tween} + */ + function linkInterpolation() { + var this$1$1 = this; + // DON'T change + Object.keys(linkProperty).forEach(function (component) { + var componentLink = linkProperty[component]; + var componentProps = supportedProperties[component]; + + Object.keys(componentLink).forEach(function (fnObj) { + if (typeof (componentLink[fnObj]) === 'function' // ATTR, colors, scroll, boxModel, borderRadius + && Object.keys(this$1$1.valuesEnd).some(function (i) { return (componentProps && componentProps.includes(i)) + || (i === 'attr' && Object.keys(this$1$1.valuesEnd[i]).some(function (j) { return componentProps && componentProps.includes(j); })); })) { + if (!KEC[fnObj]) { KEC[fnObj] = componentLink[fnObj]; } + } else { + Object.keys(this$1$1.valuesEnd).forEach(function (prop) { + var propObject = this$1$1.valuesEnd[prop]; + if (propObject instanceof Object) { + Object.keys(propObject).forEach(function (i) { + if (typeof (componentLink[i]) === 'function') { // transformCSS3 + if (!KEC[i]) { KEC[i] = componentLink[i]; } + } else { + Object.keys(componentLink[fnObj]).forEach(function (j) { + if (componentLink[i] && typeof (componentLink[i][j]) === 'function') { // transformMatrix + if (!KEC[j]) { KEC[j] = componentLink[i][j]; } + } + }); + } + }); + } + }); + } + }); + }); + } + + var internals = { + add: add, + remove: remove, + getAll: getAll, + removeAll: removeAll, + stop: stop, + linkInterpolation: linkInterpolation, + }; + + /** + * selector + * + * A selector utility for KUTE.js. + * + * @param {KUTE.selectorType} el target(s) or string selector + * @param {boolean | number} multi when true returns an array/collection of elements + * @returns {Element | Element[] | null} + */ + function selector(el, multi) { + try { + var requestedElem; + var itemsArray; + if (multi) { + itemsArray = el instanceof Array && el.every(function (x) { return x instanceof Element; }); + requestedElem = el instanceof HTMLCollection || el instanceof NodeList || itemsArray + ? el : document.querySelectorAll(el); + } else { + requestedElem = el instanceof Element || el === window // scroll + ? el : document.querySelector(el); + } + return requestedElem; + } catch (e) { + throw TypeError(("KUTE.js - Element(s) not found: " + el + ".")); + } + } + + /** + * 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. + */ + var AnimationBase = function AnimationBase(Component) { + var ComponentName = Component.component; + // const Objects = { defaultValues, defaultOptions, Interpolate, linkProperty } + var Functions = { onStart: onStart, onComplete: onComplete }; + var Category = Component.category; + var 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(function (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(function (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(function (fni) { + var compIntObj = Component.Interpolate[fni]; + if (typeof (compIntObj) === 'function' && !interpolate[fni]) { + interpolate[fni] = compIntObj; + } else { + Object.keys(compIntObj).forEach(function (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(function (fnu) { + if (!Util[fnu]) { Util[fnu] = Component.Util[fnu]; } + }); + } + + return { name: ComponentName }; + }; + + /** + * Perspective Interpolation Function. + * + * @param {number} a start value + * @param {number} b end value + * @param {string} u unit + * @param {number} v progress + * @returns {string} the perspective function in string format + */ + function perspective(a, b, u, v) { + // eslint-disable-next-line no-bitwise + return ("perspective(" + (((a + (b - a) * v) * 1000 >> 0) / 1000) + u + ")"); + } + + /** + * Translate 3D Interpolation Function. + * + * @param {number[]} a start [x,y,z] position + * @param {number[]} b end [x,y,z] position + * @param {string} u unit, usually `px` degrees + * @param {number} v progress + * @returns {string} the interpolated 3D translation string + */ + function translate3d(a, b, u, v) { + var translateArray = []; + for (var ax = 0; ax < 3; ax += 1) { + translateArray[ax] = (a[ax] || b[ax] + // eslint-disable-next-line no-bitwise + ? ((a[ax] + (b[ax] - a[ax]) * v) * 1000 >> 0) / 1000 : 0) + u; + } + return ("translate3d(" + (translateArray.join(',')) + ")"); + } + + /** + * 3D Rotation Interpolation Function. + * + * @param {number} a start [x,y,z] angles + * @param {number} b end [x,y,z] angles + * @param {string} u unit, usually `deg` degrees + * @param {number} v progress + * @returns {string} the interpolated 3D rotation string + */ + function rotate3d(a, b, u, v) { + var rotateStr = ''; + // eslint-disable-next-line no-bitwise + rotateStr += a[0] || b[0] ? ("rotateX(" + (((a[0] + (b[0] - a[0]) * v) * 1000 >> 0) / 1000) + u + ")") : ''; + // eslint-disable-next-line no-bitwise + rotateStr += a[1] || b[1] ? ("rotateY(" + (((a[1] + (b[1] - a[1]) * v) * 1000 >> 0) / 1000) + u + ")") : ''; + // eslint-disable-next-line no-bitwise + rotateStr += a[2] || b[2] ? ("rotateZ(" + (((a[2] + (b[2] - a[2]) * v) * 1000 >> 0) / 1000) + u + ")") : ''; + return rotateStr; + } + + /** + * Translate 2D Interpolation Function. + * + * @param {number[]} a start [x,y] position + * @param {number[]} b end [x,y] position + * @param {string} u unit, usually `px` degrees + * @param {number} v progress + * @returns {string} the interpolated 2D translation string + */ + function translate(a, b, u, v) { + var translateArray = []; + // eslint-disable-next-line no-bitwise + translateArray[0] = (a[0] === b[0] ? b[0] : ((a[0] + (b[0] - a[0]) * v) * 1000 >> 0) / 1000) + u; + // eslint-disable-next-line no-bitwise + translateArray[1] = a[1] || b[1] ? ((a[1] === b[1] ? b[1] : ((a[1] + (b[1] - a[1]) * v) * 1000 >> 0) / 1000) + u) : '0'; + return ("translate(" + (translateArray.join(',')) + ")"); + } + + /** + * 2D Rotation Interpolation Function. + * + * @param {number} a start angle + * @param {number} b end angle + * @param {string} u unit, usually `deg` degrees + * @param {number} v progress + * @returns {string} the interpolated rotation + */ + function rotate(a, b, u, v) { + // eslint-disable-next-line no-bitwise + return ("rotate(" + (((a + (b - a) * v) * 1000 >> 0) / 1000) + u + ")"); + } + + /** + * Scale Interpolation Function. + * + * @param {number} a start scale + * @param {number} b end scale + * @param {number} v progress + * @returns {string} the interpolated scale + */ + function scale(a, b, v) { + // eslint-disable-next-line no-bitwise + return ("scale(" + (((a + (b - a) * v) * 1000 >> 0) / 1000) + ")"); + } + + /** + * Skew Interpolation Function. + * + * @param {number} a start {x,y} angles + * @param {number} b end {x,y} angles + * @param {string} u unit, usually `deg` degrees + * @param {number} v progress + * @returns {string} the interpolated string value of skew(s) + */ + function skew(a, b, u, v) { + var skewArray = []; + // eslint-disable-next-line no-bitwise + skewArray[0] = (a[0] === b[0] ? b[0] : ((a[0] + (b[0] - a[0]) * v) * 1000 >> 0) / 1000) + u; + // eslint-disable-next-line no-bitwise + skewArray[1] = a[1] || b[1] ? ((a[1] === b[1] ? b[1] : ((a[1] + (b[1] - a[1]) * v) * 1000 >> 0) / 1000) + u) : '0'; + return ("skew(" + (skewArray.join(',')) + ")"); + } + + // Component Functions + /** + * Sets the property update function. + * * same to svgTransform, htmlAttributes + * @param {string} tweenProp the property name + */ + function onStartTransform(tweenProp) { + if (!KEC[tweenProp] && this.valuesEnd[tweenProp]) { + KEC[tweenProp] = function (elem, a, b, v) { + // eslint-disable-next-line no-param-reassign + elem.style[tweenProp] = (a.perspective || b.perspective ? perspective(a.perspective, b.perspective, 'px', v) : '') // one side might be 0 + + (a.translate3d ? translate3d(a.translate3d, b.translate3d, 'px', v) : '') // array [x,y,z] + + (a.rotate3d ? rotate3d(a.rotate3d, b.rotate3d, 'deg', v) : '') // array [x,y,z] + + (a.skew ? skew(a.skew, b.skew, 'deg', v) : '') // array [x,y] + + (a.scale || b.scale ? scale(a.scale, b.scale, v) : ''); // one side might be 0 + }; + } + } + + // Base Component + var TransformFunctionsBase = { + component: 'baseTransform', + property: 'transform', + functions: { onStart: onStartTransform }, + Interpolate: { + perspective: perspective, + translate3d: translate3d, + rotate3d: rotate3d, + translate: translate, + rotate: rotate, + scale: scale, + skew: skew, + }, + }; + + /** + * Numbers Interpolation Function. + * + * @param {number} a start value + * @param {number} b end value + * @param {number} v progress + * @returns {number} the interpolated number + */ + function numbers(a, b, v) { + var A = +a; + var B = b - a; + // a = +a; b -= a; + return A + B * v; + } + + // Component Functions + /** + * Sets the update function for the property. + * @param {string} tweenProp the property name + */ + function boxModelOnStart(tweenProp) { + if (tweenProp in this.valuesEnd && !KEC[tweenProp]) { + KEC[tweenProp] = function (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 + var baseBoxProps = ['top', 'left', 'width', 'height']; + var baseBoxOnStart = {}; + baseBoxProps.forEach(function (x) { baseBoxOnStart[x] = boxModelOnStart; }); + + // Component Base + var BoxModelBase = { + component: 'baseBoxModel', + category: 'boxModel', + properties: baseBoxProps, + Interpolate: { numbers: numbers }, + functions: { onStart: baseBoxOnStart }, + }; + + /* opacityProperty = { + property: 'opacity', + defaultValue: 1, + interpolators: {numbers}, + functions = { prepareStart, prepareProperty, onStart } + } */ + + // Component Functions + /** + * Sets the property update function. + * @param {string} tweenProp the property name + */ + function onStartOpacity(tweenProp/* , value */) { + // opacity could be 0 sometimes, we need to check regardless + if (tweenProp in this.valuesEnd && !KEC[tweenProp]) { + KEC[tweenProp] = function (elem, a, b, v) { + /* eslint-disable */ + elem.style[tweenProp] = ((numbers(a, b, v) * 1000) >> 0) / 1000; + /* eslint-enable */ + }; + } + } + + // Base Component + var OpacityPropertyBase = { + component: 'baseOpacity', + property: 'opacity', + // defaultValue: 1, + Interpolate: { numbers: numbers }, + functions: { onStart: onStartOpacity }, + }; + + // import {baseCrossBrowserMove} from '../components/crossBrowserMove' + // support for kute-base ends here + + var Components = { + Transform: new AnimationBase(TransformFunctionsBase), + BoxModel: new AnimationBase(BoxModelBase), + Opacity: new AnimationBase(OpacityPropertyBase), + }; + + function queueStart() { + var this$1$1 = this; + + // fire onStart actions + Object.keys(onStart).forEach(function (obj) { + if (typeof (onStart[obj]) === 'function') { + onStart[obj].call(this$1$1, obj); // easing functions + } else { + Object.keys(onStart[obj]).forEach(function (prop) { + onStart[obj][prop].call(this$1$1, prop); + }); + } + }); + + // add interpolations + linkInterpolation.call(this); + } + + /** + * The `TweenBase` constructor creates a new `Tween` object + * for a single `HTMLElement` and returns it. + * + * `TweenBase` is meant to be used with pre-processed values. + */ + var TweenBase = function TweenBase(targetElement, startObject, endObject, opsObject) { + var this$1$1 = this; + + // element animation is applied to + this.element = targetElement; + + /** @type {boolean} */ + this.playing = false; + /** @type {number?} */ + this._startTime = null; + /** @type {boolean} */ + this._startFired = false; + + // type is set via KUTE.tweenProps + this.valuesEnd = endObject; + this.valuesStart = startObject; + + // OPTIONS + var options = opsObject || {}; + // internal option to process inline/computed style at start instead of init + // used by to() method and expects object : {} / false + this._resetStart = options.resetStart || 0; + // you can only set a core easing function as default + /** @type {KUTE.easingOption} */ + this._easing = typeof (options.easing) === 'function' ? options.easing : connect.processEasing(options.easing); + /** @type {number} */ + this._duration = options.duration || defaultOptions.duration; // duration option | default + /** @type {number} */ + this._delay = options.delay || defaultOptions.delay; // delay option | default + + // set other options + Object.keys(options).forEach(function (op) { + var internalOption = "_" + op; + if (!(internalOption in this$1$1)) { this$1$1[internalOption] = options[op]; } + }); + + // callbacks should not be set as undefined + // this._onStart = options.onStart + // this._onUpdate = options.onUpdate + // this._onStop = options.onStop + // this._onComplete = options.onComplete + + // queue the easing + var easingFnName = this._easing.name; + if (!onStart[easingFnName]) { + onStart[easingFnName] = function easingFn(prop) { + if (!KEC[prop] && prop === this._easing.name) { KEC[prop] = this._easing; } + }; + } + + return this; + }; + + /** + * Starts tweening + * @param {number?} time the tween start time + * @returns {TweenBase} this instance + */ + TweenBase.prototype.start = function start (time) { + // now it's a good time to start + add(this); + this.playing = true; + + this._startTime = typeof time !== 'undefined' ? time : KEC.Time(); + this._startTime += this._delay; + + if (!this._startFired) { + if (this._onStart) { + this._onStart.call(this); + } + + queueStart.call(this); + + this._startFired = true; + } + + if (!Tick) { Ticker(); } + return this; + }; + + /** + * Stops tweening + * @returns {TweenBase} this instance + */ + TweenBase.prototype.stop = function stop () { + if (this.playing) { + remove(this); + this.playing = false; + + if (this._onStop) { + this._onStop.call(this); + } + this.close(); + } + return this; + }; + + /** + * Trigger internal completion callbacks. + */ + TweenBase.prototype.close = function close () { + var this$1$1 = this; + + // scroll|transformMatrix need this + Object.keys(onComplete).forEach(function (component) { + Object.keys(onComplete[component]).forEach(function (toClose) { + onComplete[component][toClose].call(this$1$1, toClose); + }); + }); + // when all animations are finished, stop ticking after ~3 frames + this._startFired = false; + stop.call(this); + }; + + /** + * Schedule another tween instance to start once this one completes. + * @param {KUTE.chainOption} args the tween animation start time + * @returns {TweenBase} this instance + */ + TweenBase.prototype.chain = function chain (args) { + this._chain = []; + this._chain = args.length ? args : this._chain.concat(args); + return this; + }; + + /** + * Stop tweening the chained tween instances. + */ + TweenBase.prototype.stopChainedTweens = function stopChainedTweens () { + if (this._chain && this._chain.length) { this._chain.forEach(function (tw) { return tw.stop(); }); } + }; + + /** + * Update the tween on each tick. + * @param {number} time the tick time + * @returns {boolean} this instance + */ + TweenBase.prototype.update = function update (time) { + var this$1$1 = this; + + var T = time !== undefined ? time : KEC.Time(); + + var elapsed; + + if (T < this._startTime && this.playing) { return true; } + + elapsed = (T - this._startTime) / this._duration; + elapsed = (this._duration === 0 || elapsed > 1) ? 1 : elapsed; + + // calculate progress + var progress = this._easing(elapsed); + + // render the update + Object.keys(this.valuesEnd).forEach(function (tweenProp) { + KEC[tweenProp](this$1$1.element, + this$1$1.valuesStart[tweenProp], + this$1$1.valuesEnd[tweenProp], + progress); + }); + + // fire the updateCallback + if (this._onUpdate) { + this._onUpdate.call(this); + } + + if (elapsed === 1) { + // fire the complete callback + if (this._onComplete) { + this._onComplete.call(this); + } + + // now we're sure no animation is running + this.playing = false; + + // stop ticking when finished + this.close(); + + // start animating chained tweens + if (this._chain !== undefined && this._chain.length) { + this._chain.map(function (tw) { return tw.start(); }); + } + + return false; + } + + return true; + }; + + // Update Tween Interface + connect.tween = TweenBase; + + var TweenConstructor = connect.tween; + + /** + * The `KUTE.fromTo()` static method returns a new Tween object + * for a single `HTMLElement` at a given state. + * + * @param {Element} element target element + * @param {KUTE.tweenProps} startObject + * @param {KUTE.tweenProps} endObject + * @param {KUTE.tweenOptions} optionsObj tween options + * @returns {KUTE.Tween} the resulting Tween object + */ + function fromTo(element, startObject, endObject, optionsObj) { + var options = optionsObj || {}; + return new TweenConstructor(selector(element), startObject, endObject, options); + } + + var version = "2.2.2"; + + // @ts-ignore + + /** + * A global namespace for library version. + * @type {string} + */ + var Version = version; + + var indexBase = { + Animation: AnimationBase, + Components: Components, + + Tween: TweenBase, + fromTo: fromTo, + + Objects: Objects, + Easing: Easing, + Util: Util, + Render: Render, + Interpolate: interpolate, + Internals: internals, + Selector: selector, + Version: Version, + }; + + return indexBase; + +})); diff --git a/src/kute-base.min.js b/src/kute-base.min.js new file mode 100644 index 0000000..72e48c4 --- /dev/null +++ b/src/kute-base.min.js @@ -0,0 +1,2 @@ +// KUTE.js Base v2.2.2 | thednp © 2021 | MIT-License +!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):(t="undefined"!=typeof globalThis?globalThis:t||self).KUTE=e()}(this,(function(){"use strict";var t={},e=[],n="undefined"!=typeof global?global:"undefined"!=typeof window?window.self:{},i={},o={},r="undefined"==typeof self&&"undefined"!=typeof process&&process.hrtime?function(){var t=process.hrtime();return 1e3*t[0]+t[1]/1e6}:"undefined"!=typeof self&&void 0!==self.performance&&void 0!==self.performance.now?self.performance.now.bind(self.performance):"undefined"!=typeof Date&&Date.now?Date.now:function(){return(new Date).getTime()},a={};a.now=r;var s=0,c=function(t){for(var n=0;n>0)/1e3+n+")"}function T(t,e,n,i){for(var o=[],r=0;r<3;r+=1)o[r]=(t[r]||e[r]?(1e3*(t[r]+(e[r]-t[r])*i)>>0)/1e3:0)+n;return"translate3d("+o.join(",")+")"}function I(t,e,n,i){var o="";return o+=t[0]||e[0]?"rotateX("+(1e3*(t[0]+(e[0]-t[0])*i)>>0)/1e3+n+")":"",o+=t[1]||e[1]?"rotateY("+(1e3*(t[1]+(e[1]-t[1])*i)>>0)/1e3+n+")":"",o+=t[2]||e[2]?"rotateZ("+(1e3*(t[2]+(e[2]-t[2])*i)>>0)/1e3+n+")":""}function S(t,e,n){return"scale("+(1e3*(t+(e-t)*n)>>0)/1e3+")"}function C(t,e,n,i){var o=[];return o[0]=(t[0]===e[0]?e[0]:(1e3*(t[0]+(e[0]-t[0])*i)>>0)/1e3)+n,o[1]=t[1]||e[1]?(t[1]===e[1]?e[1]:(1e3*(t[1]+(e[1]-t[1])*i)>>0)/1e3)+n:"0","skew("+o.join(",")+")"}var x={component:"baseTransform",property:"transform",functions:{onStart:function(e){!t[e]&&this.valuesEnd[e]&&(t[e]=function(t,n,i,o){t.style[e]=(n.perspective||i.perspective?w(n.perspective,i.perspective,"px",o):"")+(n.translate3d?T(n.translate3d,i.translate3d,"px",o):"")+(n.rotate3d?I(n.rotate3d,i.rotate3d,"deg",o):"")+(n.skew?C(n.skew,i.skew,"deg",o):"")+(n.scale||i.scale?S(n.scale,i.scale,o):"")})}},Interpolate:{perspective:w,translate3d:T,rotate3d:I,translate:function(t,e,n,i){var o=[];return o[0]=(t[0]===e[0]?e[0]:(1e3*(t[0]+(e[0]-t[0])*i)>>0)/1e3)+n,o[1]=t[1]||e[1]?(t[1]===e[1]?e[1]:(1e3*(t[1]+(e[1]-t[1])*i)>>0)/1e3)+n:"0","translate("+o.join(",")+")"},rotate:function(t,e,n,i){return"rotate("+(1e3*(t+(e-t)*i)>>0)/1e3+n+")"},scale:S,skew:C}};function U(t,e,n){return+t+(e-t)*n}function M(e){e in this.valuesEnd&&!t[e]&&(t[e]=function(t,n,i,o){t.style[e]=(o>.99||o<.01?(10*U(n,i,o)>>0)/10:U(n,i,o)>>0)+"px"})}var q=["top","left","width","height"],A={};q.forEach((function(t){A[t]=M}));var F={component:"baseBoxModel",category:"boxModel",properties:q,Interpolate:{numbers:U},functions:{onStart:A}};var B={component:"baseOpacity",property:"opacity",Interpolate:{numbers:U},functions:{onStart:function(e){e in this.valuesEnd&&!t[e]&&(t[e]=function(t,n,i,o){t.style[e]=(1e3*U(n,i,o)>>0)/1e3})}}},D={Transform:new j(x),BoxModel:new j(F),Opacity:new j(B)};function K(){var t=this;Object.keys(o).forEach((function(e){"function"==typeof o[e]?o[e].call(t,e):Object.keys(o[e]).forEach((function(n){o[e][n].call(t,n)}))})),_.call(this)}var Q=function(e,n,i,r){var a=this;this.element=e,this.playing=!1,this._startTime=null,this._startFired=!1,this.valuesEnd=i,this.valuesStart=n;var s=r||{};this._resetStart=s.resetStart||0,this._easing="function"==typeof s.easing?s.easing:v.processEasing(s.easing),this._duration=s.duration||l.duration,this._delay=s.delay||l.delay,Object.keys(s).forEach((function(t){var e="_"+t;e in a||(a[e]=s[t])}));var c=this._easing.name;return o[c]||(o[c]=function(e){t[e]||e!==this._easing.name||(t[e]=this._easing)}),this};Q.prototype.start=function(e){return g(this),this.playing=!0,this._startTime=void 0!==e?e:t.Time(),this._startTime+=this._delay,this._startFired||(this._onStart&&this._onStart.call(this),K.call(this),this._startFired=!0),s||c(),this},Q.prototype.stop=function(){return this.playing&&(E(this),this.playing=!1,this._onStop&&this._onStop.call(this),this.close()),this},Q.prototype.close=function(){var t=this;Object.keys(p).forEach((function(e){Object.keys(p[e]).forEach((function(n){p[e][n].call(t,n)}))})),this._startFired=!1,u.call(this)},Q.prototype.chain=function(t){return this._chain=[],this._chain=t.length?t:this._chain.concat(t),this},Q.prototype.stopChainedTweens=function(){this._chain&&this._chain.length&&this._chain.forEach((function(t){return t.stop()}))},Q.prototype.update=function(e){var n,i=this,o=void 0!==e?e:t.Time();if(o1?1:n;var r=this._easing(n);return Object.keys(this.valuesEnd).forEach((function(e){t[e](i.element,i.valuesStart[e],i.valuesEnd[e],r)})),this._onUpdate&&this._onUpdate.call(this),1!==n||(this._onComplete&&this._onComplete.call(this),this.playing=!1,this.close(),void 0!==this._chain&&this._chain.length&&this._chain.map((function(t){return t.start()})),!1)},v.tween=Q;var L=v.tween;return{Animation:j,Components:D,Tween:Q,fromTo:function(t,e,n,i){var o=i||{};return new L(k(t),e,n,o)},Objects:d,Easing:m,Util:y,Render:f,Interpolate:i,Internals:O,Selector:k,Version:"2.2.2"}})); diff --git a/src/kute-extra.js b/src/kute-extra.js new file mode 100644 index 0000000..ede7275 --- /dev/null +++ b/src/kute-extra.js @@ -0,0 +1,5731 @@ +/*! +* KUTE.js Extra v2.2.2 (http://thednp.github.io/kute.js) +* Copyright 2015-2021 © thednp +* Licensed under MIT (https://github.com/thednp/kute.js/blob/master/LICENSE) +*/ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : + typeof define === 'function' && define.amd ? define(factory) : + (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.KUTE = factory()); +})(this, (function () { 'use strict'; + + /** + * Creates cubic-bezier easing functions. + * + * @class + */ + var CubicBezier = function CubicBezier(p1x, p1y, p2x, p2y, functionName) { + var this$1$1 = this; + + // pre-calculate the polynomial coefficients + // First and last control points are implied to be (0,0) and (1.0, 1.0) + + /** @type {number} */ + this.cx = 3.0 * p1x; + + /** @type {number} */ + this.bx = 3.0 * (p2x - p1x) - this.cx; + + /** @type {number} */ + this.ax = 1.0 - this.cx - this.bx; + + /** @type {number} */ + this.cy = 3.0 * p1y; + + /** @type {number} */ + this.by = 3.0 * (p2y - p1y) - this.cy; + + /** @type {number} */ + this.ay = 1.0 - this.cy - this.by; + + /** @type {(t: number) => number} */ + var BezierEasing = function (t) { return this$1$1.sampleCurveY(this$1$1.solveCurveX(t)); }; + + // this function needs a name + Object.defineProperty(BezierEasing, 'name', { writable: true }); + BezierEasing.name = functionName || ("cubic-bezier(" + ([p1x, p1y, p2x, p2y]) + ")"); + + return BezierEasing; + }; + + /** + * @param {number} t - progress [0-1] + * @return {number} - sampled X value + */ + CubicBezier.prototype.sampleCurveX = function sampleCurveX (t) { + return ((this.ax * t + this.bx) * t + this.cx) * t; + }; + + /** + * @param {number} t - progress [0-1] + * @return {number} - sampled Y value + */ + CubicBezier.prototype.sampleCurveY = function sampleCurveY (t) { + return ((this.ay * t + this.by) * t + this.cy) * t; + }; + + /** + * @param {number} t - progress [0-1] + * @return {number} - sampled curve derivative X value + */ + CubicBezier.prototype.sampleCurveDerivativeX = function sampleCurveDerivativeX (t) { + return (3.0 * this.ax * t + 2.0 * this.bx) * t + this.cx; + }; + + /** + * @param {number} x - progress [0-1] + * @return {number} - solved curve X value + */ + CubicBezier.prototype.solveCurveX = function solveCurveX (x) { + var t0; + var t1; + var t2; + var x2; + var d2; + var i; + var epsilon = 1e-5; // Precision + + // First try a few iterations of Newton's method -- normally very fast. + for (t2 = x, i = 0; i < 32; i += 1) { + x2 = this.sampleCurveX(t2) - x; + if (Math.abs(x2) < epsilon) { return t2; } + d2 = this.sampleCurveDerivativeX(t2); + if (Math.abs(d2) < epsilon) { break; } + t2 -= x2 / d2; + } + + // No solution found - use bi-section + t0 = 0.0; + t1 = 1.0; + t2 = x; + + if (t2 < t0) { return t0; } + if (t2 > t1) { return t1; } + + while (t0 < t1) { + x2 = this.sampleCurveX(t2); + if (Math.abs(x2 - x) < epsilon) { return t2; } + if (x > x2) { t0 = t2; } + else { t1 = t2; } + + t2 = (t1 - t0) * 0.5 + t0; + } + + // Give up + return t2; + }; + + var version$1 = "1.0.18"; + + // @ts-ignore + + /** + * A global namespace for library version. + * @type {string} + */ + var Version$1 = version$1; + + Object.assign(CubicBezier, { Version: Version$1 }); + + /** + * The KUTE.js Execution Context + */ + var KEC = {}; + + var Tweens = []; + + var gl0bal; + + if (typeof global !== 'undefined') { gl0bal = global; } + else if (typeof window !== 'undefined') { gl0bal = window.self; } + else { gl0bal = {}; } + + var globalObject = gl0bal; + + // KUTE.js INTERPOLATE FUNCTIONS + // ============================= + var interpolate = {}; + + // schedule property specific function on animation start + // link property update function to KUTE.js execution context + var onStart = {}; + + // Include a performance.now polyfill. + // source https://github.com/tweenjs/tween.js/blob/master/src/Now.ts + var performanceNow; + + // In node.js, use process.hrtime. + // eslint-disable-next-line + // @ts-ignore + if (typeof self === 'undefined' && typeof process !== 'undefined' && process.hrtime) { + performanceNow = function () { + // eslint-disable-next-line + // @ts-ignore + var time = process.hrtime(); + + // Convert [seconds, nanoseconds] to milliseconds. + return time[0] * 1000 + time[1] / 1000000; + }; + } else if (typeof self !== 'undefined' && self.performance !== undefined && self.performance.now !== undefined) { + // In a browser, use self.performance.now if it is available. + // This must be bound, because directly assigning this function + // leads to an invocation exception in Chrome. + performanceNow = self.performance.now.bind(self.performance); + } else if (typeof Date !== 'undefined' && Date.now) { + // Use Date.now if it is available. + performanceNow = Date.now; + } else { + // Otherwise, use 'new Date().getTime()'. + performanceNow = function () { return new Date().getTime(); }; + } + + var now = performanceNow; + + var Time = {}; + Time.now = now; + + // eslint-disable-next-line import/no-mutable-exports -- impossible to satisfy + var Tick = 0; + + /** + * + * @param {number | Date} time + */ + var Ticker = function (time) { + var i = 0; + while (i < Tweens.length) { + if (Tweens[i].update(time)) { + i += 1; + } else { + Tweens.splice(i, 1); + } + } + Tick = requestAnimationFrame(Ticker); + }; + + // stop requesting animation frame + function stop() { + setTimeout(function () { // re-added for #81 + if (!Tweens.length && Tick) { + cancelAnimationFrame(Tick); + Tick = null; + Object.keys(onStart).forEach(function (obj) { + if (typeof (onStart[obj]) === 'function') { + if (KEC[obj]) { delete KEC[obj]; } + } else { + Object.keys(onStart[obj]).forEach(function (prop) { + if (KEC[prop]) { delete KEC[prop]; } + }); + } + }); + + Object.keys(interpolate).forEach(function (i) { + if (KEC[i]) { delete KEC[i]; } + }); + } + }, 64); + } + + // render update functions + // ======================= + var Render = { + Tick: Tick, Ticker: Ticker, Tweens: Tweens, Time: Time, + }; + Object.keys(Render).forEach(function (blob) { + if (!KEC[blob]) { + KEC[blob] = blob === 'Time' ? Time.now : Render[blob]; + } + }); + + globalObject._KUTE = KEC; + + // all supported properties + var supportedProperties = {}; + + var defaultValues = {}; + + var defaultOptions$1 = { + duration: 700, + delay: 0, + easing: 'linear', + repeat: 0, + repeatDelay: 0, + yoyo: false, + resetStart: false, + offset: 0, + }; + + // used in preparePropertiesObject + var prepareProperty = {}; + + // check current property value when .to() method is used + var prepareStart = {}; + + // checks for differences between the processed start and end values, + // can be set to make sure start unit and end unit are same, + // stack transforms, process SVG paths, + // any type of post processing the component needs + var crossCheck = {}; + + // schedule property specific function on animation complete + var onComplete = {}; + + // link properties to interpolate functions + var linkProperty = {}; + + var Objects = { + supportedProperties: supportedProperties, + defaultValues: defaultValues, + defaultOptions: defaultOptions$1, + prepareProperty: prepareProperty, + prepareStart: prepareStart, + crossCheck: crossCheck, + onStart: onStart, + onComplete: onComplete, + linkProperty: linkProperty, + }; + + // util - a general object for utils like rgbToHex, processEasing + var Util = {}; + + /** + * KUTE.add(Tween) + * + * @param {KUTE.Tween} tw a new tween to add + */ + var add = function (tw) { return Tweens.push(tw); }; + + /** + * KUTE.remove(Tween) + * + * @param {KUTE.Tween} tw a new tween to add + */ + var remove = function (tw) { + var i = Tweens.indexOf(tw); + if (i !== -1) { Tweens.splice(i, 1); } + }; + + /** + * KUTE.add(Tween) + * + * @return {KUTE.Tween[]} tw a new tween to add + */ + var getAll = function () { return Tweens; }; + + /** + * KUTE.removeAll() + */ + var removeAll = function () { Tweens.length = 0; }; + + /** + * linkInterpolation + * @this {KUTE.Tween} + */ + function linkInterpolation() { + var this$1$1 = this; + // DON'T change + Object.keys(linkProperty).forEach(function (component) { + var componentLink = linkProperty[component]; + var componentProps = supportedProperties[component]; + + Object.keys(componentLink).forEach(function (fnObj) { + if (typeof (componentLink[fnObj]) === 'function' // ATTR, colors, scroll, boxModel, borderRadius + && Object.keys(this$1$1.valuesEnd).some(function (i) { return (componentProps && componentProps.includes(i)) + || (i === 'attr' && Object.keys(this$1$1.valuesEnd[i]).some(function (j) { return componentProps && componentProps.includes(j); })); })) { + if (!KEC[fnObj]) { KEC[fnObj] = componentLink[fnObj]; } + } else { + Object.keys(this$1$1.valuesEnd).forEach(function (prop) { + var propObject = this$1$1.valuesEnd[prop]; + if (propObject instanceof Object) { + Object.keys(propObject).forEach(function (i) { + if (typeof (componentLink[i]) === 'function') { // transformCSS3 + if (!KEC[i]) { KEC[i] = componentLink[i]; } + } else { + Object.keys(componentLink[fnObj]).forEach(function (j) { + if (componentLink[i] && typeof (componentLink[i][j]) === 'function') { // transformMatrix + if (!KEC[j]) { KEC[j] = componentLink[i][j]; } + } + }); + } + }); + } + }); + } + }); + }); + } + + var internals = { + add: add, + remove: remove, + getAll: getAll, + removeAll: removeAll, + stop: stop, + linkInterpolation: linkInterpolation, + }; + + /** + * getInlineStyle + * Returns the transform style for element from + * cssText. Used by for the `.to()` static method. + * + * @param {Element} el target element + * @returns {object} + */ + function getInlineStyle(el) { + // if the scroll applies to `window` it returns as it has no styling + if (!el.style) { return false; } + // the cssText | the resulting transform object + var css = el.style.cssText.replace(/\s/g, '').split(';'); + var transformObject = {}; + var arrayFn = ['translate3d', 'translate', 'scale3d', 'skew']; + + css.forEach(function (cs) { + if (/transform/i.test(cs)) { + // all transform properties + var tps = cs.split(':')[1].split(')'); + tps.forEach(function (tpi) { + var tpv = tpi.split('('); + var tp = tpv[0]; + // each transform property + var tv = tpv[1]; + if (!/matrix/.test(tp)) { + transformObject[tp] = arrayFn.includes(tp) ? tv.split(',') : tv; + } + }); + } + }); + + return transformObject; + } + + /** + * getStyleForProperty + * + * Returns the computed style property for element for .to() method. + * Used by for the `.to()` static method. + * + * @param {Element} elem + * @param {string} propertyName + * @returns {string} + */ + function getStyleForProperty(elem, propertyName) { + var result = defaultValues[propertyName]; + var styleAttribute = elem.style; + var computedStyle = getComputedStyle(elem) || elem.currentStyle; + var styleValue = styleAttribute[propertyName] && !/auto|initial|none|unset/.test(styleAttribute[propertyName]) + ? styleAttribute[propertyName] + : computedStyle[propertyName]; + + if (propertyName !== 'transform' && (propertyName in computedStyle || propertyName in styleAttribute)) { + result = styleValue; + } + + return result; + } + + /** + * prepareObject + * + * Returns all processed valuesStart / valuesEnd. + * + * @param {Element} obj the values start/end object + * @param {string} fn toggles between the two + */ + function prepareObject(obj, fn) { + var this$1$1 = this; + // this, props object, type: start/end + var propertiesObject = fn === 'start' ? this.valuesStart : this.valuesEnd; + + Object.keys(prepareProperty).forEach(function (component) { + var prepareComponent = prepareProperty[component]; + var supportComponent = supportedProperties[component]; + + Object.keys(prepareComponent).forEach(function (tweenCategory) { + var transformObject = {}; + + Object.keys(obj).forEach(function (tweenProp) { + // scroll, opacity, other components + if (defaultValues[tweenProp] && prepareComponent[tweenProp]) { + propertiesObject[tweenProp] = prepareComponent[tweenProp] + .call(this$1$1, tweenProp, obj[tweenProp]); + + // transform + } else if (!defaultValues[tweenCategory] && tweenCategory === 'transform' + && supportComponent.includes(tweenProp)) { + transformObject[tweenProp] = obj[tweenProp]; + + // allow transformFunctions to work with preprocessed input values + } else if (!defaultValues[tweenProp] && tweenProp === 'transform') { + propertiesObject[tweenProp] = obj[tweenProp]; + + // colors, boxModel, category + } else if (!defaultValues[tweenCategory] + && supportComponent && supportComponent.includes(tweenProp)) { + propertiesObject[tweenProp] = prepareComponent[tweenCategory] + .call(this$1$1, tweenProp, obj[tweenProp]); + } + }); + + // we filter out older browsers by checking Object.keys + if (Object.keys(transformObject).length) { + propertiesObject[tweenCategory] = prepareComponent[tweenCategory] + .call(this$1$1, tweenCategory, transformObject); + } + }); + }); + } + + /** + * getStartValues + * + * Returns the start values for to() method. + * Used by for the `.to()` static method. + * + * @this {KUTE.Tween} the tween instance + */ + function getStartValues() { + var this$1$1 = this; + + var startValues = {}; + var currentStyle = getInlineStyle(this.element); + + Object.keys(this.valuesStart).forEach(function (tweenProp) { + Object.keys(prepareStart).forEach(function (component) { + var componentStart = prepareStart[component]; + + Object.keys(componentStart).forEach(function (tweenCategory) { + // clip, opacity, scroll + if (tweenCategory === tweenProp && componentStart[tweenProp]) { + startValues[tweenProp] = componentStart[tweenCategory] + .call(this$1$1, tweenProp, this$1$1.valuesStart[tweenProp]); + // find in an array of properties + } else if (supportedProperties[component] + && supportedProperties[component].includes(tweenProp)) { + startValues[tweenProp] = componentStart[tweenCategory] + .call(this$1$1, tweenProp, this$1$1.valuesStart[tweenProp]); + } + }); + }); + }); + + // stack transformCSS props for .to() chains + // also add to startValues values from previous tweens + Object.keys(currentStyle).forEach(function (current) { + if (!(current in this$1$1.valuesStart)) { + startValues[current] = currentStyle[current] || defaultValues[current]; + } + }); + + this.valuesStart = {}; + prepareObject.call(this, startValues, 'start'); + } + + var Process = { + getInlineStyle: getInlineStyle, + getStyleForProperty: getStyleForProperty, + getStartValues: getStartValues, + prepareObject: prepareObject, + }; + + var connect = {}; + /** @type {KUTE.TweenBase | KUTE.Tween | KUTE.TweenExtra} */ + connect.tween = null; + connect.processEasing = null; + + var Easing = { + linear: new CubicBezier(0, 0, 1, 1, 'linear'), + easingSinusoidalIn: new CubicBezier(0.47, 0, 0.745, 0.715, 'easingSinusoidalIn'), + easingSinusoidalOut: new CubicBezier(0.39, 0.575, 0.565, 1, 'easingSinusoidalOut'), + easingSinusoidalInOut: new CubicBezier(0.445, 0.05, 0.55, 0.95, 'easingSinusoidalInOut'), + + easingQuadraticIn: new CubicBezier(0.550, 0.085, 0.680, 0.530, 'easingQuadraticIn'), + easingQuadraticOut: new CubicBezier(0.250, 0.460, 0.450, 0.940, 'easingQuadraticOut'), + easingQuadraticInOut: new CubicBezier(0.455, 0.030, 0.515, 0.955, 'easingQuadraticInOut'), + + easingCubicIn: new CubicBezier(0.55, 0.055, 0.675, 0.19, 'easingCubicIn'), + easingCubicOut: new CubicBezier(0.215, 0.61, 0.355, 1, 'easingCubicOut'), + easingCubicInOut: new CubicBezier(0.645, 0.045, 0.355, 1, 'easingCubicInOut'), + + easingQuarticIn: new CubicBezier(0.895, 0.03, 0.685, 0.22, 'easingQuarticIn'), + easingQuarticOut: new CubicBezier(0.165, 0.84, 0.44, 1, 'easingQuarticOut'), + easingQuarticInOut: new CubicBezier(0.77, 0, 0.175, 1, 'easingQuarticInOut'), + + easingQuinticIn: new CubicBezier(0.755, 0.05, 0.855, 0.06, 'easingQuinticIn'), + easingQuinticOut: new CubicBezier(0.23, 1, 0.32, 1, 'easingQuinticOut'), + easingQuinticInOut: new CubicBezier(0.86, 0, 0.07, 1, 'easingQuinticInOut'), + + easingExponentialIn: new CubicBezier(0.95, 0.05, 0.795, 0.035, 'easingExponentialIn'), + easingExponentialOut: new CubicBezier(0.19, 1, 0.22, 1, 'easingExponentialOut'), + easingExponentialInOut: new CubicBezier(1, 0, 0, 1, 'easingExponentialInOut'), + + easingCircularIn: new CubicBezier(0.6, 0.04, 0.98, 0.335, 'easingCircularIn'), + easingCircularOut: new CubicBezier(0.075, 0.82, 0.165, 1, 'easingCircularOut'), + easingCircularInOut: new CubicBezier(0.785, 0.135, 0.15, 0.86, 'easingCircularInOut'), + + easingBackIn: new CubicBezier(0.6, -0.28, 0.735, 0.045, 'easingBackIn'), + easingBackOut: new CubicBezier(0.175, 0.885, 0.32, 1.275, 'easingBackOut'), + easingBackInOut: new CubicBezier(0.68, -0.55, 0.265, 1.55, 'easingBackInOut'), + }; + + /** + * Returns a valid `easingFunction`. + * + * @param {KUTE.easingFunction | string} fn function name or constructor name + * @returns {KUTE.easingFunction} a valid easingfunction + */ + function processBezierEasing(fn) { + if (typeof fn === 'function') { + return fn; + } if (typeof (Easing[fn]) === 'function') { + return Easing[fn]; + } if (/bezier/.test(fn)) { + var bz = fn.replace(/bezier|\s|\(|\)/g, '').split(','); + return new CubicBezier(bz[0] * 1, bz[1] * 1, bz[2] * 1, bz[3] * 1); // bezier easing + } + // if (/elastic|bounce/i.test(fn)) { + // throw TypeError(`KUTE - CubicBezier doesn't support ${fn} easing.`); + // } + return Easing.linear; + } + + connect.processEasing = processBezierEasing; + + /** + * selector + * + * A selector utility for KUTE.js. + * + * @param {KUTE.selectorType} el target(s) or string selector + * @param {boolean | number} multi when true returns an array/collection of elements + * @returns {Element | Element[] | null} + */ + function selector(el, multi) { + try { + var requestedElem; + var itemsArray; + if (multi) { + itemsArray = el instanceof Array && el.every(function (x) { return x instanceof Element; }); + requestedElem = el instanceof HTMLCollection || el instanceof NodeList || itemsArray + ? el : document.querySelectorAll(el); + } else { + requestedElem = el instanceof Element || el === window // scroll + ? el : document.querySelector(el); + } + return requestedElem; + } catch (e) { + throw TypeError(("KUTE.js - Element(s) not found: " + el + ".")); + } + } + + function queueStart() { + var this$1$1 = this; + + // fire onStart actions + Object.keys(onStart).forEach(function (obj) { + if (typeof (onStart[obj]) === 'function') { + onStart[obj].call(this$1$1, obj); // easing functions + } else { + Object.keys(onStart[obj]).forEach(function (prop) { + onStart[obj][prop].call(this$1$1, prop); + }); + } + }); + + // add interpolations + linkInterpolation.call(this); + } + + /** + * The `TweenBase` constructor creates a new `Tween` object + * for a single `HTMLElement` and returns it. + * + * `TweenBase` is meant to be used with pre-processed values. + */ + var TweenBase = function TweenBase(targetElement, startObject, endObject, opsObject) { + var this$1$1 = this; + + // element animation is applied to + this.element = targetElement; + + /** @type {boolean} */ + this.playing = false; + /** @type {number?} */ + this._startTime = null; + /** @type {boolean} */ + this._startFired = false; + + // type is set via KUTE.tweenProps + this.valuesEnd = endObject; + this.valuesStart = startObject; + + // OPTIONS + var options = opsObject || {}; + // internal option to process inline/computed style at start instead of init + // used by to() method and expects object : {} / false + this._resetStart = options.resetStart || 0; + // you can only set a core easing function as default + /** @type {KUTE.easingOption} */ + this._easing = typeof (options.easing) === 'function' ? options.easing : connect.processEasing(options.easing); + /** @type {number} */ + this._duration = options.duration || defaultOptions$1.duration; // duration option | default + /** @type {number} */ + this._delay = options.delay || defaultOptions$1.delay; // delay option | default + + // set other options + Object.keys(options).forEach(function (op) { + var internalOption = "_" + op; + if (!(internalOption in this$1$1)) { this$1$1[internalOption] = options[op]; } + }); + + // callbacks should not be set as undefined + // this._onStart = options.onStart + // this._onUpdate = options.onUpdate + // this._onStop = options.onStop + // this._onComplete = options.onComplete + + // queue the easing + var easingFnName = this._easing.name; + if (!onStart[easingFnName]) { + onStart[easingFnName] = function easingFn(prop) { + if (!KEC[prop] && prop === this._easing.name) { KEC[prop] = this._easing; } + }; + } + + return this; + }; + + /** + * Starts tweening + * @param {number?} time the tween start time + * @returns {TweenBase} this instance + */ + TweenBase.prototype.start = function start (time) { + // now it's a good time to start + add(this); + this.playing = true; + + this._startTime = typeof time !== 'undefined' ? time : KEC.Time(); + this._startTime += this._delay; + + if (!this._startFired) { + if (this._onStart) { + this._onStart.call(this); + } + + queueStart.call(this); + + this._startFired = true; + } + + if (!Tick) { Ticker(); } + return this; + }; + + /** + * Stops tweening + * @returns {TweenBase} this instance + */ + TweenBase.prototype.stop = function stop () { + if (this.playing) { + remove(this); + this.playing = false; + + if (this._onStop) { + this._onStop.call(this); + } + this.close(); + } + return this; + }; + + /** + * Trigger internal completion callbacks. + */ + TweenBase.prototype.close = function close () { + var this$1$1 = this; + + // scroll|transformMatrix need this + Object.keys(onComplete).forEach(function (component) { + Object.keys(onComplete[component]).forEach(function (toClose) { + onComplete[component][toClose].call(this$1$1, toClose); + }); + }); + // when all animations are finished, stop ticking after ~3 frames + this._startFired = false; + stop.call(this); + }; + + /** + * Schedule another tween instance to start once this one completes. + * @param {KUTE.chainOption} args the tween animation start time + * @returns {TweenBase} this instance + */ + TweenBase.prototype.chain = function chain (args) { + this._chain = []; + this._chain = args.length ? args : this._chain.concat(args); + return this; + }; + + /** + * Stop tweening the chained tween instances. + */ + TweenBase.prototype.stopChainedTweens = function stopChainedTweens () { + if (this._chain && this._chain.length) { this._chain.forEach(function (tw) { return tw.stop(); }); } + }; + + /** + * Update the tween on each tick. + * @param {number} time the tick time + * @returns {boolean} this instance + */ + TweenBase.prototype.update = function update (time) { + var this$1$1 = this; + + var T = time !== undefined ? time : KEC.Time(); + + var elapsed; + + if (T < this._startTime && this.playing) { return true; } + + elapsed = (T - this._startTime) / this._duration; + elapsed = (this._duration === 0 || elapsed > 1) ? 1 : elapsed; + + // calculate progress + var progress = this._easing(elapsed); + + // render the update + Object.keys(this.valuesEnd).forEach(function (tweenProp) { + KEC[tweenProp](this$1$1.element, + this$1$1.valuesStart[tweenProp], + this$1$1.valuesEnd[tweenProp], + progress); + }); + + // fire the updateCallback + if (this._onUpdate) { + this._onUpdate.call(this); + } + + if (elapsed === 1) { + // fire the complete callback + if (this._onComplete) { + this._onComplete.call(this); + } + + // now we're sure no animation is running + this.playing = false; + + // stop ticking when finished + this.close(); + + // start animating chained tweens + if (this._chain !== undefined && this._chain.length) { + this._chain.map(function (tw) { return tw.start(); }); + } + + return false; + } + + return true; + }; + + // Update Tween Interface + connect.tween = TweenBase; + + /** + * The `KUTE.Tween()` constructor creates a new `Tween` object + * for a single `HTMLElement` and returns it. + * + * This constructor adds additional functionality and is the default + * Tween object constructor in KUTE.js. + */ + var Tween = /*@__PURE__*/(function (TweenBase) { + function Tween() { + var this$1$1 = this; + var args = [], len = arguments.length; + while ( len-- ) args[ len ] = arguments[ len ]; + + TweenBase.apply(this, args); // this calls the constructor of TweenBase + + // reset interpolation values + this.valuesStart = {}; + this.valuesEnd = {}; + + // const startObject = args[1]; + // const endObject = args[2]; + var ref = args.slice(1); + var startObject = ref[0]; + var endObject = ref[1]; + var options = ref[2]; + + // set valuesEnd + prepareObject.call(this, endObject, 'end'); + + // set valuesStart + if (this._resetStart) { + this.valuesStart = startObject; + } else { + prepareObject.call(this, startObject, 'start'); + } + + // ready for crossCheck + if (!this._resetStart) { + Object.keys(crossCheck).forEach(function (component) { + Object.keys(crossCheck[component]).forEach(function (checkProp) { + crossCheck[component][checkProp].call(this$1$1, checkProp); + }); + }); + } + + // set paused state + /** @type {boolean} */ + this.paused = false; + /** @type {number?} */ + this._pauseTime = null; + + // additional properties and options + /** @type {number?} */ + this._repeat = options.repeat || defaultOptions$1.repeat; + /** @type {number?} */ + this._repeatDelay = options.repeatDelay || defaultOptions$1.repeatDelay; + // we cache the number of repeats to be able to put it back after all cycles finish + /** @type {number?} */ + this._repeatOption = this._repeat; + + // yoyo needs at least repeat: 1 + /** @type {KUTE.tweenProps} */ + this.valuesRepeat = {}; // valuesRepeat + /** @type {boolean} */ + this._yoyo = options.yoyo || defaultOptions$1.yoyo; + /** @type {boolean} */ + this._reversed = false; + + // don't load extra callbacks + // this._onPause = options.onPause || defaultOptions.onPause + // this._onResume = options.onResume || defaultOptions.onResume + + // chained Tweens + // this._chain = options.chain || defaultOptions.chain; + return this; + } + + if ( TweenBase ) Tween.__proto__ = TweenBase; + Tween.prototype = Object.create( TweenBase && TweenBase.prototype ); + Tween.prototype.constructor = Tween; + + /** + * Starts tweening, extended method + * @param {number?} time the tween start time + * @returns {Tween} this instance + */ + Tween.prototype.start = function start (time) { + var this$1$1 = this; + + // on start we reprocess the valuesStart for TO() method + if (this._resetStart) { + this.valuesStart = this._resetStart; + getStartValues.call(this); + + // this is where we do the valuesStart and valuesEnd check for fromTo() method + Object.keys(crossCheck).forEach(function (component) { + Object.keys(crossCheck[component]).forEach(function (checkProp) { + crossCheck[component][checkProp].call(this$1$1, checkProp); + }); + }); + } + // still not paused + this.paused = false; + + // set yoyo values + if (this._yoyo) { + Object.keys(this.valuesEnd).forEach(function (endProp) { + this$1$1.valuesRepeat[endProp] = this$1$1.valuesStart[endProp]; + }); + } + + TweenBase.prototype.start.call(this, time); + + return this; + }; + + /** + * Stops tweening, extended method + * @returns {Tween} this instance + */ + Tween.prototype.stop = function stop () { + TweenBase.prototype.stop.call(this); + if (!this.paused && this.playing) { + this.paused = false; + this.stopChainedTweens(); + } + return this; + }; + + /** + * Trigger internal completion callbacks. + */ + Tween.prototype.close = function close () { + TweenBase.prototype.close.call(this); + + if (this._repeatOption > 0) { + this._repeat = this._repeatOption; + } + if (this._yoyo && this._reversed === true) { + this.reverse(); + this._reversed = false; + } + + return this; + }; + + /** + * Resume tweening + * @returns {Tween} this instance + */ + Tween.prototype.resume = function resume () { + if (this.paused && this.playing) { + this.paused = false; + if (this._onResume !== undefined) { + this._onResume.call(this); + } + // re-queue execution context + queueStart.call(this); + // update time and let it roll + this._startTime += KEC.Time() - this._pauseTime; + add(this); + // restart ticker if stopped + if (!Tick) { Ticker(); } + } + return this; + }; + + /** + * Pause tweening + * @returns {Tween} this instance + */ + Tween.prototype.pause = function pause () { + if (!this.paused && this.playing) { + remove(this); + this.paused = true; + this._pauseTime = KEC.Time(); + if (this._onPause !== undefined) { + this._onPause.call(this); + } + } + return this; + }; + + /** + * Reverses start values with end values + */ + Tween.prototype.reverse = function reverse () { + var this$1$1 = this; + + Object.keys(this.valuesEnd).forEach(function (reverseProp) { + var tmp = this$1$1.valuesRepeat[reverseProp]; + this$1$1.valuesRepeat[reverseProp] = this$1$1.valuesEnd[reverseProp]; + this$1$1.valuesEnd[reverseProp] = tmp; + this$1$1.valuesStart[reverseProp] = this$1$1.valuesRepeat[reverseProp]; + }); + }; + + /** + * Update the tween on each tick. + * @param {number} time the tick time + * @returns {boolean} this instance + */ + Tween.prototype.update = function update (time) { + var this$1$1 = this; + + var T = time !== undefined ? time : KEC.Time(); + + var elapsed; + + if (T < this._startTime && this.playing) { return true; } + + elapsed = (T - this._startTime) / this._duration; + elapsed = (this._duration === 0 || elapsed > 1) ? 1 : elapsed; + + // calculate progress + var progress = this._easing(elapsed); + + // render the update + Object.keys(this.valuesEnd).forEach(function (tweenProp) { + KEC[tweenProp](this$1$1.element, + this$1$1.valuesStart[tweenProp], + this$1$1.valuesEnd[tweenProp], + progress); + }); + + // fire the updateCallback + if (this._onUpdate) { + this._onUpdate.call(this); + } + + if (elapsed === 1) { + if (this._repeat > 0) { + if (Number.isFinite(this._repeat)) { this._repeat -= 1; } + + // set the right time for delay + this._startTime = T; + if (Number.isFinite(this._repeat) && this._yoyo && !this._reversed) { + this._startTime += this._repeatDelay; + } + + if (this._yoyo) { // handle yoyo + this._reversed = !this._reversed; + this.reverse(); + } + + return true; + } + + // fire the complete callback + if (this._onComplete) { + this._onComplete.call(this); + } + + // now we're sure no animation is running + this.playing = false; + + // stop ticking when finished + this.close(); + + // start animating chained tweens + if (this._chain !== undefined && this._chain.length) { + this._chain.forEach(function (tw) { return tw.start(); }); + } + + return false; + } + return true; + }; + + return Tween; + }(TweenBase)); + + // Update Tween Interface Update + connect.tween = Tween; + + // to do + // * per property easing + // * per property duration + // * per property callback + // * per property delay/offset + // * new update method to work with the above + // * other cool ideas + + /** + * The `KUTE.TweenExtra()` constructor creates a new `Tween` object + * for a single `HTMLElement` and returns it. + * + * This constructor is intended for experiments or testing of new features. + */ + var TweenExtra = /*@__PURE__*/(function (Tween) { + function TweenExtra() { + var args = [], len = arguments.length; + while ( len-- ) args[ len ] = arguments[ len ]; + + Tween.apply(this, args); // import constructor of TweenBase -> Tween + + return this; + } + + if ( Tween ) TweenExtra.__proto__ = Tween; + TweenExtra.prototype = Object.create( Tween && Tween.prototype ); + TweenExtra.prototype.constructor = TweenExtra; + + // additional methods + // set/override property + // to(property, value) { + // TO DO + // this.valuesEnd[property] = value // well that's not all + // } + + // fromTo(property, value) { + // TO DO + // this.valuesEnd[property] = value // well that's not all + // } + + // getTotalDuration() { + // to do + // } + + /** + * Method to add callbacks on the fly. + * @param {string} name callback name + * @param {Function} fn callback function + * @returns {TweenExtra} + */ + TweenExtra.prototype.on = function on (name, fn) { + if (['start', 'stop', 'update', 'complete', 'pause', 'resume'].indexOf(name) > -1) { + this[("_on" + (name.charAt(0).toUpperCase() + name.slice(1)))] = fn; + } + return this; + }; + + /** + * Method to set options on the fly. + * * accepting [repeat,yoyo,delay,repeatDelay,easing] + * + * @param {string} option the tick time + * @param {string | number | number[]} value the tick time + * @returns {TweenExtra} + */ + TweenExtra.prototype.option = function option (option$1, value) { + this[("_" + option$1)] = value; + return this; + }; + + return TweenExtra; + }(Tween)); + + // Tween Interface + connect.tween = TweenExtra; + + /** + * The static method creates a new `Tween` object for each `HTMLElement` + * from and `Array`, `HTMLCollection` or `NodeList`. + */ + var TweenCollection = function TweenCollection(els, vS, vE, Options) { + var this$1$1 = this; + + var TweenConstructor = connect.tween; + /** @type {KUTE.twCollection[]} */ + this.tweens = []; + + var Ops = Options || {}; + /** @type {number?} */ + Ops.delay = Ops.delay || defaultOptions$1.delay; + + // set all options + var options = []; + + Array.from(els).forEach(function (el, i) { + options[i] = Ops || {}; + options[i].delay = i > 0 ? Ops.delay + (Ops.offset || defaultOptions$1.offset) : Ops.delay; + if (el instanceof Element) { + this$1$1.tweens.push(new TweenConstructor(el, vS, vE, options[i])); + } else { + throw Error(("KUTE - " + el + " is not instanceof Element")); + } + }); + + /** @type {number?} */ + this.length = this.tweens.length; + return this; + }; + + /** + * Starts tweening, all targets + * @param {number?} time the tween start time + * @returns {TweenCollection} this instance + */ + TweenCollection.prototype.start = function start (time) { + var T = time === undefined ? KEC.Time() : time; + this.tweens.map(function (tween) { return tween.start(T); }); + return this; + }; + + /** + * Stops tweening, all targets and their chains + * @returns {TweenCollection} this instance + */ + TweenCollection.prototype.stop = function stop () { + this.tweens.map(function (tween) { return tween.stop(); }); + return this; + }; + + /** + * Pause tweening, all targets + * @returns {TweenCollection} this instance + */ + TweenCollection.prototype.pause = function pause () { + this.tweens.map(function (tween) { return tween.pause(); }); + return this; + }; + + /** + * Resume tweening, all targets + * @returns {TweenCollection} this instance + */ + TweenCollection.prototype.resume = function resume () { + this.tweens.map(function (tween) { return tween.resume(); }); + return this; + }; + + /** + * Schedule another tween or collection to start after + * this one is complete. + * @param {number?} args the tween start time + * @returns {TweenCollection} this instance + */ + TweenCollection.prototype.chain = function chain (args) { + var lastTween = this.tweens[this.length - 1]; + if (args instanceof TweenCollection) { + lastTween.chain(args.tweens); + } else if (args instanceof connect.tween) { + lastTween.chain(args); + } else { + throw new TypeError('KUTE.js - invalid chain value'); + } + return this; + }; + + /** + * Check if any tween instance is playing + * @param {number?} time the tween start time + * @returns {TweenCollection} this instance + */ + TweenCollection.prototype.playing = function playing () { + return this.tweens.some(function (tw) { return tw.playing; }); + }; + + /** + * Remove all tweens in the collection + */ + TweenCollection.prototype.removeTweens = function removeTweens () { + this.tweens = []; + }; + + /** + * Returns the maximum animation duration + * @returns {number} this instance + */ + TweenCollection.prototype.getMaxDuration = function getMaxDuration () { + var durations = []; + this.tweens.forEach(function (tw) { + durations.push(tw._duration + tw._delay + tw._repeat * tw._repeatDelay); + }); + return Math.max(durations); + }; + + /** + * ProgressBar + * + * @class + * A progress bar utility for KUTE.js that will connect + * a target ``. with a Tween instance + * allowing it to control the progress of the Tween. + */ + var ProgressBar = function ProgressBar(element, tween) { + var assign; + + this.element = selector(element); + this.element.tween = tween; + this.element.tween.toolbar = this.element; + this.element.toolbar = this; + (assign = this.element.parentNode.getElementsByTagName('OUTPUT'), this.element.output = assign[0]); + + // invalidate + if (!(this.element instanceof HTMLInputElement)) { throw TypeError('Target element is not [HTMLInputElement]'); } + if (this.element.type !== 'range') { throw TypeError('Target element is not a range input'); } + if (!(tween instanceof connect.tween)) { throw TypeError(("tween parameter is not [" + (connect.tween) + "]")); } + + this.element.setAttribute('value', 0); + this.element.setAttribute('min', 0); + this.element.setAttribute('max', 1); + this.element.setAttribute('step', 0.0001); + + this.element.tween._onUpdate = this.updateBar; + + this.element.addEventListener('mousedown', this.downAction, false); + }; + + ProgressBar.prototype.updateBar = function updateBar () { + var tick = 0.0001; + var ref = this.toolbar; + var output = ref.output; + + // let progress = this.paused ? this.toolbar.value + // : (KEC.Time() - this._startTime) / this._duration; + // progress = progress > 1 - tick ? 1 : progress < 0.01 ? 0 : progress; + + var progress; + if (this.paused) { + progress = this.toolbar.value; + } else { + progress = (KEC.Time() - this._startTime) / this._duration; + } + + // progress = progress > 1 - tick ? 1 : progress < 0.01 ? 0 : progress; + if (progress > 1 - tick) { progress = 1; } + if (progress < 0.01) { progress = 0; } + + var value = !this._reversed ? progress : 1 - progress; + this.toolbar.value = value; + // eslint-disable-next-line no-bitwise + if (output) { output.value = ((value * 10000 >> 0) / 100) + "%"; } + }; + + ProgressBar.prototype.toggleEvents = function toggleEvents (action) { + // add passive handler ? + this.element[(action + "EventListener")]('mousemove', this.moveAction, false); + this.element[(action + "EventListener")]('mouseup', this.upAction, false); + }; + + ProgressBar.prototype.updateTween = function updateTween () { + // make sure we never complete the tween + var progress = (!this.tween._reversed ? this.value : 1 - this.value) + * this.tween._duration - 0.0001; + + this.tween._startTime = 0; + this.tween.update(progress); + }; + + ProgressBar.prototype.moveAction = function moveAction () { + this.toolbar.updateTween.call(this); + }; + + ProgressBar.prototype.downAction = function downAction () { + if (!this.tween.playing) { + this.tween.start(); + } + + if (!this.tween.paused) { + this.tween.pause(); + this.toolbar.toggleEvents('add'); + + KEC.Tick = cancelAnimationFrame(KEC.Ticker); + } + }; + + ProgressBar.prototype.upAction = function upAction () { + if (this.tween.paused) { + if (this.tween.paused) { this.tween.resume(); } + + this.tween._startTime = KEC.Time() + - (!this.tween._reversed ? this.value : 1 - this.value) * this.tween._duration; + + this.toolbar.toggleEvents('remove'); + KEC.Tick = requestAnimationFrame(KEC.Ticker); + } + }; + + var TweenConstructor$1 = connect.tween; + + /** + * The `KUTE.to()` static method returns a new Tween object + * for a single `HTMLElement` at its current state. + * + * @param {Element} element target element + * @param {KUTE.tweenProps} endObject + * @param {KUTE.tweenOptions} optionsObj tween options + * @returns {KUTE.Tween} the resulting Tween object + */ + function to(element, endObject, optionsObj) { + var options = optionsObj || {}; + options.resetStart = endObject; + return new TweenConstructor$1(selector(element), endObject, endObject, options); + } + + var TweenConstructor = connect.tween; + + /** + * The `KUTE.fromTo()` static method returns a new Tween object + * for a single `HTMLElement` at a given state. + * + * @param {Element} element target element + * @param {KUTE.tweenProps} startObject + * @param {KUTE.tweenProps} endObject + * @param {KUTE.tweenOptions} optionsObj tween options + * @returns {KUTE.Tween} the resulting Tween object + */ + function fromTo(element, startObject, endObject, optionsObj) { + var options = optionsObj || {}; + return new TweenConstructor(selector(element), startObject, endObject, options); + } + + /** + * The `KUTE.allTo()` static method creates a new Tween object + * for multiple `HTMLElement`s, `HTMLCollection` or `NodeListat` + * at their current state. + * + * @param {Element[] | HTMLCollection | NodeList} elements target elements + * @param {KUTE.tweenProps} endObject + * @param {KUTE.tweenProps} optionsObj progress + * @returns {TweenCollection} the Tween object collection + */ + function allTo(elements, endObject, optionsObj) { + var options = optionsObj || {}; + options.resetStart = endObject; + return new TweenCollection(selector(elements, true), endObject, endObject, options); + } + + /** + * The `KUTE.allFromTo()` static method creates a new Tween object + * for multiple `HTMLElement`s, `HTMLCollection` or `NodeListat` + * at a given state. + * + * @param {Element[] | HTMLCollection | NodeList} elements target elements + * @param {KUTE.tweenProps} startObject + * @param {KUTE.tweenProps} endObject + * @param {KUTE.tweenOptions} optionsObj tween options + * @returns {TweenCollection} the Tween object collection + */ + function allFromTo(elements, startObject, endObject, optionsObj) { + var options = optionsObj || {}; + return new TweenCollection(selector(elements, true), startObject, endObject, options); + } + + /** + * Animation Class + * + * Registers components by populating KUTE.js objects and makes sure + * no duplicate component / property is allowed. + */ + var Animation = function Animation(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); + } + + var propertyInfo = this; + var ComponentName = Component.component; + // const Objects = { defaultValues, defaultOptions, Interpolate, linkProperty, Util } + var Functions = { + prepareProperty: prepareProperty, prepareStart: prepareStart, onStart: onStart, onComplete: onComplete, crossCheck: crossCheck, + }; + var Category = Component.category; + var Property = Component.property; + var 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(function (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$1, Component.defaultOptions); + } + + // set functions + if (Component.functions) { + Object.keys(Functions).forEach(function (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(function (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(function (fni) { + var compIntObj = Component.Interpolate[fni]; + if (typeof (compIntObj) === 'function' && !interpolate[fni]) { + interpolate[fni] = compIntObj; + } else { + Object.keys(compIntObj).forEach(function (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(function (fnu) { + if (!Util[fnu]) { Util[fnu] = Component.Util[fnu]; } + }); + } + + return propertyInfo; + }; + + /** + * 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. + */ + var AnimationDevelopment = /*@__PURE__*/(function (Animation) { + function AnimationDevelopment(Component) { + Animation.call(this, Component); + + var propertyInfo = this; + // const Objects = { defaultValues, defaultOptions, Interpolate, linkProperty, Util } + var Functions = { + prepareProperty: prepareProperty, prepareStart: prepareStart, onStart: onStart, onComplete: onComplete, crossCheck: crossCheck, + }; + var Category = Component.category; + var Property = Component.property; + var 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(function (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(function (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(function (fni) { + var 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(function (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; + } + + if ( Animation ) AnimationDevelopment.__proto__ = Animation; + AnimationDevelopment.prototype = Object.create( Animation && Animation.prototype ); + AnimationDevelopment.prototype.constructor = AnimationDevelopment; + + return AnimationDevelopment; + }(Animation)); + + /** + * Numbers Interpolation Function. + * + * @param {number} a start value + * @param {number} b end value + * @param {number} v progress + * @returns {number} the interpolated number + */ + function numbers(a, b, v) { + var A = +a; + var B = b - a; + // a = +a; b -= a; + return A + B * v; + } + + /** + * trueDimension + * + * Returns the string value of a specific CSS property converted into a nice + * { v = value, u = unit } object. + * + * @param {string} dimValue the property string value + * @param {boolean | number} isAngle sets the utility to investigate angles + * @returns {{v: number, u: string}} the true {value, unit} tuple + */ + var trueDimension = function (dimValue, isAngle) { + var intValue = parseInt(dimValue, 10) || 0; + var mUnits = ['px', '%', 'deg', 'rad', 'em', 'rem', 'vh', 'vw']; + var theUnit; + + for (var mIndex = 0; mIndex < mUnits.length; mIndex += 1) { + if (typeof dimValue === 'string' && dimValue.includes(mUnits[mIndex])) { + theUnit = mUnits[mIndex]; break; + } + } + if (theUnit === undefined) { + theUnit = isAngle ? 'deg' : 'px'; + } + + return { v: intValue, u: theUnit }; + }; + + // Component Functions + /** + * Sets the property update function. + * @param {string} prop the property name + */ + function onStartBgPos(prop) { + if (this.valuesEnd[prop] && !KEC[prop]) { + KEC[prop] = function (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 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) { + var x = trueDimension(value[0]).v; + var y = trueDimension(value[1]).v; + return [!Number.isNaN(x * 1) ? x : 50, !Number.isNaN(y * 1) ? y : 50]; + } + + var 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 + var bgPositionFunctions = { + prepareStart: getBgPos, + prepareProperty: prepareBgPos, + onStart: onStartBgPos, + }; + + // Component Full Object + var BackgroundPosition = { + component: 'backgroundPositionProp', + property: 'backgroundPosition', + defaultValue: [50, 50], + Interpolate: { numbers: numbers }, + functions: bgPositionFunctions, + Util: { trueDimension: trueDimension }, + }; + + /** + * Units Interpolation Function. + * + * @param {number} a start value + * @param {number} b end value + * @param {string} u unit + * @param {number} v progress + * @returns {string} the interpolated value + unit string + */ + function units(a, b, u, v) { // number1, number2, unit, progress + var A = +a; + var B = b - a; + // a = +a; b -= a; + return (A + B * v) + u; + } + + /* borderRadius = { + category: 'borderRadius', + properties : [..], + defaultValues: {..}, + interpolation: {units} + } */ + + // Component Properties + var radiusProps$1 = [ + 'borderRadius', + 'borderTopLeftRadius', 'borderTopRightRadius', + 'borderBottomLeftRadius', 'borderBottomRightRadius' ]; + + // Component Functions + /** + * Sets the property update function. + * @param {string} tweenProp the property name + */ + function radiusOnStartFn(tweenProp) { + if (tweenProp in this.valuesEnd && !KEC[tweenProp]) { + KEC[tweenProp] = function (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); + }; + } + } + var radiusOnStart$1 = {}; + radiusProps$1.forEach(function (tweenProp) { + radiusOnStart$1[tweenProp] = radiusOnStartFn; + }); + + // Component Properties + var radiusProps = [ + 'borderRadius', + 'borderTopLeftRadius', 'borderTopRightRadius', + 'borderBottomLeftRadius', 'borderBottomRightRadius']; + + var radiusValues = {}; + radiusProps.forEach(function (x) { radiusValues[x] = 0; }); + + // Component Functions + var radiusOnStart = {}; + radiusProps.forEach(function (tweenProp) { + radiusOnStart[tweenProp] = radiusOnStartFn; + }); + + /** + * Returns the current property computed style. + * @param {string} tweenProp the property name + * @returns {string} the property computed style + */ + 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 + */ + function prepareRadius(/* tweenProp, */_, value) { + return trueDimension(value); + } + + // All Component Functions + var radiusFunctions = { + prepareStart: getRadius, + prepareProperty: prepareRadius, + onStart: radiusOnStart, + }; + + // Full Component + var BorderRadius = { + component: 'borderRadiusProperties', + category: 'borderRadius', + properties: radiusProps, + defaultValues: radiusValues, + Interpolate: { units: units }, + functions: radiusFunctions, + Util: { trueDimension: trueDimension }, + }; + + // Component Functions + /** + * Sets the update function for the property. + * @param {string} tweenProp the property name + */ + function boxModelOnStart(tweenProp) { + if (tweenProp in this.valuesEnd && !KEC[tweenProp]) { + KEC[tweenProp] = function (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 + var baseBoxProps = ['top', 'left', 'width', 'height']; + var baseBoxOnStart = {}; + baseBoxProps.forEach(function (x) { baseBoxOnStart[x] = boxModelOnStart; }); + + // Component Properties + var 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']; + + var boxModelValues = {}; + boxModelProperties.forEach(function (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) { + var boxValue = trueDimension(value); var + offsetProp = tweenProp === 'height' ? 'offsetHeight' : 'offsetWidth'; + return boxValue.u === '%' ? (boxValue.v * this.element[offsetProp]) / 100 : boxValue.v; + } + var boxPropsOnStart = {}; + boxModelProperties.forEach(function (x) { boxPropsOnStart[x] = boxModelOnStart; }); + + // All Component Functions + var boxModelFunctions = { + prepareStart: getBoxModel, + prepareProperty: prepareBoxModel, + onStart: boxPropsOnStart, + }; + + // Component Full Component + var BoxModel = { + component: 'boxModelProperties', + category: 'boxModel', + properties: boxModelProperties, + defaultValues: boxModelValues, + Interpolate: { numbers: numbers }, + functions: boxModelFunctions, + }; + + // Component Functions + /** + * Sets the property update function. + * @param {string} tweenProp the property name + */ + function onStartClip(tweenProp) { + if (this.valuesEnd[tweenProp] && !KEC[tweenProp]) { + KEC[tweenProp] = function (elem, a, b, v) { + var h = 0; var + cl = []; + for (h; h < 4; h += 1) { + var c1 = a[h].v; + var c2 = b[h].v; + var 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 Functions + /** + * Returns the current property computed style. + * @param {string} tweenProp the property name + * @returns {string | number[]} computed style for property + */ + function getClip(tweenProp/* , value */) { + var ref = this; + var element = ref.element; + var currentClip = getStyleForProperty(element, tweenProp); + var width = getStyleForProperty(element, 'width'); + var 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(function (x) { return trueDimension(x); }); + } + var clipValue = value.replace(/rect|\(|\)/g, ''); + clipValue = /,/g.test(clipValue) ? clipValue.split(',') : clipValue.split(/\s/); + return clipValue.map(function (x) { return trueDimension(x); }); + } + + // All Component Functions + var clipFunctions = { + prepareStart: getClip, + prepareProperty: prepareClip, + onStart: onStartClip, + }; + + // Component Full + var ClipProperty = { + component: 'clipProperty', + property: 'clip', + defaultValue: [0, 0, 0, 0], + Interpolate: { numbers: numbers }, + functions: clipFunctions, + Util: { trueDimension: trueDimension }, + }; + + /** + * hexToRGB + * + * Converts a #HEX color format into RGB + * and returns a color object {r,g,b}. + * + * @param {string} hex the degree angle + * @returns {KUTE.colorObject | null} the radian angle + */ + var hexToRGB = function (hex) { + // Expand shorthand form (e.g. "03F") to full form (e.g. "0033FF") + var hexShorthand = /^#?([a-f\d])([a-f\d])([a-f\d])$/i; + var HEX = hex.replace(hexShorthand, function (_, r, g, b) { return r + r + g + g + b + b; }); + var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(HEX); + + return result ? { + r: parseInt(result[1], 16), + g: parseInt(result[2], 16), + b: parseInt(result[3], 16), + } : null; + }; + + /** + * trueColor + * + * Transform any color to rgba()/rgb() and return a nice RGB(a) object. + * + * @param {string} colorString the color input + * @returns {KUTE.colorObject} the {r,g,b,a} color object + */ + var trueColor = function (colorString) { + var result; + if (/rgb|rgba/.test(colorString)) { // first check if it's a rgb string + var vrgb = colorString.replace(/\s|\)/, '').split('(')[1].split(','); + var colorAlpha = vrgb[3] ? vrgb[3] : null; + if (!colorAlpha) { + result = { r: parseInt(vrgb[0], 10), g: parseInt(vrgb[1], 10), b: parseInt(vrgb[2], 10) }; + } + result = { + r: parseInt(vrgb[0], 10), + g: parseInt(vrgb[1], 10), + b: parseInt(vrgb[2], 10), + a: parseFloat(colorAlpha), + }; + } if (/^#/.test(colorString)) { + var fromHex = hexToRGB(colorString); + result = { r: fromHex.r, g: fromHex.g, b: fromHex.b }; + } if (/transparent|none|initial|inherit/.test(colorString)) { + result = { + r: 0, g: 0, b: 0, a: 0, + }; + } + // maybe we can check for web safe colors + // only works in a browser + if (!/^#|^rgb/.test(colorString)) { + var siteHead = document.getElementsByTagName('head')[0]; + siteHead.style.color = colorString; + var webColor = getComputedStyle(siteHead, null).color; + webColor = /rgb/.test(webColor) ? webColor.replace(/[^\d,]/g, '').split(',') : [0, 0, 0]; + siteHead.style.color = ''; + result = { + r: parseInt(webColor[0], 10), + g: parseInt(webColor[1], 10), + b: parseInt(webColor[2], 10), + }; + } + return result; + }; + + /** + * Color Interpolation Function. + * + * @param {KUTE.colorObject} a start color + * @param {KUTE.colorObject} b end color + * @param {number} v progress + * @returns {string} the resulting color + */ + function colors(a, b, v) { + var _c = {}; + var ep = ')'; + var cm = ','; + var rgb = 'rgb('; + var rgba = 'rgba('; + + Object.keys(b).forEach(function (c) { + if (c !== 'a') { + _c[c] = numbers(a[c], b[c], v) >> 0 || 0; // eslint-disable-line no-bitwise + } else if (a[c] && b[c]) { + _c[c] = (numbers(a[c], b[c], v) * 100 >> 0) / 100; // eslint-disable-line no-bitwise + } + }); + + return !_c.a + ? rgb + _c.r + cm + _c.g + cm + _c.b + ep + : rgba + _c.r + cm + _c.g + cm + _c.b + cm + _c.a + ep; + } + + // Component Interpolation + // rgba1, rgba2, progress + + // Component Properties + // supported formats + // 'hex', 'rgb', 'rgba' '#fff' 'rgb(0,0,0)' / 'rgba(0,0,0,0)' 'red' (IE9+) + var supportedColors$1 = [ + 'color', 'backgroundColor', 'outlineColor', + 'borderColor', + 'borderTopColor', 'borderRightColor', + 'borderBottomColor', 'borderLeftColor' ]; + + // Component Functions + /** + * Sets the property update function. + * @param {string} tweenProp the property name + */ + function onStartColors(tweenProp) { + if (this.valuesEnd[tweenProp] && !KEC[tweenProp]) { + KEC[tweenProp] = function (elem, a, b, v) { + // eslint-disable-next-line no-param-reassign + elem.style[tweenProp] = colors(a, b, v); + }; + } + } + + var colorsOnStart$1 = {}; + supportedColors$1.forEach(function (x) { colorsOnStart$1[x] = onStartColors; }); + + // Component Properties + // supported formats + // 'hex', 'rgb', 'rgba' '#fff' 'rgb(0,0,0)' / 'rgba(0,0,0,0)' 'red' (IE9+) + var supportedColors = [ + 'color', 'backgroundColor', 'outlineColor', + 'borderColor', 'borderTopColor', 'borderRightColor', + 'borderBottomColor', 'borderLeftColor' ]; + + var defaultColors = {}; + supportedColors.forEach(function (tweenProp) { + defaultColors[tweenProp] = '#000'; + }); + + // Component Functions + var colorsOnStart = {}; + supportedColors.forEach(function (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 + var colorFunctions = { + prepareStart: getColor, + prepareProperty: prepareColor, + onStart: colorsOnStart, + }; + + // Component Full + var colorProperties = { + component: 'colorProperties', + category: 'colors', + properties: supportedColors, + defaultValues: defaultColors, + Interpolate: { numbers: numbers, colors: colors }, + functions: colorFunctions, + Util: { trueColor: trueColor }, + }; + + // Component Interpolation + /** + * Sets the `drop-shadow` sub-property update function. + * * disimbiguation `dropshadow` interpolation function and `dropShadow` property + * @param {string} tweenProp the property name + */ + function dropshadow(a, b, v) { + var params = []; + var unit = 'px'; + + for (var 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 + */ + function onStartFilter(tweenProp) { + if (this.valuesEnd[tweenProp] && !KEC[tweenProp]) { + KEC[tweenProp] = function (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 */ + }; + } + } + + /* 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) { + var newShadow; + + if (shadow.length === 3) { // [h-shadow, v-shadow, color] + newShadow = [shadow[0], shadow[1], 0, shadow[2]]; + } else if (shadow.length === 4) { // ideal [, , , ] + newShadow = [shadow[0], shadow[1], shadow[2], shadow[3]]; + } + + // make sure the values are ready to tween + for (var 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) { + var result = {}; + var fnReg = /(([a-z].*?)\(.*?\))(?=\s([a-z].*?)\(.*?\)|\s*$)/g; + var matches = currentStyle.match(fnReg); + var fnArray = currentStyle !== 'none' ? matches : 'none'; + + if (fnArray instanceof Array) { + for (var j = 0, jl = fnArray.length; j < jl; j += 1) { + var p = fnArray[j].trim().split(/\((.+)/); + var pp = replaceDashNamespace(p[0]); + if (pp === 'dropShadow') { + var shadowColor = p[1].match(/(([a-z].*?)\(.*?\))(?=\s(.*?))/)[0]; + var params = p[1].replace(shadowColor, '').split(/\s/).map(parseFloat); + result[pp] = params.filter(function (el) { return !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) { + var currentStyle = getStyleForProperty(this.element, tweenProp); + var filterObject = parseFilterString(currentStyle); + var fnp; + + Object.keys(value).forEach(function (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) { + var filterObject = {}; + var 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(function (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) { + var this$1$1 = this; + + if (this.valuesEnd[tweenProp]) { + Object.keys(this.valuesStart[tweenProp]).forEach(function (fn) { + if (!this$1$1.valuesEnd[tweenProp][fn]) { + this$1$1.valuesEnd[tweenProp][fn] = this$1$1.valuesStart[tweenProp][fn]; + } + }); + } + } + + // All Component Functions + var filterFunctions = { + prepareStart: getFilter, + prepareProperty: prepareFilter, + onStart: onStartFilter, + crossCheck: crossCheckFilter, + }; + + // Full Component + var 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: numbers, colors: colors, dropshadow: dropshadow }, + }, + functions: filterFunctions, + Util: { + parseDropShadow: parseDropShadow, parseFilterString: parseFilterString, replaceDashNamespace: replaceDashNamespace, trueColor: trueColor, + }, + }; + + // Component Special + var attributes = {}; + + var onStartAttr = { + /** + * onStartAttr.attr + * + * Sets the sub-property update function. + * @param {string} tweenProp the property name + */ + attr: function attr(tweenProp) { + if (!KEC[tweenProp] && this.valuesEnd[tweenProp]) { + KEC[tweenProp] = function (elem, vS, vE, v) { + Object.keys(vE).forEach(function (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: function attributes$1(tweenProp) { + if (!KEC[tweenProp] && this.valuesEnd.attr) { + KEC[tweenProp] = attributes; + } + }, + }; + + // Component Name + var ComponentName = 'htmlAttributes'; + + // Component Properties + var 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 + */ + function getAttr(/* tweenProp, */_, value) { + var this$1$1 = this; + + var attrStartValues = {}; + Object.keys(value).forEach(function (attr) { + // get the value for 'fill-opacity' not fillOpacity + // also 'width' not the internal 'width_px' + var attribute = replaceUppercase(attr).replace(/_+[a-z]+/, ''); + var currentValue = this$1$1.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 + */ + function prepareAttr(tweenProp, attrObj) { + var this$1$1 = this; + // attr (string),attrObj (object) + var attributesObject = {}; + + Object.keys(attrObj).forEach(function (p) { + var prop = replaceUppercase(p); + var regex = /(%|[a-z]+)$/; + var currentValue = this$1$1.element.getAttribute(prop.replace(/_+[a-z]+/, '')); + + if (!svgColors.includes(prop)) { + // attributes set with unit suffixes + if (currentValue !== null && regex.test(currentValue)) { + var unit = trueDimension(currentValue).u || trueDimension(attrObj[p]).u; + var suffix = /%/.test(unit) ? '_percent' : ("_" + unit); + + // most "unknown" attributes cannot register into onStart, so we manually add them + onStart[ComponentName][prop + suffix] = function (tp) { + if (this$1$1.valuesEnd[tweenProp] && this$1$1.valuesEnd[tweenProp][tp] && !(tp in attributes)) { + attributes[tp] = function (elem, oneAttr, a, b, v) { + var _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] = function (tp) { + if (this$1$1.valuesEnd[tweenProp] && this$1$1.valuesEnd[tweenProp][tp] && !(tp in attributes)) { + attributes[tp] = function (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] = function (tp) { + if (this$1$1.valuesEnd[tweenProp] && this$1$1.valuesEnd[tweenProp][tp] && !(tp in attributes)) { + attributes[tp] = function (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 + var attrFunctions = { + prepareStart: getAttr, + prepareProperty: prepareAttr, + onStart: onStartAttr, + }; + + // Component Full + var 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: numbers, colors: colors }, + functions: attrFunctions, + // export to global for faster execution + Util: { replaceUppercase: replaceUppercase, trueColor: trueColor, trueDimension: trueDimension }, + }; + + /* opacityProperty = { + property: 'opacity', + defaultValue: 1, + interpolators: {numbers}, + functions = { prepareStart, prepareProperty, onStart } + } */ + + // Component Functions + /** + * Sets the property update function. + * @param {string} tweenProp the property name + */ + function onStartOpacity(tweenProp/* , value */) { + // opacity could be 0 sometimes, we need to check regardless + if (tweenProp in this.valuesEnd && !KEC[tweenProp]) { + KEC[tweenProp] = function (elem, a, b, v) { + /* eslint-disable */ + elem.style[tweenProp] = ((numbers(a, b, v) * 1000) >> 0) / 1000; + /* eslint-enable */ + }; + } + } + + // 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 + var opacityFunctions = { + prepareStart: getOpacity, + prepareProperty: prepareOpacity, + onStart: onStartOpacity, + }; + + // Full Component + var OpacityProperty = { + component: 'opacityProperty', + property: 'opacity', + defaultValue: 1, + Interpolate: { numbers: numbers }, + functions: opacityFunctions, + }; + + // Component Functions + /** + * Sets the property update function. + * @param {string} tweenProp the property name + */ + function onStartDraw(tweenProp) { + if (tweenProp in this.valuesEnd && !KEC[tweenProp]) { + KEC[tweenProp] = function (elem, a, b, v) { + /* eslint-disable no-bitwise -- impossible to satisfy */ + var pathLength = (a.l * 100 >> 0) / 100; + var start = (numbers(a.s, b.s, v) * 100 >> 0) / 100; + var end = (numbers(a.e, b.e, v) * 100 >> 0) / 100; + var offset = 0 - start; + var 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 Util + /** + * Convert a `` 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 `` 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 `` length + */ + function getRectLength(el) { + var w = el.getAttribute('width'); + var h = el.getAttribute('height'); + return (w * 2) + (h * 2); + } + + /** + * Returns the `` / `` length. + * @param {SVGPolylineElement | SVGPolygonElement} el target element + * @returns {number} the element length + */ + function getPolyLength(el) { + var points = el.getAttribute('points').split(' '); + + var len = 0; + if (points.length > 1) { + var coord = function (p) { + var 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])]; + }; + + var dist = function (c1, c2) { + if (c1 !== undefined && c2 !== undefined) { + return Math.sqrt(Math.pow( (c2[0] - c1[0]), 2 ) + Math.pow( (c2[1] - c1[1]), 2 )); + } + return 0; + }; + + if (points.length > 2) { + for (var 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 `` length. + * @param {SVGLineElement} el target element + * @returns {number} the element length + */ + function getLineLength(el) { + var x1 = el.getAttribute('x1'); + var x2 = el.getAttribute('x2'); + var y1 = el.getAttribute('y1'); + var y2 = el.getAttribute('y2'); + return Math.sqrt(Math.pow( (x2 - x1), 2 ) + Math.pow( (y2 - y1), 2 )); + } + + /** + * Returns the `` length. + * @param {SVGCircleElement} el target element + * @returns {number} the element length + */ + function getCircleLength(el) { + var r = el.getAttribute('r'); + return 2 * Math.PI * r; + } + + // returns the length of an ellipse + /** + * Returns the `` length. + * @param {SVGEllipseElement} el target element + * @returns {number} the element length + */ + function getEllipseLength(el) { + var rx = el.getAttribute('rx'); + var ry = el.getAttribute('ry'); + var len = 2 * rx; + var 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) { + var length = /path|glyph/.test(element.tagName) + ? element.getTotalLength() + : getTotalLength(element); + var start; + var end; + var dasharray; + var offset; + + if (value instanceof Object && Object.keys(value).every(function (v) { return ['s', 'e', 'l'].includes(v); })) { + return value; + } if (typeof value === 'string') { + var 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 + var svgDrawFunctions = { + prepareStart: getDrawValue, + prepareProperty: prepareDraw, + onStart: onStartDraw, + }; + + // Component Full + var SvgDrawProperty = { + component: 'svgDraw', + property: 'draw', + defaultValue: '0% 0%', + Interpolate: { numbers: numbers }, + functions: svgDrawFunctions, + // Export to global for faster execution + Util: { + getRectLength: getRectLength, + getPolyLength: getPolyLength, + getLineLength: getLineLength, + getCircleLength: getCircleLength, + getEllipseLength: getEllipseLength, + getTotalLength: getTotalLength, + resetDraw: resetDraw, + getDraw: getDraw, + percent: percent, + }, + }; + + /** + * Segment params length + * @type {Record} + */ + var paramsCount = { + a: 7, c: 6, h: 1, l: 2, m: 2, r: 4, q: 4, s: 4, t: 2, v: 1, z: 0, + }; + + /** + * Breaks the parsing of a pathString once a segment is finalized. + * + * @param {SVGPathCommander.PathParser} path the `PathParser` instance + */ + function finalizeSegment(path) { + var pathCommand = path.pathValue[path.segmentStart]; + var LK = pathCommand.toLowerCase(); + var data = path.data; + + // Process duplicated commands (without comand name) + if (LK === 'm' && data.length > 2) { + // @ts-ignore + path.segments.push([pathCommand, data[0], data[1]]); + data = data.slice(2); + LK = 'l'; + pathCommand = pathCommand === 'm' ? 'l' : 'L'; + } + + // @ts-ignore + while (data.length >= paramsCount[LK]) { + // path.segments.push([pathCommand].concat(data.splice(0, paramsCount[LK]))); + // @ts-ignore + path.segments.push([pathCommand ].concat( data.splice(0, paramsCount[LK]))); + // @ts-ignore + if (!paramsCount[LK]) { + break; + } + } + } + + var invalidPathValue = 'Invalid path value'; + + /** + * Validates an A (arc-to) specific path command value. + * Usually a `large-arc-flag` or `sweep-flag`. + * + * @param {SVGPathCommander.PathParser} path the `PathParser` instance + */ + function scanFlag(path) { + var index = path.index; + var ch = path.pathValue.charCodeAt(index); + + if (ch === 0x30/* 0 */) { + path.param = 0; + path.index += 1; + return; + } + + if (ch === 0x31/* 1 */) { + path.param = 1; + path.index += 1; + return; + } + + path.err = invalidPathValue + ": invalid Arc flag \"" + ch + "\", expecting 0 or 1 at index " + index; + } + + /** + * Checks if a character is a digit. + * + * @param {number} code the character to check + * @returns {boolean} check result + */ + function isDigit(code) { + return (code >= 48 && code <= 57); // 0..9 + } + + /** + * Validates every character of the path string, + * every path command, negative numbers or floating point numbers. + * + * @param {SVGPathCommander.PathParser} path the `PathParser` instance + */ + function scanParam(path) { + var max = path.max; + var pathValue = path.pathValue; + var start = path.index; + var index = start; + var zeroFirst = false; + var hasCeiling = false; + var hasDecimal = false; + var hasDot = false; + var ch; + + if (index >= max) { + // path.err = 'SvgPath: missed param (at pos ' + index + ')'; + path.err = invalidPathValue + " at " + index + ": missing param " + (pathValue[index]); + return; + } + ch = pathValue.charCodeAt(index); + + if (ch === 0x2B/* + */ || ch === 0x2D/* - */) { + index += 1; + ch = (index < max) ? pathValue.charCodeAt(index) : 0; + } + + // This logic is shamelessly borrowed from Esprima + // https://github.com/ariya/esprimas + if (!isDigit(ch) && ch !== 0x2E/* . */) { + // path.err = 'SvgPath: param should start with 0..9 or `.` (at pos ' + index + ')'; + path.err = invalidPathValue + " at index " + index + ": " + (pathValue[index]) + " is not a number"; + return; + } + + if (ch !== 0x2E/* . */) { + zeroFirst = (ch === 0x30/* 0 */); + index += 1; + + ch = (index < max) ? pathValue.charCodeAt(index) : 0; + + if (zeroFirst && index < max) { + // decimal number starts with '0' such as '09' is illegal. + if (ch && isDigit(ch)) { + // path.err = 'SvgPath: numbers started with `0` such as `09` + // are illegal (at pos ' + start + ')'; + path.err = invalidPathValue + " at index " + start + ": " + (pathValue[start]) + " illegal number"; + return; + } + } + + while (index < max && isDigit(pathValue.charCodeAt(index))) { + index += 1; + hasCeiling = true; + } + ch = (index < max) ? pathValue.charCodeAt(index) : 0; + } + + if (ch === 0x2E/* . */) { + hasDot = true; + index += 1; + while (isDigit(pathValue.charCodeAt(index))) { + index += 1; + hasDecimal = true; + } + ch = (index < max) ? pathValue.charCodeAt(index) : 0; + } + + if (ch === 0x65/* e */ || ch === 0x45/* E */) { + if (hasDot && !hasCeiling && !hasDecimal) { + path.err = invalidPathValue + " at index " + index + ": " + (pathValue[index]) + " invalid float exponent"; + return; + } + + index += 1; + + ch = (index < max) ? pathValue.charCodeAt(index) : 0; + if (ch === 0x2B/* + */ || ch === 0x2D/* - */) { + index += 1; + } + if (index < max && isDigit(pathValue.charCodeAt(index))) { + while (index < max && isDigit(pathValue.charCodeAt(index))) { + index += 1; + } + } else { + // path.err = 'SvgPath: invalid float exponent (at pos ' + index + ')'; + path.err = invalidPathValue + " at index " + index + ": " + (pathValue[index]) + " invalid float exponent"; + return; + } + } + + path.index = index; + path.param = +path.pathValue.slice(start, index); + } + + /** + * Checks if the character is a space. + * + * @param {number} ch the character to check + * @returns {boolean} check result + */ + function isSpace(ch) { + var specialSpaces = [ + 0x1680, 0x180E, 0x2000, 0x2001, 0x2002, 0x2003, 0x2004, 0x2005, 0x2006, + 0x2007, 0x2008, 0x2009, 0x200A, 0x202F, 0x205F, 0x3000, 0xFEFF]; + return (ch === 0x0A) || (ch === 0x0D) || (ch === 0x2028) || (ch === 0x2029) // Line terminators + // White spaces + || (ch === 0x20) || (ch === 0x09) || (ch === 0x0B) || (ch === 0x0C) || (ch === 0xA0) + || (ch >= 0x1680 && specialSpaces.indexOf(ch) >= 0); + } + + /** + * Points the parser to the next character in the + * path string every time it encounters any kind of + * space character. + * + * @param {SVGPathCommander.PathParser} path the `PathParser` instance + */ + function skipSpaces(path) { + var pathValue = path.pathValue; + var max = path.max; + while (path.index < max && isSpace(pathValue.charCodeAt(path.index))) { + path.index += 1; + } + } + + /** + * Checks if the character is a path command. + * + * @param {any} code the character to check + * @returns {boolean} check result + */ + function isPathCommand(code) { + // eslint-disable-next-line no-bitwise -- Impossible to satisfy + switch (code | 0x20) { + case 0x6D/* m */: + case 0x7A/* z */: + case 0x6C/* l */: + case 0x68/* h */: + case 0x76/* v */: + case 0x63/* c */: + case 0x73/* s */: + case 0x71/* q */: + case 0x74/* t */: + case 0x61/* a */: + // case 0x72/* r */: + return true; + default: + return false; + } + } + + /** + * Checks if the character is or belongs to a number. + * [0-9]|+|-|. + * + * @param {number} code the character to check + * @returns {boolean} check result + */ + function isDigitStart(code) { + return (code >= 48 && code <= 57) /* 0..9 */ + || code === 0x2B /* + */ + || code === 0x2D /* - */ + || code === 0x2E; /* . */ + } + + /** + * Checks if the character is an A (arc-to) path command. + * + * @param {number} code the character to check + * @returns {boolean} check result + */ + function isArcCommand(code) { + // eslint-disable-next-line no-bitwise -- Impossible to satisfy + return (code | 0x20) === 0x61; + } + + /** + * Scans every character in the path string to determine + * where a segment starts and where it ends. + * + * @param {SVGPathCommander.PathParser} path the `PathParser` instance + */ + function scanSegment(path) { + var max = path.max; + var pathValue = path.pathValue; + var index = path.index; + var cmdCode = pathValue.charCodeAt(index); + var reqParams = paramsCount[pathValue[index].toLowerCase()]; + + path.segmentStart = index; + + if (!isPathCommand(cmdCode)) { + path.err = invalidPathValue + ": " + (pathValue[index]) + " not a path command"; + return; + } + + path.index += 1; + skipSpaces(path); + + path.data = []; + + if (!reqParams) { + // Z + finalizeSegment(path); + return; + } + + for (;;) { + for (var i = reqParams; i > 0; i -= 1) { + if (isArcCommand(cmdCode) && (i === 3 || i === 4)) { scanFlag(path); } + else { scanParam(path); } + + if (path.err.length) { + return; + } + path.data.push(path.param); + + skipSpaces(path); + + // after ',' param is mandatory + if (path.index < max && pathValue.charCodeAt(path.index) === 0x2C/* , */) { + path.index += 1; + skipSpaces(path); + } + } + + if (path.index >= path.max) { + break; + } + + // Stop on next segment + if (!isDigitStart(pathValue.charCodeAt(path.index))) { + break; + } + } + + finalizeSegment(path); + } + + /** + * Returns a clone of an existing `pathArray`. + * + * @param {SVGPathCommander.pathArray | SVGPathCommander.pathSegment} path the source `pathArray` + * @returns {any} the cloned `pathArray` + */ + function clonePath(path) { + return path.map(function (x) { return (Array.isArray(x) ? [].concat( x ) : x); }); + } + + /** + * The `PathParser` is used by the `parsePathString` static method + * to generate a `pathArray`. + * + * @param {string} pathString + */ + function PathParser(pathString) { + /** @type {SVGPathCommander.pathArray} */ + // @ts-ignore + this.segments = []; + /** @type {string} */ + this.pathValue = pathString; + /** @type {number} */ + this.max = pathString.length; + /** @type {number} */ + this.index = 0; + /** @type {number} */ + this.param = 0.0; + /** @type {number} */ + this.segmentStart = 0; + /** @type {any} */ + this.data = []; + /** @type {string} */ + this.err = ''; + } + + /** + * Iterates an array to check if it's an actual `pathArray`. + * + * @param {string | SVGPathCommander.pathArray} path the `pathArray` to be checked + * @returns {boolean} iteration result + */ + function isPathArray(path) { + return Array.isArray(path) && path.every(function (seg) { + var lk = seg[0].toLowerCase(); + return paramsCount[lk] === seg.length - 1 && 'achlmqstvz'.includes(lk); + }); + } + + /** + * Parses a path string value and returns an array + * of segments we like to call `pathArray`. + * + * @param {SVGPathCommander.pathArray | string} pathInput the string to be parsed + * @returns {SVGPathCommander.pathArray} the resulted `pathArray` + */ + function parsePathString(pathInput) { + if (isPathArray(pathInput)) { + // @ts-ignore -- isPathArray also checks if it's an `Array` + return clonePath(pathInput); + } + + // @ts-ignore -- pathInput is now string + var path = new PathParser(pathInput); + + skipSpaces(path); + + while (path.index < path.max && !path.err.length) { + scanSegment(path); + } + + if (path.err.length) { + // @ts-ignore + path.segments = []; + } else if (path.segments.length) { + if (!'mM'.includes(path.segments[0][0])) { + path.err = invalidPathValue + ": missing M/m"; + // @ts-ignore + path.segments = []; + } else { + path.segments[0][0] = 'M'; + } + } + + return path.segments; + } + + /** + * Iterates an array to check if it's a `pathArray` + * with all absolute values. + * + * @param {string | SVGPathCommander.pathArray} path the `pathArray` to be checked + * @returns {boolean} iteration result + */ + function isAbsoluteArray(path) { + return isPathArray(path) + // @ts-ignore -- `isPathArray` also checks if it's `Array` + && path.every(function (x) { return x[0] === x[0].toUpperCase(); }); + } + + /** + * Parses a path string value or object and returns an array + * of segments, all converted to absolute values. + * + * @param {string | SVGPathCommander.pathArray} pathInput the path string | object + * @returns {SVGPathCommander.absoluteArray} the resulted `pathArray` with absolute values + */ + function pathToAbsolute(pathInput) { + if (isAbsoluteArray(pathInput)) { + // @ts-ignore -- `isAbsoluteArray` checks if it's `pathArray` + return clonePath(pathInput); + } + + var path = parsePathString(pathInput); + var x = 0; var y = 0; + var mx = 0; var my = 0; + + // @ts-ignore -- the `absoluteSegment[]` is for sure an `absolutePath` + return path.map(function (segment) { + var assign, assign$1, assign$2; + + var values = segment.slice(1).map(Number); + var pathCommand = segment[0]; + /** @type {SVGPathCommander.absoluteCommand} */ + // @ts-ignore + var absCommand = pathCommand.toUpperCase(); + + if (pathCommand === 'M') { + (assign = values, x = assign[0], y = assign[1]); + mx = x; + my = y; + return ['M', x, y]; + } + /** @type {SVGPathCommander.absoluteSegment} */ + // @ts-ignore + var absoluteSegment = []; + + if (pathCommand !== absCommand) { + switch (absCommand) { + case 'A': + absoluteSegment = [ + absCommand, values[0], values[1], values[2], + values[3], values[4], values[5] + x, values[6] + y]; + break; + case 'V': + absoluteSegment = [absCommand, values[0] + y]; + break; + case 'H': + absoluteSegment = [absCommand, values[0] + x]; + break; + default: { + // use brakets for `eslint: no-case-declaration` + // https://stackoverflow.com/a/50753272/803358 + var absValues = values.map(function (n, j) { return n + (j % 2 ? y : x); }); + // @ts-ignore for n, l, c, s, q, t + absoluteSegment = [absCommand ].concat( absValues); + } + } + } else { + // @ts-ignore + absoluteSegment = [absCommand ].concat( values); + } + + var segLength = absoluteSegment.length; + switch (absCommand) { + case 'Z': + x = mx; + y = my; + break; + case 'H': + // @ts-ignore + (assign$1 = absoluteSegment, x = assign$1[1]); + break; + case 'V': + // @ts-ignore + (assign$2 = absoluteSegment, y = assign$2[1]); + break; + default: + // @ts-ignore + x = absoluteSegment[segLength - 2]; + // @ts-ignore + y = absoluteSegment[segLength - 1]; + + if (absCommand === 'M') { + mx = x; + my = y; + } + } + return absoluteSegment; + }); + } + + /** + * Splits an extended A (arc-to) segment into two cubic-bezier segments. + * + * @param {SVGPathCommander.pathArray} path the `pathArray` this segment belongs to + * @param {string[]} allPathCommands all previous path commands + * @param {number} i the segment index + */ + + function fixArc(path, allPathCommands, i) { + if (path[i].length > 7) { + path[i].shift(); + var segment = path[i]; + var ni = i; // ESLint + while (segment.length) { + // if created multiple C:s, their original seg is saved + allPathCommands[i] = 'A'; + // @ts-ignore + path.splice(ni += 1, 0, ['C' ].concat( segment.splice(0, 6))); + } + path.splice(i, 1); + } + } + + /** + * Returns the missing control point from an + * T (shorthand quadratic bezier) segment. + * + * @param {number} x1 curve start x + * @param {number} y1 curve start y + * @param {number} qx control point x + * @param {number} qy control point y + * @param {string} prevCommand the previous path command + * @returns {{qx: number, qy: number}}} the missing control point + */ + function shorthandToQuad(x1, y1, qx, qy, prevCommand) { + return 'QT'.includes(prevCommand) + ? { qx: x1 * 2 - qx, qy: y1 * 2 - qy } + : { qx: x1, qy: y1 }; + } + + /** + * Returns the missing control point from an + * S (shorthand cubic bezier) segment. + * + * @param {number} x1 curve start x + * @param {number} y1 curve start y + * @param {number} x2 curve end x + * @param {number} y2 curve end y + * @param {string} prevCommand the previous path command + * @returns {{x1: number, y1: number}}} the missing control point + */ + function shorthandToCubic(x1, y1, x2, y2, prevCommand) { + return 'CS'.includes(prevCommand) + ? { x1: x1 * 2 - x2, y1: y1 * 2 - y2 } + : { x1: x1, y1: y1 }; + } + + /** + * Normalizes a single segment of a `pathArray` object. + * + * @param {SVGPathCommander.pathSegment} segment the segment object + * @param {any} params the coordinates of the previous segment + * @param {string} prevCommand the path command of the previous segment + * @returns {SVGPathCommander.normalSegment} the normalized segment + */ + function normalizeSegment(segment, params, prevCommand) { + var pathCommand = segment[0]; + var px1 = params.x1; + var py1 = params.y1; + var px2 = params.x2; + var py2 = params.y2; + var values = segment.slice(1).map(Number); + var result = segment; + + if (!'TQ'.includes(pathCommand)) { + // optional but good to be cautious + params.qx = null; + params.qy = null; + } + + if (pathCommand === 'H') { + result = ['L', segment[1], py1]; + } else if (pathCommand === 'V') { + result = ['L', px1, segment[1]]; + } else if (pathCommand === 'S') { + var ref = shorthandToCubic(px1, py1, px2, py2, prevCommand); + var x1 = ref.x1; + var y1 = ref.y1; + params.x1 = x1; + params.y1 = y1; + // @ts-ignore + result = ['C', x1, y1 ].concat( values); + } else if (pathCommand === 'T') { + var ref$1 = shorthandToQuad(px1, py1, params.qx, params.qy, prevCommand); + var qx = ref$1.qx; + var qy = ref$1.qy; + params.qx = qx; + params.qy = qy; + // @ts-ignore + result = ['Q', qx, qy ].concat( values); + } else if (pathCommand === 'Q') { + var nqx = values[0]; + var nqy = values[1]; + params.qx = nqx; + params.qy = nqy; + } + + // @ts-ignore -- we-re switching `pathSegment` type + return result; + } + + /** + * Iterates an array to check if it's a `pathArray` + * with all segments are in non-shorthand notation + * with absolute values. + * + * @param {string | SVGPathCommander.pathArray} path the `pathArray` to be checked + * @returns {boolean} iteration result + */ + function isNormalizedArray(path) { + // @ts-ignore -- `isAbsoluteArray` also checks if it's `Array` + return isAbsoluteArray(path) && path.every(function (seg) { return 'ACLMQZ'.includes(seg[0]); }); + } + + /** + * @type {SVGPathCommander.parserParams} + */ + var paramsParser = { + x1: 0, y1: 0, x2: 0, y2: 0, x: 0, y: 0, qx: null, qy: null, + }; + + /** + * Normalizes a `path` object for further processing: + * * convert segments to absolute values + * * convert shorthand path commands to their non-shorthand notation + * + * @param {string | SVGPathCommander.pathArray} pathInput the string to be parsed or 'pathArray' + * @returns {SVGPathCommander.normalArray} the normalized `pathArray` + */ + function normalizePath(pathInput) { + var assign; + + if (isNormalizedArray(pathInput)) { + // @ts-ignore -- `isNormalizedArray` checks if it's `pathArray` + return clonePath(pathInput); + } + + /** @type {SVGPathCommander.normalArray} */ + // @ts-ignore -- `absoluteArray` will become a `normalArray` + var path = pathToAbsolute(pathInput); + var params = Object.assign({}, paramsParser); + var allPathCommands = []; + var ii = path.length; + var pathCommand = ''; + var prevCommand = ''; + + for (var i = 0; i < ii; i += 1) { + (assign = path[i], pathCommand = assign[0]); + + // Save current path command + allPathCommands[i] = pathCommand; + // Get previous path command + if (i) { prevCommand = allPathCommands[i - 1]; } + // Previous path command is used to normalizeSegment + // @ts-ignore -- expected on normalization + path[i] = normalizeSegment(path[i], params, prevCommand); + + var segment = path[i]; + var seglen = segment.length; + + params.x1 = +segment[seglen - 2]; + params.y1 = +segment[seglen - 1]; + params.x2 = +(segment[seglen - 4]) || params.x1; + params.y2 = +(segment[seglen - 3]) || params.y1; + } + + return path; + } + + /** + * Checks a `pathArray` for an unnecessary `Z` segment + * and returns a new `pathArray` without it. + * + * The `pathInput` must be a single path, without + * sub-paths. For multi-path `` elements, + * use `splitPath` first and apply this utility on each + * sub-path separately. + * + * @param {SVGPathCommander.pathArray | string} pathInput the `pathArray` source + * @return {SVGPathCommander.pathArray} a fixed `pathArray` + */ + function fixPath(pathInput) { + var pathArray = parsePathString(pathInput); + var normalArray = normalizePath(pathArray); + var length = pathArray.length; + var isClosed = normalArray.slice(-1)[0][0] === 'Z'; + var segBeforeZ = isClosed ? length - 2 : length - 1; + + var ref = normalArray[0].slice(1); + var mx = ref[0]; + var my = ref[1]; + var ref$1 = normalArray[segBeforeZ].slice(-2); + var x = ref$1[0]; + var y = ref$1[1]; + + if (isClosed && mx === x && my === y) { + // @ts-ignore -- `pathSegment[]` is quite a `pathArray` + return pathArray.slice(0, -1); + } + return pathArray; + } + + /** + * Iterates an array to check if it's a `pathArray` + * with all C (cubic bezier) segments. + * + * @param {string | SVGPathCommander.pathArray} path the `Array` to be checked + * @returns {boolean} iteration result + */ + function isCurveArray(path) { + // @ts-ignore -- `isPathArray` also checks if it's `Array` + return isPathArray(path) && path.every(function (seg) { return 'MC'.includes(seg[0]); }); + } + + /** + * Returns an {x,y} vector rotated by a given + * angle in radian. + * + * @param {number} x the initial vector x + * @param {number} y the initial vector y + * @param {number} rad the radian vector angle + * @returns {{x: number, y: number}} the rotated vector + */ + function rotateVector(x, y, rad) { + var X = x * Math.cos(rad) - y * Math.sin(rad); + var Y = x * Math.sin(rad) + y * Math.cos(rad); + return { x: X, y: Y }; + } + + /** + * Converts A (arc-to) segments to C (cubic-bezier-to). + * + * For more information of where this math came from visit: + * http://www.w3.org/TR/SVG11/implnote.html#ArcImplementationNotes + * + * @param {number} X1 the starting x position + * @param {number} Y1 the starting y position + * @param {number} RX x-radius of the arc + * @param {number} RY y-radius of the arc + * @param {number} angle x-axis-rotation of the arc + * @param {number} LAF large-arc-flag of the arc + * @param {number} SF sweep-flag of the arc + * @param {number} X2 the ending x position + * @param {number} Y2 the ending y position + * @param {number[]=} recursive the parameters needed to split arc into 2 segments + * @return {number[]} the resulting cubic-bezier segment(s) + */ + function arcToCubic(X1, Y1, RX, RY, angle, LAF, SF, X2, Y2, recursive) { + var assign; + + var x1 = X1; var y1 = Y1; var rx = RX; var ry = RY; var x2 = X2; var y2 = Y2; + // for more information of where this Math came from visit: + // http://www.w3.org/TR/SVG11/implnote.html#ArcImplementationNotes + var d120 = (Math.PI * 120) / 180; + + var rad = (Math.PI / 180) * (+angle || 0); + /** @type {number[]} */ + var res = []; + var xy; + var f1; + var f2; + var cx; + var cy; + + if (!recursive) { + xy = rotateVector(x1, y1, -rad); + x1 = xy.x; + y1 = xy.y; + xy = rotateVector(x2, y2, -rad); + x2 = xy.x; + y2 = xy.y; + + var x = (x1 - x2) / 2; + var y = (y1 - y2) / 2; + var h = (x * x) / (rx * rx) + (y * y) / (ry * ry); + if (h > 1) { + h = Math.sqrt(h); + rx *= h; + ry *= h; + } + var rx2 = rx * rx; + var ry2 = ry * ry; + + var k = (LAF === SF ? -1 : 1) + * Math.sqrt(Math.abs((rx2 * ry2 - rx2 * y * y - ry2 * x * x) + / (rx2 * y * y + ry2 * x * x))); + + cx = ((k * rx * y) / ry) + ((x1 + x2) / 2); + cy = ((k * -ry * x) / rx) + ((y1 + y2) / 2); + // eslint-disable-next-line no-bitwise -- Impossible to satisfy no-bitwise + f1 = (Math.asin((((y1 - cy) / ry))) * (Math.pow( 10, 9 )) >> 0) / (Math.pow( 10, 9 )); + // eslint-disable-next-line no-bitwise -- Impossible to satisfy no-bitwise + f2 = (Math.asin((((y2 - cy) / ry))) * (Math.pow( 10, 9 )) >> 0) / (Math.pow( 10, 9 )); + + f1 = x1 < cx ? Math.PI - f1 : f1; + f2 = x2 < cx ? Math.PI - f2 : f2; + if (f1 < 0) { (f1 = Math.PI * 2 + f1); } + if (f2 < 0) { (f2 = Math.PI * 2 + f2); } + if (SF && f1 > f2) { + f1 -= Math.PI * 2; + } + if (!SF && f2 > f1) { + f2 -= Math.PI * 2; + } + } else { + (assign = recursive, f1 = assign[0], f2 = assign[1], cx = assign[2], cy = assign[3]); + } + var df = f2 - f1; + if (Math.abs(df) > d120) { + var f2old = f2; + var x2old = x2; + var y2old = y2; + f2 = f1 + d120 * (SF && f2 > f1 ? 1 : -1); + x2 = cx + rx * Math.cos(f2); + y2 = cy + ry * Math.sin(f2); + res = arcToCubic(x2, y2, rx, ry, angle, 0, SF, x2old, y2old, [f2, f2old, cx, cy]); + } + df = f2 - f1; + var c1 = Math.cos(f1); + var s1 = Math.sin(f1); + var c2 = Math.cos(f2); + var s2 = Math.sin(f2); + var t = Math.tan(df / 4); + var hx = (4 / 3) * rx * t; + var hy = (4 / 3) * ry * t; + var m1 = [x1, y1]; + var m2 = [x1 + hx * s1, y1 - hy * c1]; + var m3 = [x2 + hx * s2, y2 - hy * c2]; + var m4 = [x2, y2]; + m2[0] = 2 * m1[0] - m2[0]; + m2[1] = 2 * m1[1] - m2[1]; + if (recursive) { + return m2.concat( m3, m4, res); + } + res = m2.concat( m3, m4, res); + var newres = []; + for (var i = 0, ii = res.length; i < ii; i += 1) { + newres[i] = i % 2 + ? rotateVector(res[i - 1], res[i], rad).y + : rotateVector(res[i], res[i + 1], rad).x; + } + return newres; + } + + /** + * Converts a Q (quadratic-bezier) segment to C (cubic-bezier). + * + * @param {number} x1 curve start x + * @param {number} y1 curve start y + * @param {number} qx control point x + * @param {number} qy control point y + * @param {number} x2 curve end x + * @param {number} y2 curve end y + * @returns {number[]} the cubic-bezier segment + */ + function quadToCubic(x1, y1, qx, qy, x2, y2) { + var r13 = 1 / 3; + var r23 = 2 / 3; + return [ + r13 * x1 + r23 * qx, // cpx1 + r13 * y1 + r23 * qy, // cpy1 + r13 * x2 + r23 * qx, // cpx2 + r13 * y2 + r23 * qy, // cpy2 + x2, y2 ]; + } + + /** + * Returns the coordinates of a specified distance + * ratio between two points. + * + * @param {[number, number]} a the first point coordinates + * @param {[number, number]} b the second point coordinates + * @param {number} t the ratio + * @returns {[number, number]} the midpoint coordinates + */ + function midPoint(a, b, t) { + var ax = a[0]; + var ay = a[1]; var bx = b[0]; + var by = b[1]; + return [ax + (bx - ax) * t, ay + (by - ay) * t]; + } + + /** + * Returns the square root of the distance + * between two given points. + * + * @param {[number, number]} a the first point coordinates + * @param {[number, number]} b the second point coordinates + * @returns {number} the distance value + */ + function distanceSquareRoot(a, b) { + return Math.sqrt( + (a[0] - b[0]) * (a[0] - b[0]) + + (a[1] - b[1]) * (a[1] - b[1]) + ); + } + + /** + * Returns the length of a line (L,V,H,Z) segment, + * or a point at a given length. + * + * @param {number} x1 the starting point X + * @param {number} y1 the starting point Y + * @param {number} x2 the ending point X + * @param {number} y2 the ending point Y + * @param {number=} distance the distance to point + * @returns {{x: number, y: number} | number} the segment length or point + */ + function segmentLineFactory(x1, y1, x2, y2, distance) { + var length = distanceSquareRoot([x1, y1], [x2, y2]); + var margin = 0.001; + + if (typeof distance === 'number') { + if (distance < margin) { + return { x: x1, y: y1 }; + } + if (distance > length + margin) { + return { x: x2, y: y2 }; + } + var ref = midPoint([x1, y1], [x2, y2], distance / length); + var x = ref[0]; + var y = ref[1]; + return { x: x, y: y }; + } + return length; + } + + /** + * Converts an L (line-to) segment to C (cubic-bezier). + * + * @param {number} x1 line start x + * @param {number} y1 line start y + * @param {number} x2 line end x + * @param {number} y2 line end y + * @returns {number[]} the cubic-bezier segment + */ + function lineToCubic(x1, y1, x2, y2) { + var t = 0.5; + /** @type {[number, number]} */ + var p0 = [x1, y1]; + /** @type {[number, number]} */ + var p1 = [x2, y2]; + var p2 = midPoint(p0, p1, t); + var p3 = midPoint(p1, p2, t); + var p4 = midPoint(p2, p3, t); + var p5 = midPoint(p3, p4, t); + var p6 = midPoint(p4, p5, t); + var seg1 = p0.concat( p2, p4, p6, [t]); + // @ts-ignore + var cp1 = segmentLineFactory.apply(void 0, seg1); + var seg2 = p6.concat( p5, p3, p1, [0]); + // @ts-ignore + var cp2 = segmentLineFactory.apply(void 0, seg2); + + // @ts-ignore + return [cp1.x, cp1.y, cp2.x, cp2.y, x2, y2]; + } + + /** + * Converts any segment to C (cubic-bezier). + * + * @param {SVGPathCommander.pathSegment} segment the source segment + * @param {SVGPathCommander.parserParams} params the source segment parameters + * @returns {SVGPathCommander.cubicSegment | SVGPathCommander.MSegment} the cubic-bezier segment + */ + function segmentToCubic(segment, params) { + var pathCommand = segment[0]; + var values = segment.slice(1).map(function (n) { return +n; }); + var x = values[0]; + var y = values[1]; + var args; + var px1 = params.x1; + var py1 = params.y1; + var px = params.x; + var py = params.y; + + if (!'TQ'.includes(pathCommand)) { + params.qx = null; + params.qy = null; + } + + switch (pathCommand) { + case 'M': + params.x = x; + params.y = y; + return segment; + case 'A': + args = [px1, py1 ].concat( values); + // @ts-ignore -- relax, the utility will return 6 numbers + return ['C' ].concat( arcToCubic.apply(void 0, args)); + case 'Q': + params.qx = x; + params.qy = y; + args = [px1, py1 ].concat( values); + // @ts-ignore -- also returning 6 numbers + return ['C' ].concat( quadToCubic.apply(void 0, args)); + case 'L': + // @ts-ignore -- also returning 6 numbers + return ['C' ].concat( lineToCubic(px1, py1, x, y)); + case 'Z': + // @ts-ignore -- also returning 6 numbers + return ['C' ].concat( lineToCubic(px1, py1, px, py)); + } + // @ts-ignore -- we're switching `pathSegment` type + return segment; + } + + /** + * Parses a path string value or 'pathArray' and returns a new one + * in which all segments are converted to cubic-bezier. + * + * In addition, un-necessary `Z` segment is removed if previous segment + * extends to the `M` segment. + * + * @param {string | SVGPathCommander.pathArray} pathInput the string to be parsed or 'pathArray' + * @returns {SVGPathCommander.curveArray} the resulted `pathArray` converted to cubic-bezier + */ + function pathToCurve(pathInput) { + var assign; + + if (isCurveArray(pathInput)) { + // @ts-ignore -- `isCurveArray` checks if it's `pathArray` + return clonePath(pathInput); + } + + var path = fixPath(normalizePath(pathInput)); + var params = Object.assign({}, paramsParser); + var allPathCommands = []; + var pathCommand = ''; // ts-lint + var ii = path.length; + + for (var i = 0; i < ii; i += 1) { + (assign = path[i], pathCommand = assign[0]); + allPathCommands[i] = pathCommand; + + path[i] = segmentToCubic(path[i], params); + + fixArc(path, allPathCommands, i); + ii = path.length; + + var segment = path[i]; + var seglen = segment.length; + params.x1 = +segment[seglen - 2]; + params.y1 = +segment[seglen - 1]; + params.x2 = +(segment[seglen - 4]) || params.x1; + params.y2 = +(segment[seglen - 3]) || params.y1; + } + + // @ts-ignore + return path; + } + + /** + * SVGPathCommander default options + * @type {SVGPathCommander.options} + */ + var defaultOptions = { + origin: [0, 0, 0], + round: 4, + }; + + /** + * Rounds the values of a `pathArray` instance to + * a specified amount of decimals and returns it. + * + * @param {SVGPathCommander.pathArray} path the source `pathArray` + * @param {number | boolean} roundOption the amount of decimals to round numbers to + * @returns {SVGPathCommander.pathArray} the resulted `pathArray` with rounded values + */ + function roundPath(path, roundOption) { + var round = defaultOptions.round; + if (roundOption === false || round === false) { return clonePath(path); } + round = roundOption >= 1 ? roundOption : round; + // to round values to the power + // the `round` value must be integer + // @ts-ignore + var pow = round >= 1 ? (Math.pow( 10, round )) : 1; + + // @ts-ignore -- `pathSegment[]` is `pathArray` + return path.map(function (pi) { + var values = pi.slice(1).map(Number) + .map(function (n) { return (n % 1 === 0 ? n : Math.round(n * pow) / pow); }); + return [pi[0] ].concat( values); + }); + } + + /** + * Returns a valid `d` attribute string value created + * by rounding values and concatenating the `pathArray` segments. + * + * @param {SVGPathCommander.pathArray} path the `pathArray` object + * @param {any} round amount of decimals to round values to + * @returns {string} the concatenated path string + */ + function pathToString(path, round) { + return roundPath(path, round) + .map(function (x) { return x[0] + x.slice(1).join(' '); }).join(''); + } + + /** + * Reverses all segments and their values from a `pathArray` + * which consists of only C (cubic-bezier) path commands. + * + * @param {SVGPathCommander.curveArray} path the source `pathArray` + * @returns {SVGPathCommander.curveArray} the reversed `pathArray` + */ + function reverseCurve(path) { + var rotatedCurve = path.slice(1) + .map(function (x, i, curveOnly) { return (!i + ? path[0].slice(1).concat( x.slice(1)) + : curveOnly[i - 1].slice(-2).concat( x.slice(1))); }) + .map(function (x) { return x.map(function (_, i) { return x[x.length - i - 2 * (1 - (i % 2))]; }); }) + .reverse(); + + // @ts-ignore -- expected on reverse operations + return [['M' ].concat( rotatedCurve[0].slice(0, 2)) ].concat( rotatedCurve.map(function (x) { return ['C' ].concat( x.slice(2)); })); + } + + /** + * Returns the area of a single cubic-bezier segment. + * + * http://objectmix.com/graphics/133553-area-closed-bezier-curve.html + * + * @param {number} x1 the starting point X + * @param {number} y1 the starting point Y + * @param {number} c1x the first control point X + * @param {number} c1y the first control point Y + * @param {number} c2x the second control point X + * @param {number} c2y the second control point Y + * @param {number} x2 the ending point X + * @param {number} y2 the ending point Y + * @returns {number} the area of the cubic-bezier segment + */ + function getCubicSegArea(x1, y1, c1x, c1y, c2x, c2y, x2, y2) { + return (3 * ((y2 - y1) * (c1x + c2x) - (x2 - x1) * (c1y + c2y) + + (c1y * (x1 - c2x)) - (c1x * (y1 - c2y)) + + (y2 * (c2x + x1 / 3)) - (x2 * (c2y + y1 / 3)))) / 20; + } + + /** + * Returns the area of a shape. + * @author Jürg Lehni & Jonathan Puckey + * + * @see https://github.com/paperjs/paper.js/blob/develop/src/path/Path.js + * + * @param {SVGPathCommander.pathArray} path the shape `pathArray` + * @returns {number} the length of the cubic-bezier segment + */ + function getPathArea(path) { + var x = 0; var y = 0; var len = 0; + + return pathToCurve(path).map(function (seg) { + var assign, assign$1; + + switch (seg[0]) { + case 'M': + (assign = seg, x = assign[1], y = assign[2]); + return 0; + default: + // @ts-ignore -- the utility will have proper amount of params + len = getCubicSegArea.apply(void 0, [ x, y ].concat( seg.slice(1) )); + // @ts-ignore -- the segment always has numbers + (assign$1 = seg.slice(-2), x = assign$1[0], y = assign$1[1]); + return len; + } + }).reduce(function (a, b) { return a + b; }, 0); + } + + /** + * Check if a path is drawn clockwise and returns true if so, + * false otherwise. + * + * @param {SVGPathCommander.pathArray} path the path string or `pathArray` + * @returns {boolean} true when clockwise or false if not + */ + function getDrawDirection(path) { + return getPathArea(pathToCurve(path)) >= 0; + } + + /** + * Split a cubic-bezier segment into two. + * + * @param {number[]} pts the cubic-bezier parameters + * @return {SVGPathCommander.cubicSegment[]} two new cubic-bezier segments + */ + function splitCubic(pts/* , ratio */) { + var t = /* ratio || */ 0.5; + var p0 = pts.slice(0, 2); + var p1 = pts.slice(2, 4); + var p2 = pts.slice(4, 6); + var p3 = pts.slice(6, 8); + // @ts-ignore + var p4 = midPoint(p0, p1, t); + // @ts-ignore + var p5 = midPoint(p1, p2, t); + // @ts-ignore + var p6 = midPoint(p2, p3, t); + var p7 = midPoint(p4, p5, t); + var p8 = midPoint(p5, p6, t); + var p9 = midPoint(p7, p8, t); + + return [ + ['C' ].concat( p4, p7, p9), + // @ts-ignore + ['C' ].concat( p8, p6, p3) ]; + } + + /** + * Split a path into an `Array` of sub-path strings. + * + * In the process, values are converted to absolute + * for visual consistency. + * + * @param {SVGPathCommander.pathArray | string} pathInput the source `pathArray` + * @return {string[]} an array with all sub-path strings + */ + function splitPath(pathInput) { + return pathToString(pathToAbsolute(pathInput), 0) + .replace(/(m|M)/g, '|$1') + .split('|') + .map(function (s) { return s.trim(); }) + .filter(function (s) { return s; }); + } + + /** + * Returns a point at a given length of a C (cubic-bezier) segment. + * + * @param {number} x1 the starting point X + * @param {number} y1 the starting point Y + * @param {number} c1x the first control point X + * @param {number} c1y the first control point Y + * @param {number} c2x the second control point X + * @param {number} c2y the second control point Y + * @param {number} x2 the ending point X + * @param {number} y2 the ending point Y + * @param {number} t a [0-1] ratio + * @returns {{x: number, y: number}} the cubic-bezier segment length + */ + function getPointAtCubicSegmentLength(x1, y1, c1x, c1y, c2x, c2y, x2, y2, t) { + var t1 = 1 - t; + return { + x: (Math.pow( t1, 3 )) * x1 + + 3 * (Math.pow( t1, 2 )) * t * c1x + + 3 * t1 * (Math.pow( t, 2 )) * c2x + + (Math.pow( t, 3 )) * x2, + y: (Math.pow( t1, 3 )) * y1 + + 3 * (Math.pow( t1, 2 )) * t * c1y + + 3 * t1 * (Math.pow( t, 2 )) * c2y + + (Math.pow( t, 3 )) * y2, + }; + } + + /** + * Returns the length of a C (cubic-bezier) segment, + * or an {x,y} point at a given length. + * + * @param {number} x1 the starting point X + * @param {number} y1 the starting point Y + * @param {number} c1x the first control point X + * @param {number} c1y the first control point Y + * @param {number} c2x the second control point X + * @param {number} c2y the second control point Y + * @param {number} x2 the ending point X + * @param {number} y2 the ending point Y + * @param {number=} distance the point distance + * @returns {{x: number, y: number} | number} the segment length or point + */ + function segmentCubicFactory(x1, y1, c1x, c1y, c2x, c2y, x2, y2, distance) { + var assign; + + var x = x1; var y = y1; + var lengthMargin = 0.001; + var totalLength = 0; + var prev = [x1, y1, totalLength]; + /** @type {[number, number]} */ + var cur = [x1, y1]; + var t = 0; + + if (typeof distance === 'number' && distance < lengthMargin) { + return { x: x, y: y }; + } + + var n = 100; + for (var j = 0; j <= n; j += 1) { + t = j / n; + + ((assign = getPointAtCubicSegmentLength(x1, y1, c1x, c1y, c2x, c2y, x2, y2, t), x = assign.x, y = assign.y)); + totalLength += distanceSquareRoot(cur, [x, y]); + cur = [x, y]; + + if (typeof distance === 'number' && totalLength >= distance) { + var dv = (totalLength - distance) / (totalLength - prev[2]); + + return { + x: cur[0] * (1 - dv) + prev[0] * dv, + y: cur[1] * (1 - dv) + prev[1] * dv, + }; + } + prev = [x, y, totalLength]; + } + + if (typeof distance === 'number' && distance >= totalLength) { + return { x: x2, y: y2 }; + } + return totalLength; + } + + // Component Functions + + /** + * Sets the property update function. + * @param {string} tweenProp the `path` property + */ + function onStartCubicMorph(tweenProp) { + if (!KEC[tweenProp] && this.valuesEnd[tweenProp]) { + KEC[tweenProp] = function updateMorph(elem, a, b, v) { + var curve = []; + var path1 = a.curve; + var path2 = b.curve; + for (var i = 0, l = path2.length; i < l; i += 1) { // each path command + curve.push([path1[i][0]]); + for (var 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 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(function (segment, i, pathArray) { + var segmentData = i && pathArray[i - 1].slice(-2).concat( segment.slice(1)); + var curveLength = i ? segmentCubicFactory.apply(void 0, segmentData) : 0; + + var 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) { + var c1 = getCurveArray(path1); + var c2 = getCurveArray(path2); + var L1 = c1.length; + var L2 = c2.length; + var l1 = c1.filter(function (x) { return x.l; }).length; + var l2 = c2.filter(function (x) { return x.l; }).length; + var m1 = c1.filter(function (x) { return x.l; }).reduce(function (a, ref) { + var l = ref.l; + + return a + l; + }, 0) / l1 || 0; + var m2 = c2.filter(function (x) { return x.l; }).reduce(function (a, ref) { + var l = ref.l; + + return a + l; + }, 0) / l2 || 0; + var tl = TL || Math.max(L1, L2); + var mm = [m1, m2]; + var dif = [tl - L1, tl - L2]; + var canSplit = 0; + var result = [c1, c2] + .map(function (x, i) { return (x.l === tl + ? x.map(function (y) { return y.s; }) + : x.map(function (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) { + var segCount = a.length; + var pointCount = segCount - 1; + + return a.map(function (_, idx) { return a.map(function (__, i) { + var oldSegIdx = idx + i; + var seg; + + if (i === 0 || (a[oldSegIdx] && a[oldSegIdx][0] === 'M')) { + seg = a[oldSegIdx]; + return ['M' ].concat( 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) { + var segCount = a.length - 1; + var lineLengths = []; + var computedIndex = 0; + var sumLensSqrd = 0; + var rotations = getRotations(a); + + rotations.forEach(function (_, i) { + a.slice(1).forEach(function (__, 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 + var pathObject = {}; + // remove newlines, they break some JSON strings + var pathReg = new RegExp('\\n', 'ig'); + + var 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]) { + var pathCurve1 = this.valuesStart[tweenProp].curve; + var pathCurve2 = this.valuesEnd[tweenProp].curve; + + if (!pathCurve1 || !pathCurve2 + || (pathCurve1 && pathCurve2 && pathCurve1[0][0] === 'M' && pathCurve1.length !== pathCurve2.length)) { + var path1 = this.valuesStart[tweenProp].original; + var path2 = this.valuesEnd[tweenProp].original; + var curves = equalizeSegments(path1, path2); + var 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 + var svgCubicMorphFunctions = { + prepareStart: getCubicMorph, + prepareProperty: prepareCubicMorph, + onStart: onStartCubicMorph, + crossCheck: crossCheckCubicMorph, + }; + + // Component Full + var svgCubicMorph = { + component: 'svgCubicMorph', + property: 'path', + defaultValue: [], + Interpolate: { numbers: numbers, pathToString: pathToString }, + functions: svgCubicMorphFunctions, + // export utils to global for faster execution + Util: { + pathToCurve: pathToCurve, + pathToAbsolute: pathToAbsolute, + pathToString: pathToString, + parsePathString: parsePathString, + getRotatedCurve: getRotatedCurve, + getRotations: getRotations, + equalizeSegments: equalizeSegments, + reverseCurve: reverseCurve, + clonePath: clonePath, + getDrawDirection: getDrawDirection, + segmentCubicFactory: segmentCubicFactory, + splitCubic: splitCubic, + splitPath: splitPath, + fixPath: fixPath, + getCurveArray: getCurveArray, + }, + }; + + // Component Functions + /** + * Sets the property update function. + * @param {string} tweenProp the property name + */ + function svgTransformOnStart(tweenProp) { + if (!KEC[tweenProp] && this.valuesEnd[tweenProp]) { + KEC[tweenProp] = function (l, a, b, v) { + var x = 0; + var y = 0; + var deg = Math.PI / 180; + var scale = 'scale' in b ? numbers(a.scale, b.scale, v) : 1; + var rotate = 'rotate' in b ? numbers(a.rotate, b.rotate, v) : 0; + var sin = Math.sin(rotate * deg); + var cos = Math.cos(rotate * deg); + var skewX = 'skewX' in b ? numbers(a.skewX, b.skewX, v) : 0; + var skewY = 'skewY' in b ? numbers(a.skewY, b.skewY, v) : 0; + var 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; + var 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 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) { + var result; + var x = bbox.x; + var width = bbox.width; + 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} + */ + function parseTransformString(a) { + var c = {}; + var d = a && /\)/.test(a) + ? a.substring(0, a.length - 1).split(/\)\s|\)/) + : 'none'; + + if (d instanceof Array) { + for (var j = 0, jl = d.length; j < jl; j += 1) { + var ref = d[j].trim().split('('); + var prop = ref[0]; + var val = ref[1]; + c[prop] = val; + } + } + return c; + } + + /** + * Returns the SVG transform tween object. + * @param {string} _ property name + * @param {Object} v property value object + * @returns {KUTE.transformSVGObject} the SVG transform tween object + */ + function parseTransformSVG(/* prop */_, v) { + /** @type {KUTE.transformSVGObject} */ + var svgTransformObject = {}; + + // by default the transformOrigin is "50% 50%" of the shape box + var bb = this.element.getBBox(); + var cx = bb.x + bb.width / 2; + var cy = bb.y + bb.height / 2; + + var origin = this._transformOrigin; + var 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(function (i) { + var assign; + + if (i === 'rotate') { + if (typeof v[i] === 'number') { + svgTransformObject[i] = v[i]; + } else if (v[i] instanceof Array) { + (assign = v[i], svgTransformObject[i] = assign[0]); + } 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) { + var transformObject = {}; + var currentTransform = parseTransformString(this.element.getAttribute('transform')); + + // find a value in current attribute value or add a default value + Object.keys(value).forEach(function (j) { + var 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]) { + var valuesStart = this.valuesStart[prop]; + var valuesEnd = this.valuesEnd[prop]; + var currentTransform = parseTransformSVG.call(this, prop, + parseTransformString(this.element.getAttribute('transform'))); + + // populate the valuesStart first + Object.keys(currentTransform).forEach(function (tp) { + valuesStart[tp] = currentTransform[tp]; + }); + + // now try to determine the REAL translation + var parentSVG = this.element.ownerSVGElement; + var 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(function (s) { + if (!(s in valuesEnd) || s === 'origin') { + valuesEnd[s] = valuesStart[s]; + } + }); + } + } + + // All Component Functions + var svgTransformFunctions = { + prepareStart: getStartSvgTransform, + prepareProperty: prepareSvgTransform, + onStart: svgTransformOnStart, + crossCheck: svgTransformCrossCheck, + }; + + // Component Full + var 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: numbers }, + functions: svgTransformFunctions, + + // export utils to globals for faster execution + Util: { parseStringOrigin: parseStringOrigin, parseTransformString: parseTransformString, parseTransformSVG: parseTransformSVG }, + }; + + /** + * A global namespace for 'addEventListener' string. + * @type {string} + */ + var addEventListener = 'addEventListener'; + + /** + * A global namespace for 'removeEventListener' string. + * @type {string} + */ + var removeEventListener = 'removeEventListener'; + + /** + * A global namespace for passive events support. + * @type {boolean} + */ + var supportPassive = (function () { + var result = false; + try { + var opts = Object.defineProperty({}, 'passive', { + get: function get() { + result = true; + return result; + }, + }); + document[addEventListener]('DOMContentLoaded', function wrap() { + document[removeEventListener]('DOMContentLoaded', wrap, opts); + }, opts); + } catch (e) { + throw Error('Passive events are not supported'); + } + + return result; + })(); + + // general event options + + /** + * A global namespace for most scroll event listeners. + */ + var passiveHandler = supportPassive ? { passive: true } : false; + + /** + * A global namespace for mouse hover events. + * @type {[string, string]} + */ + var mouseHoverEvents = ('onmouseleave' in document) ? ['mouseenter', 'mouseleave'] : ['mouseover', 'mouseout']; + + /** + * A global namespace for touch events support. + * @type {boolean} + */ + var supportTouch = 'ontouchstart' in window || 'msMaxTouchPoints' in navigator; + + // Component Util + // events preventing scroll + var touchOrWheel = supportTouch ? 'touchstart' : 'mousewheel'; + + // true scroll container + // very important and specific to the component + var 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 + */ + function preventScroll(e) { + if (this.scrolling) { e.preventDefault(); } + } + + /** + * Returns the scroll element / target. + * @returns {{el: Element, st: Element}} + */ + function getScrollTargets() { + var el = this.element; + return el === scrollContainer ? { el: document, st: document.body } : { el: el, st: el }; + } + + /** + * Toggles scroll prevention callback on scroll events. + * @param {string} action addEventListener / removeEventListener + * @param {Element} element target + */ + function toggleScrollEvents(action, element) { + element[action](mouseHoverEvents[0], preventScroll, passiveHandler); + element[action](touchOrWheel, preventScroll, passiveHandler); + } + + /** + * Action performed before scroll animation start. + */ + function scrollIn() { + var 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. + */ + function scrollOut() { // prevent scroll when tweening scroll + var 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 + */ + 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] = function (elem, a, b, v) { + /* eslint-disable */ + elem.scrollTop = (numbers(a, b, v)) >> 0; + /* eslint-enable */ + }; + } + } + + /** + * Removes the scroll prevention event listener. + */ + function onCompleteScroll(/* tweenProp */) { + scrollOut.call(this); + } + + // 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 + var scrollFunctions = { + prepareStart: getScroll, + prepareProperty: prepareScroll, + onStart: onStartScroll, + onComplete: onCompleteScroll, + }; + + // Full Component + var ScrollProperty = { + component: 'scrollProperty', + property: 'scroll', + defaultValue: 0, + Interpolate: { numbers: numbers }, + functions: scrollFunctions, + // export stuff to global + Util: { + preventScroll: preventScroll, scrollIn: scrollIn, scrollOut: scrollOut, getScrollTargets: getScrollTargets, toggleScrollEvents: toggleScrollEvents, + }, + }; + + // Component Properties + var shadowProps$1 = ['boxShadow', 'textShadow']; + + // Component Functions + /** + * Sets the property update function. + * @param {string} tweenProp the property name + */ + function onStartShadow(tweenProp) { + if (this.valuesEnd[tweenProp] && !KEC[tweenProp]) { + KEC[tweenProp] = function (elem, a, b, v) { + // let's start with the numbers | set unit | also determine inset + var params = []; + var unit = 'px'; + var sl = tweenProp === 'textShadow' ? 3 : 4; + var colA = sl === 3 ? a[3] : a[4]; + var colB = sl === 3 ? b[3] : b[4]; + var inset = (a[5] && a[5] !== 'none') || (b[5] && b[5] !== 'none') ? ' inset' : false; + + for (var 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(' '); + }; + } + } + var shadowPropOnStart$1 = {}; + shadowProps$1.forEach(function (x) { shadowPropOnStart$1[x] = onStartShadow; }); + + // Component Properties + var 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) { + var 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 (var 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(function (_, i) { return [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 + */ + function getShadow(tweenProp/* , value */) { + var 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 + */ + 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 + var value = propValue; + if (typeof value === 'string') { + var inset = 'none'; + // a full RegEx for color strings + var colRegEx = /(\s?(?:#(?:[\da-f]{3}){1,2}|rgba?\(\d{1,3},\s*\d{1,3},\s*\d{1,3}\))\s?)/gi; + var 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; + } + + var shadowPropOnStart = {}; + shadowProps.forEach(function (x) { shadowPropOnStart[x] = onStartShadow; }); + + // All Component Functions + var shadowFunctions = { + prepareStart: getShadow, + prepareProperty: prepareShadow, + onStart: shadowPropOnStart, + }; + + // Component Full + var 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: numbers, colors: colors }, + functions: shadowFunctions, + Util: { processShadowArray: processShadowArray, trueColor: trueColor }, + }; + + // Component Properties + var textProperties = ['fontSize', 'lineHeight', 'letterSpacing', 'wordSpacing']; + var textOnStart$1 = {}; + + /** + * Sets the property update function. + * @param {string} tweenProp the property name + */ + function textPropOnStart(tweenProp) { + if (this.valuesEnd[tweenProp] && !KEC[tweenProp]) { + KEC[tweenProp] = function (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); + }; + } + } + + textProperties.forEach(function (tweenProp) { + textOnStart$1[tweenProp] = textPropOnStart; + }); + + // Component Properties + var textProps = ['fontSize', 'lineHeight', 'letterSpacing', 'wordSpacing']; + var textOnStart = {}; + + // Component Functions + textProps.forEach(function (tweenProp) { + textOnStart[tweenProp] = textPropOnStart; + }); + + /** + * Returns the current property computed style. + * @param {string} prop the property name + * @returns {string} computed style for property + */ + function getTextProp(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 prepareTextProp(/* prop */_, value) { + return trueDimension(value); + } + + // All Component Functions + var textPropFunctions = { + prepareStart: getTextProp, + prepareProperty: prepareTextProp, + onStart: textOnStart, + }; + + // Component Full + var TextProperties = { + component: 'textProperties', + category: 'textProperties', + properties: textProps, + defaultValues: { + fontSize: 0, lineHeight: 0, letterSpacing: 0, wordSpacing: 0, + }, + Interpolate: { units: units }, + functions: textPropFunctions, + Util: { trueDimension: trueDimension }, + }; + + // Component Values + var lowerCaseAlpha = String('abcdefghijklmnopqrstuvwxyz').split(''); // lowercase + var upperCaseAlpha = String('abcdefghijklmnopqrstuvwxyz').toUpperCase().split(''); // uppercase + var nonAlpha = String("~!@#$%^&*()_+{}[];'<>,./?=-").split(''); // symbols + var numeric = String('0123456789').split(''); // numeric + var alphaNumeric = lowerCaseAlpha.concat(upperCaseAlpha, numeric); // alpha numeric + var allTypes = alphaNumeric.concat(nonAlpha); // all caracters + + var charSet = { + alpha: lowerCaseAlpha, // lowercase + upper: upperCaseAlpha, // uppercase + symbols: nonAlpha, // symbols + numeric: numeric, + alphanumeric: alphaNumeric, + all: allTypes, + }; + + // Component Functions + var onStartWrite = { + /** + * onStartWrite.text + * + * Sets the property update function. + * @param {string} tweenProp the property name + */ + text: function text(tweenProp) { + if (!KEC[tweenProp] && this.valuesEnd[tweenProp]) { + var chars = this._textChars; + var charsets = charSet[defaultOptions$1.textChars]; + + if (chars in charSet) { + charsets = charSet[chars]; + } else if (chars && chars.length) { + charsets = chars; + } + + KEC[tweenProp] = function (elem, a, b, v) { + var initialText = ''; + var endText = ''; + var finalText = b === '' ? ' ' : b; + var firstLetterA = a.substring(0); + var firstLetterB = b.substring(0); + /* eslint-disable */ + var pointer = charsets[(Math.random() * charsets.length) >> 0]; + + if (a === ' ') { + endText = firstLetterB + .substring(Math.min(v * firstLetterB.length, firstLetterB.length) >> 0, 0); + elem.innerHTML = v < 1 ? ((endText + pointer)) : finalText; + } else if (b === ' ') { + initialText = firstLetterA + .substring(0, Math.min((1 - v) * firstLetterA.length, firstLetterA.length) >> 0); + elem.innerHTML = v < 1 ? ((initialText + pointer)) : finalText; + } else { + initialText = firstLetterA + .substring(firstLetterA.length, + Math.min(v * firstLetterA.length, firstLetterA.length) >> 0); + endText = firstLetterB + .substring(0, Math.min(v * firstLetterB.length, firstLetterB.length) >> 0); + elem.innerHTML = v < 1 ? ((endText + pointer + initialText)) : finalText; + } + /* eslint-enable */ + }; + } + }, + /** + * onStartWrite.number + * + * Sets the property update function. + * @param {string} tweenProp the property name + */ + number: function number(tweenProp) { + if (tweenProp in this.valuesEnd && !KEC[tweenProp]) { // numbers can be 0 + KEC[tweenProp] = function (elem, a, b, v) { + /* eslint-disable */ + elem.innerHTML = numbers(a, b, v) >> 0; + /* eslint-enable */ + }; + } + }, + }; + + // Component Util + // utility for multi-child targets + // wrapContentsSpan returns an [Element] with the SPAN.tagName and a desired class + function wrapContentsSpan(el, classNAME) { + var assign; + + var textWriteWrapper; + var newElem; + if (typeof (el) === 'string') { + newElem = document.createElement('SPAN'); + newElem.innerHTML = el; + newElem.className = classNAME; + return newElem; + } + if (!el.children.length || (el.children.length && el.children[0].className !== classNAME)) { + var elementInnerHTML = el.innerHTML; + textWriteWrapper = document.createElement('SPAN'); + textWriteWrapper.className = classNAME; + textWriteWrapper.innerHTML = elementInnerHTML; + /* eslint-disable no-param-reassign -- impossible to satisfy */ + el.appendChild(textWriteWrapper); + el.innerHTML = textWriteWrapper.outerHTML; + /* eslint-enable no-param-reassign -- impossible to satisfy */ + } else if (el.children.length && el.children[0].className === classNAME) { + (assign = el.children, textWriteWrapper = assign[0]); + } + return textWriteWrapper; + } + + function getTextPartsArray(el, classNAME) { + var elementsArray = []; + var len = el.children.length; + if (len) { + var textParts = []; + var remainingMarkup = el.innerHTML; + var wrapperParts; + + for (var i = 0, currentChild = (void 0), childOuter = (void 0), unTaggedContent = (void 0); i < len; i += 1) { + currentChild = el.children[i]; + childOuter = currentChild.outerHTML; + wrapperParts = remainingMarkup.split(childOuter); + + if (wrapperParts[0] !== '') { + unTaggedContent = wrapContentsSpan(wrapperParts[0], classNAME); + textParts.push(unTaggedContent); + remainingMarkup = remainingMarkup.replace(wrapperParts[0], ''); + } else if (wrapperParts[1] !== '') { + unTaggedContent = wrapContentsSpan(wrapperParts[1].split('<')[0], classNAME); + textParts.push(unTaggedContent); + remainingMarkup = remainingMarkup.replace(wrapperParts[0].split('<')[0], ''); + } + + if (!currentChild.classList.contains(classNAME)) { currentChild.classList.add(classNAME); } + textParts.push(currentChild); + remainingMarkup = remainingMarkup.replace(childOuter, ''); + } + + if (remainingMarkup !== '') { + var unTaggedRemaining = wrapContentsSpan(remainingMarkup, classNAME); + textParts.push(unTaggedRemaining); + } + + elementsArray = elementsArray.concat(textParts); + } else { + elementsArray = elementsArray.concat([wrapContentsSpan(el, classNAME)]); + } + return elementsArray; + } + + function setSegments(target, newText) { + var oldTargetSegs = getTextPartsArray(target, 'text-part'); + var newTargetSegs = getTextPartsArray(wrapContentsSpan(newText), 'text-part'); + + /* eslint-disable no-param-reassign */ + target.innerHTML = ''; + target.innerHTML += oldTargetSegs.map(function (s) { s.className += ' oldText'; return s.outerHTML; }).join(''); + target.innerHTML += newTargetSegs.map(function (s) { s.className += ' newText'; return s.outerHTML.replace(s.innerHTML, ''); }).join(''); + /* eslint-enable no-param-reassign */ + + return [oldTargetSegs, newTargetSegs]; + } + + function createTextTweens(target, newText, ops) { + if (target.playing) { return false; } + + var options = ops || {}; + options.duration = 1000; + + if (ops.duration === 'auto') { + options.duration = 'auto'; + } else if (Number.isFinite(ops.duration * 1)) { + options.duration = ops.duration * 1; + } + + var TweenContructor = connect.tween; + var segs = setSegments(target, newText); + var oldTargetSegs = segs[0]; + var newTargetSegs = segs[1]; + var oldTargets = [].slice.call(target.getElementsByClassName('oldText')).reverse(); + var newTargets = [].slice.call(target.getElementsByClassName('newText')); + + var textTween = []; + var totalDelay = 0; + + textTween = textTween.concat(oldTargets.map(function (el, i) { + options.duration = options.duration === 'auto' + ? oldTargetSegs[i].innerHTML.length * 75 + : options.duration; + options.delay = totalDelay; + options.onComplete = null; + + totalDelay += options.duration; + return new TweenContructor(el, { text: el.innerHTML }, { text: '' }, options); + })); + textTween = textTween.concat(newTargets.map(function (el, i) { + function onComplete() { + /* eslint-disable no-param-reassign */ + target.innerHTML = newText; + target.playing = false; + /* eslint-enable no-param-reassign */ + } + + options.duration = options.duration === 'auto' ? newTargetSegs[i].innerHTML.length * 75 : options.duration; + options.delay = totalDelay; + options.onComplete = i === newTargetSegs.length - 1 ? onComplete : null; + totalDelay += options.duration; + + return new TweenContructor(el, { text: '' }, { text: newTargetSegs[i].innerHTML }, options); + })); + + textTween.start = function startTweens() { + if (!target.playing) { + textTween.forEach(function (tw) { return tw.start(); }); + // eslint-disable-next-line no-param-reassign + target.playing = true; + } + }; + + return textTween; + } + + // Component Functions + /** + * Returns the current element `innerHTML`. + * @returns {string} computed style for property + */ + function getWrite(/* tweenProp, value */) { + return this.element.innerHTML; + } + + /** + * Returns the property tween object. + * @param {string} tweenProp the property name + * @param {string} value the property value + * @returns {number | string} the property tween object + */ + function prepareText(tweenProp, value) { + if (tweenProp === 'number') { + return parseFloat(value); + } + // empty strings crash the update function + return value === '' ? ' ' : value; + } + + // All Component Functions + var textWriteFunctions = { + prepareStart: getWrite, + prepareProperty: prepareText, + onStart: onStartWrite, + }; + + // Full Component + var TextWrite = { + component: 'textWriteProperties', + category: 'textWrite', + properties: ['text', 'number'], + defaultValues: { text: ' ', number: '0' }, + defaultOptions: { textChars: 'alpha' }, + Interpolate: { numbers: numbers }, + functions: textWriteFunctions, + // export to global for faster execution + Util: { charSet: charSet, createTextTweens: createTextTweens }, + }; + + /** + * Array Interpolation Function. + * + * @param {number[]} a start array + * @param {number[]} b end array + * @param {number} v progress + * @returns {number[]} the resulting array + */ + function arrays(a, b, v) { + var result = []; var length = b.length; + for (var i = 0; i < length; i += 1) { + // eslint-disable-next-line no-bitwise + result[i] = ((a[i] + (b[i] - a[i]) * v) * 1000 >> 0) / 1000; + } + return result; + } + + // Component special + // this component is restricted to modern browsers only + var CSS3Matrix = typeof (DOMMatrix) !== 'undefined' ? DOMMatrix : null; + + // Component Functions + var onStartTransform = { + /** + * Sets the property update function. + * @param {string} tweenProp the property name + */ + transform: function transform(tweenProp) { + if (CSS3Matrix && this.valuesEnd[tweenProp] && !KEC[tweenProp]) { + KEC[tweenProp] = function (elem, a, b, v) { + var matrix = new CSS3Matrix(); + var tObject = {}; + + Object.keys(b).forEach(function (p) { + tObject[p] = p === 'perspective' ? numbers(a[p], b[p], v) : arrays(a[p], b[p], v); + }); + + // set perspective + if (tObject.perspective) { matrix.m34 = -1 / tObject.perspective; } + + // set translate + matrix = tObject.translate3d + ? matrix.translate(tObject.translate3d[0], tObject.translate3d[1], tObject.translate3d[2]) + : matrix; + + // set rotation + matrix = tObject.rotate3d + ? matrix.rotate(tObject.rotate3d[0], tObject.rotate3d[1], tObject.rotate3d[2]) + : matrix; + + // set skew + if (tObject.skew) { + matrix = tObject.skew[0] ? matrix.skewX(tObject.skew[0]) : matrix; + matrix = tObject.skew[1] ? matrix.skewY(tObject.skew[1]) : matrix; + } + + // set scale + matrix = tObject.scale3d + ? matrix.scale(tObject.scale3d[0], tObject.scale3d[1], tObject.scale3d[2]) + : matrix; + + // set element style + // eslint-disable-next-line no-param-reassign + elem.style[tweenProp] = matrix.toString(); + }; + } + }, + /** + * onStartTransform.CSS3Matrix + * + * Sets the update function for the property. + * @param {string} prop the property name + */ + CSS3Matrix: function CSS3Matrix$1(prop) { + if (CSS3Matrix && this.valuesEnd.transform) { + if (!KEC[prop]) { KEC[prop] = CSS3Matrix; } + } + }, + }; + + // Component name + var matrixComponent = 'transformMatrix'; + + // Component Functions + /** + * Returns the current transform object. + * @param {string} _ the property name + * @param {string} value the property value + * @returns {KUTE.transformMObject} transform object + */ + function getTransform(/* tweenProp, */_, value) { + var transformObject = {}; + var currentValue = this.element[matrixComponent]; + + if (currentValue) { + Object.keys(currentValue).forEach(function (vS) { + transformObject[vS] = currentValue[vS]; + }); + } else { + Object.keys(value).forEach(function (vE) { + transformObject[vE] = vE === 'perspective' ? value[vE] : defaultValues.transform[vE]; + }); + } + return transformObject; + } + + /** + * Returns the property tween object. + * @param {string} _ the property name + * @param {Object} obj the property value + * @returns {KUTE.transformMObject} the property tween object + */ + function prepareTransform(/* tweenProp, */_, value) { + if (typeof (value) === 'object' && !value.length) { + var pv; + var transformObject = {}; + var translate3dObj = {}; + var rotate3dObj = {}; + var scale3dObj = {}; + var skewObj = {}; + var axis = [{ translate3d: translate3dObj }, + { rotate3d: rotate3dObj }, + { skew: skewObj }, + { scale3d: scale3dObj }]; + + Object.keys(value).forEach(function (prop) { + if (/3d/.test(prop) && typeof (value[prop]) === 'object' && value[prop].length) { + pv = value[prop].map(function (v) { return (prop === 'scale3d' ? parseFloat(v) : parseInt(v, 10)); }); + transformObject[prop] = prop === 'scale3d' ? [pv[0] || 1, pv[1] || 1, pv[2] || 1] : [pv[0] || 0, pv[1] || 0, pv[2] || 0]; + } else if (/[XYZ]/.test(prop)) { + var obj = {}; + if (/translate/.test(prop)) { + obj = translate3dObj; + } else if (/rotate/.test(prop)) { + obj = rotate3dObj; + } else if (/scale/.test(prop)) { + obj = scale3dObj; + } else if (/skew/.test(prop)) { + obj = skewObj; + } + var idx = prop.replace(/translate|rotate|scale|skew/, '').toLowerCase(); + obj[idx] = /scale/.test(prop) ? parseFloat(value[prop]) : parseInt(value[prop], 10); + } else if (prop === 'skew') { + pv = value[prop].map(function (v) { return parseInt(v, 10) || 0; }); + transformObject[prop] = [pv[0] || 0, pv[1] || 0]; + } else { // perspective + transformObject[prop] = parseInt(value[prop], 10); + } + }); + + axis.forEach(function (o) { + var tp = Object.keys(o)[0]; + var tv = o[tp]; + if (Object.keys(tv).length && !transformObject[tp]) { + if (tp === 'scale3d') { + transformObject[tp] = [tv.x || 1, tv.y || 1, tv.z || 1]; + } else if (tp === 'skew') { + transformObject[tp] = [tv.x || 0, tv.y || 0]; + } else { // translate | rotate + transformObject[tp] = [tv.x || 0, tv.y || 0, tv.z || 0]; + } + } + }); + return transformObject; + } // string | array + // if ( typeof (value) === 'object' && value.length ) { + // } else if ( typeof (value) === string && value.includes('matrix')) { + // decompose matrix to object + throw Error(("KUTE.js - \"" + value + "\" is not valid/supported transform function")); + } + + /** + * Sets the end values for the next `to()` method call. + * @param {string} tweenProp the property name + */ + function onCompleteTransform(tweenProp) { + if (this.valuesEnd[tweenProp]) { + this.element[matrixComponent] = Object.assign({}, this.valuesEnd[tweenProp]); + } + } + + /** + * Prepare tween object in advance for `to()` method. + * @param {string} tweenProp the property name + */ + function crossCheckTransform(tweenProp) { + if (this.valuesEnd[tweenProp]) { + if (this.valuesEnd[tweenProp].perspective && !this.valuesStart[tweenProp].perspective) { + this.valuesStart[tweenProp].perspective = this.valuesEnd[tweenProp].perspective; + } + } + } + + // All Component Functions + var matrixFunctions = { + prepareStart: getTransform, + prepareProperty: prepareTransform, + onStart: onStartTransform, + onComplete: onCompleteTransform, + crossCheck: crossCheckTransform, + }; + + // Component Full Object + var matrixTransform = { + component: matrixComponent, + property: 'transform', + /* subProperties: [ + 'perspective', 'translate3d', 'translateX', 'translateY', 'translateZ', + 'rotate3d', 'rotateX', 'rotateY', 'rotateZ', + 'skew','skewX','skewY', + 'scale3d', 'scaleX', 'scaleY', 'scaleZ'], */ + defaultValue: { + perspective: 400, + translate3d: [0, 0, 0], + translateX: 0, + translateY: 0, + translateZ: 0, + rotate3d: [0, 0, 0], + rotateX: 0, + rotateY: 0, + rotateZ: 0, + skew: [0, 0], + skewX: 0, + skewY: 0, + scale3d: [1, 1, 1], + scaleX: 1, + scaleY: 1, + scaleZ: 1, + }, + functions: matrixFunctions, + Interpolate: { + perspective: numbers, + translate3d: arrays, + rotate3d: arrays, + skew: arrays, + scale3d: arrays, + }, + }; + + var Components = { + BackgroundPosition: BackgroundPosition, + BorderRadius: BorderRadius, + BoxModel: BoxModel, + ClipProperty: ClipProperty, + ColorProperties: colorProperties, + FilterEffects: filterEffects, + HTMLAttributes: htmlAttributes, + OpacityProperty: OpacityProperty, + SVGDraw: SvgDrawProperty, + SVGCubicMorph: svgCubicMorph, + SVGTransform: svgTransform, + ScrollProperty: ScrollProperty, + ShadowProperties: ShadowProperties, + TextProperties: TextProperties, + TextWriteProperties: TextWrite, + MatrixTransform: matrixTransform, + }; + + // init components + Object.keys(Components).forEach(function (component) { + var compOps = Components[component]; + Components[component] = new AnimationDevelopment(compOps); + }); + + var version = "2.2.2"; + + // @ts-ignore + + /** + * A global namespace for library version. + * @type {string} + */ + var Version = version; + + var indexExtra = { + Animation: AnimationDevelopment, + Components: Components, + + // Tween Interface + Tween: TweenExtra, + fromTo: fromTo, + to: to, + // Tween Collection + TweenCollection: TweenCollection, + ProgressBar: ProgressBar, + allFromTo: allFromTo, + allTo: allTo, + // Tween Interface + + Objects: Objects, + Util: Util, + Easing: Easing, + CubicBezier: CubicBezier, + Render: Render, + Interpolate: interpolate, + Process: Process, + Internals: internals, + Selector: selector, + Version: Version, + }; + + return indexExtra; + +})); diff --git a/src/kute-extra.min.js b/src/kute-extra.min.js new file mode 100644 index 0000000..8ad4417 --- /dev/null +++ b/src/kute-extra.min.js @@ -0,0 +1,2 @@ +// KUTE.js Extra v2.2.2 | thednp © 2021 | MIT-License +!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):(t="undefined"!=typeof globalThis?globalThis:t||self).KUTE=e()}(this,(function(){"use strict";var t=function(t,e,n,r,a){var i=this;this.cx=3*t,this.bx=3*(n-t)-this.cx,this.ax=1-this.cx-this.bx,this.cy=3*e,this.by=3*(r-e)-this.cy,this.ay=1-this.cy-this.by;var o=function(t){return i.sampleCurveY(i.solveCurveX(t))};return Object.defineProperty(o,"name",{writable:!0}),o.name=a||"cubic-bezier("+[t,e,n,r]+")",o};t.prototype.sampleCurveX=function(t){return((this.ax*t+this.bx)*t+this.cx)*t},t.prototype.sampleCurveY=function(t){return((this.ay*t+this.by)*t+this.cy)*t},t.prototype.sampleCurveDerivativeX=function(t){return(3*this.ax*t+2*this.bx)*t+this.cx},t.prototype.solveCurveX=function(t){var e,n,r,a,i,o,s=1e-5;for(r=t,o=0;o<32;o+=1){if(a=this.sampleCurveX(r)-t,Math.abs(a)(n=1))return n;for(;ea?e=r:n=r,r=.5*(n-e)+e}return r};Object.assign(t,{Version:"1.0.18"});var e={},n=[],r="undefined"!=typeof global?global:"undefined"!=typeof window?window.self:{},a={},i={},o="undefined"==typeof self&&"undefined"!=typeof process&&process.hrtime?function(){var t=process.hrtime();return 1e3*t[0]+t[1]/1e6}:"undefined"!=typeof self&&void 0!==self.performance&&void 0!==self.performance.now?self.performance.now.bind(self.performance):"undefined"!=typeof Date&&Date.now?Date.now:function(){return(new Date).getTime()},s={};s.now=o;var u=0,c=function(t){for(var e=0;e1?1:n;var i=this._easing(n);return Object.keys(this.valuesEnd).forEach((function(t){e[t](r.element,r.valuesStart[t],r.valuesEnd[t],i)})),this._onUpdate&&this._onUpdate.call(this),1!==n||(this._onComplete&&this._onComplete.call(this),this.playing=!1,this.close(),void 0!==this._chain&&this._chain.length&&this._chain.map((function(t){return t.start()})),!1)},I.tween=V;var N=function(t){function n(){for(var e=this,n=[],r=arguments.length;r--;)n[r]=arguments[r];t.apply(this,n),this.valuesStart={},this.valuesEnd={};var a=n.slice(1),i=a[0],o=a[1],s=a[2];return _.call(this,o,"end"),this._resetStart?this.valuesStart=i:_.call(this,i,"start"),this._resetStart||Object.keys(m).forEach((function(t){Object.keys(m[t]).forEach((function(n){m[t][n].call(e,n)}))})),this.paused=!1,this._pauseTime=null,this._repeat=s.repeat||d.repeat,this._repeatDelay=s.repeatDelay||d.repeatDelay,this._repeatOption=this._repeat,this.valuesRepeat={},this._yoyo=s.yoyo||d.yoyo,this._reversed=!1,this}return t&&(n.__proto__=t),n.prototype=Object.create(t&&t.prototype),n.prototype.constructor=n,n.prototype.start=function(e){var n=this;return this._resetStart&&(this.valuesStart=this._resetStart,C.call(this),Object.keys(m).forEach((function(t){Object.keys(m[t]).forEach((function(e){m[t][e].call(n,e)}))}))),this.paused=!1,this._yoyo&&Object.keys(this.valuesEnd).forEach((function(t){n.valuesRepeat[t]=n.valuesStart[t]})),t.prototype.start.call(this,e),this},n.prototype.stop=function(){return t.prototype.stop.call(this),!this.paused&&this.playing&&(this.paused=!1,this.stopChainedTweens()),this},n.prototype.close=function(){return t.prototype.close.call(this),this._repeatOption>0&&(this._repeat=this._repeatOption),this._yoyo&&!0===this._reversed&&(this.reverse(),this._reversed=!1),this},n.prototype.resume=function(){return this.paused&&this.playing&&(this.paused=!1,void 0!==this._onResume&&this._onResume.call(this),L.call(this),this._startTime+=e.Time()-this._pauseTime,x(this),u||c()),this},n.prototype.pause=function(){return!this.paused&&this.playing&&(S(this),this.paused=!0,this._pauseTime=e.Time(),void 0!==this._onPause&&this._onPause.call(this)),this},n.prototype.reverse=function(){var t=this;Object.keys(this.valuesEnd).forEach((function(e){var n=t.valuesRepeat[e];t.valuesRepeat[e]=t.valuesEnd[e],t.valuesEnd[e]=n,t.valuesStart[e]=t.valuesRepeat[e]}))},n.prototype.update=function(t){var n,r=this,a=void 0!==t?t:e.Time();if(a1?1:n;var i=this._easing(n);return Object.keys(this.valuesEnd).forEach((function(t){e[t](r.element,r.valuesStart[t],r.valuesEnd[t],i)})),this._onUpdate&&this._onUpdate.call(this),1!==n||(this._repeat>0?(Number.isFinite(this._repeat)&&(this._repeat-=1),this._startTime=a,Number.isFinite(this._repeat)&&this._yoyo&&!this._reversed&&(this._startTime+=this._repeatDelay),this._yoyo&&(this._reversed=!this._reversed,this.reverse()),!0):(this._onComplete&&this._onComplete.call(this),this.playing=!1,this.close(),void 0!==this._chain&&this._chain.length&&this._chain.forEach((function(t){return t.start()})),!1))},n}(V);I.tween=N;var R=function(t){function e(){for(var e=[],n=arguments.length;n--;)e[n]=arguments[n];return t.apply(this,e),this}return t&&(e.__proto__=t),e.prototype=Object.create(t&&t.prototype),e.prototype.constructor=e,e.prototype.on=function(t,e){return["start","stop","update","complete","pause","resume"].indexOf(t)>-1&&(this["_on"+(t.charAt(0).toUpperCase()+t.slice(1))]=e),this},e.prototype.option=function(t,e){return this["_"+t]=e,this},e}(N);I.tween=R;var U=function(t,e,n,r){var a=this,i=I.tween;this.tweens=[];var o=r||{};o.delay=o.delay||d.delay;var s=[];return Array.from(t).forEach((function(t,r){if(s[r]=o||{},s[r].delay=r>0?o.delay+(o.offset||d.offset):o.delay,!(t instanceof Element))throw Error("KUTE - "+t+" is not instanceof Element");a.tweens.push(new i(t,e,n,s[r]))})),this.length=this.tweens.length,this};U.prototype.start=function(t){var n=void 0===t?e.Time():t;return this.tweens.map((function(t){return t.start(n)})),this},U.prototype.stop=function(){return this.tweens.map((function(t){return t.stop()})),this},U.prototype.pause=function(){return this.tweens.map((function(t){return t.pause()})),this},U.prototype.resume=function(){return this.tweens.map((function(t){return t.resume()})),this},U.prototype.chain=function(t){var e=this.tweens[this.length-1];if(t instanceof U)e.chain(t.tweens);else{if(!(t instanceof I.tween))throw new TypeError("KUTE.js - invalid chain value");e.chain(t)}return this},U.prototype.playing=function(){return this.tweens.some((function(t){return t.playing}))},U.prototype.removeTweens=function(){this.tweens=[]},U.prototype.getMaxDuration=function(){var t=[];return this.tweens.forEach((function(e){t.push(e._duration+e._delay+e._repeat*e._repeatDelay)})),Math.max(t)};var F=function(t,e){var n;if(this.element=P(t),this.element.tween=e,this.element.tween.toolbar=this.element,this.element.toolbar=this,n=this.element.parentNode.getElementsByTagName("OUTPUT"),this.element.output=n[0],!(this.element instanceof HTMLInputElement))throw TypeError("Target element is not [HTMLInputElement]");if("range"!==this.element.type)throw TypeError("Target element is not a range input");if(!(e instanceof I.tween))throw TypeError("tween parameter is not ["+I.tween+"]");this.element.setAttribute("value",0),this.element.setAttribute("min",0),this.element.setAttribute("max",1),this.element.setAttribute("step",1e-4),this.element.tween._onUpdate=this.updateBar,this.element.addEventListener("mousedown",this.downAction,!1)};F.prototype.updateBar=function(){var t,n=this.toolbar.output;(t=this.paused?this.toolbar.value:(e.Time()-this._startTime)/this._duration)>.9999&&(t=1),t<.01&&(t=0);var r=this._reversed?1-t:t;this.toolbar.value=r,n&&(n.value=(1e4*r>>0)/100+"%")},F.prototype.toggleEvents=function(t){this.element[t+"EventListener"]("mousemove",this.moveAction,!1),this.element[t+"EventListener"]("mouseup",this.upAction,!1)},F.prototype.updateTween=function(){var t=(this.tween._reversed?1-this.value:this.value)*this.tween._duration-1e-4;this.tween._startTime=0,this.tween.update(t)},F.prototype.moveAction=function(){this.toolbar.updateTween.call(this)},F.prototype.downAction=function(){this.tween.playing||this.tween.start(),this.tween.paused||(this.tween.pause(),this.toolbar.toggleEvents("add"),e.Tick=cancelAnimationFrame(e.Ticker))},F.prototype.upAction=function(){this.tween.paused&&(this.tween.paused&&this.tween.resume(),this.tween._startTime=e.Time()-(this.tween._reversed?1-this.value:this.value)*this.tween._duration,this.toolbar.toggleEvents("remove"),e.Tick=requestAnimationFrame(e.Ticker))};var D=I.tween;var H=I.tween;var q=function(t){try{if(t.component in h)throw Error("KUTE - "+t.component+" already registered");if(t.property in f)throw Error("KUTE - "+t.property+" already registered")}catch(t){throw Error(t)}var e=this,n=t.component,r={prepareProperty:v,prepareStart:g,onStart:i,onComplete:y,crossCheck:m},o=t.category,s=t.property,u=t.properties&&t.properties.length||t.subProperties&&t.subProperties.length;return h[n]=t.properties||t.subProperties||t.property,"defaultValue"in t?(f[s]=t.defaultValue,e.supports=s+" property"):t.defaultValues&&(Object.keys(t.defaultValues).forEach((function(e){f[e]=t.defaultValues[e]})),e.supports=(u||s)+" "+(s||o)+" properties"),t.defaultOptions&&Object.assign(d,t.defaultOptions),t.functions&&Object.keys(r).forEach((function(e){e in t.functions&&("function"==typeof t.functions[e]?(r[e][n]||(r[e][n]={}),r[e][n][o||s]||(r[e][n][o||s]=t.functions[e])):Object.keys(t.functions[e]).forEach((function(a){r[e][n]||(r[e][n]={}),r[e][n][a]||(r[e][n][a]=t.functions[e][a])})))})),t.Interpolate&&(Object.keys(t.Interpolate).forEach((function(e){var n=t.Interpolate[e];"function"!=typeof n||a[e]?Object.keys(n).forEach((function(t){"function"!=typeof n[t]||a[e]||(a[e]=n[t])})):a[e]=n})),b[n]=t.Interpolate),t.Util&&Object.keys(t.Util).forEach((function(e){E[e]||(E[e]=t.Util[e])})),e},B=function(t){function e(e){t.call(this,e);var n=this,r={prepareProperty:v,prepareStart:g,onStart:i,onComplete:y,crossCheck:m},o=e.category,s=e.property,u=e.properties&&e.properties.length||e.subProperties&&e.subProperties.length;return"defaultValue"in e?(n.supports=s+" property",n.defaultValue=(""+e.defaultValue).length?"YES":"not set or incorrect"):e.defaultValues&&(n.supports=(u||s)+" "+(s||o)+" properties",n.defaultValues=Object.keys(e.defaultValues).length===u?"YES":"Not set or incomplete"),e.defaultOptions&&(n.extends=[],Object.keys(e.defaultOptions).forEach((function(t){n.extends.push(t)})),n.extends.length?n.extends="with <"+n.extends.join(", ")+"> new option(s)":delete n.extends),e.functions&&(n.interface=[],n.render=[],n.warning=[],Object.keys(r).forEach((function(t){t in e.functions?("prepareProperty"===t&&n.interface.push("fromTo()"),"prepareStart"===t&&n.interface.push("to()"),"onStart"===t&&(n.render="can render update")):("prepareProperty"===t&&n.warning.push("fromTo()"),"prepareStart"===t&&n.warning.push("to()"),"onStart"===t&&(n.render="no function to render update"))})),n.interface.length?n.interface=(o||s)+" can use ["+n.interface.join(", ")+"] method(s)":delete n.uses,n.warning.length?n.warning=(o||s)+" can't use ["+n.warning.join(", ")+"] method(s) because values aren't processed":delete n.warning),e.Interpolate?(n.uses=[],n.adds=[],Object.keys(e.Interpolate).forEach((function(t){var r=e.Interpolate[t];"function"==typeof r?(a[t]||n.adds.push(""+t),n.uses.push(""+t)):Object.keys(r).forEach((function(e){"function"!=typeof r[e]||a[t]||n.adds.push(""+e),n.uses.push(""+e)}))})),n.uses.length?n.uses="["+n.uses.join(", ")+"] interpolation function(s)":delete n.uses,n.adds.length?n.adds="new ["+n.adds.join(", ")+"] interpolation function(s)":delete n.adds):n.critical="For "+(s||o)+" no interpolation function[s] is set",e.Util&&(n.hasUtil=Object.keys(e.Util).join(",")),n}return t&&(e.__proto__=t),e.prototype=Object.create(t&&t.prototype),e.prototype.constructor=e,e}(q);function Q(t,e,n){return+t+(e-t)*n}var z=function(t,e){for(var n,r=parseInt(t,10)||0,a=["px","%","deg","rad","em","rem","vh","vw"],i=0;i>0)/100+"% "+(100*Q(n[1],r[1],a)>>0)/100+"%"})}},Y={component:"backgroundPositionProp",property:"backgroundPosition",defaultValue:[50,50],Interpolate:{numbers:Q},functions:X,Util:{trueDimension:z}};function W(t,e,n,r){return+t+(e-t)*r+n}function $(t){t in this.valuesEnd&&!e[t]&&(e[t]=function(e,n,r,a){e.style[t]=W(n.v,r.v,r.u,a)})}var G={};["borderRadius","borderTopLeftRadius","borderTopRightRadius","borderBottomLeftRadius","borderBottomRightRadius"].forEach((function(t){G[t]=$}));var Z=["borderRadius","borderTopLeftRadius","borderTopRightRadius","borderBottomLeftRadius","borderBottomRightRadius"],K={};Z.forEach((function(t){K[t]=0}));var J={};Z.forEach((function(t){J[t]=$}));var tt={component:"borderRadiusProperties",category:"borderRadius",properties:Z,defaultValues:K,Interpolate:{units:W},functions:{prepareStart:function(t){return M(this.element,t)||f[t]},prepareProperty:function(t,e){return z(e)},onStart:J},Util:{trueDimension:z}};function et(t){t in this.valuesEnd&&!e[t]&&(e[t]=function(e,n,r,a){e.style[t]=(a>.99||a<.01?(10*Q(n,r,a)>>0)/10:Q(n,r,a)>>0)+"px"})}var nt={};["top","left","width","height"].forEach((function(t){nt[t]=et}));var rt=["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"],at={};rt.forEach((function(t){at[t]=0}));var it={};rt.forEach((function(t){it[t]=et}));var ot={component:"boxModelProperties",category:"boxModel",properties:rt,defaultValues:at,Interpolate:{numbers:Q},functions:{prepareStart:function(t){return M(this.element,t)||f[t]},prepareProperty:function(t,e){var n=z(e),r="height"===t?"offsetHeight":"offsetWidth";return"%"===n.u?n.v*this.element[r]/100:n.v},onStart:it}};var st={prepareStart:function(t){var e=this.element,n=M(e,t),r=M(e,"width"),a=M(e,"height");return/rect/.test(n)?n:[0,r,a,0]},prepareProperty:function(t,e){if(e instanceof Array)return e.map((function(t){return z(t)}));var n=e.replace(/rect|\(|\)/g,"");return(n=/,/g.test(n)?n.split(","):n.split(/\s/)).map((function(t){return z(t)}))},onStart:function(t){this.valuesEnd[t]&&!e[t]&&(e[t]=function(t,e,n,r){for(var a=0,i=[];a<4;a+=1){var o=e[a].v,s=n[a].v,u=n[a].u||"px";i[a]=(100*Q(o,s,r)>>0)/100+u}t.style.clip="rect("+i+")"})}},ut={component:"clipProperty",property:"clip",defaultValue:[0,0,0,0],Interpolate:{numbers:Q},functions:st,Util:{trueDimension:z}},ct=function(t){var e;if(/rgb|rgba/.test(t)){var n=t.replace(/\s|\)/,"").split("(")[1].split(","),r=n[3]?n[3]:null;r||(e={r:parseInt(n[0],10),g:parseInt(n[1],10),b:parseInt(n[2],10)}),e={r:parseInt(n[0],10),g:parseInt(n[1],10),b:parseInt(n[2],10),a:parseFloat(r)}}if(/^#/.test(t)){var a=function(t){var e=t.replace(/^#?([a-f\d])([a-f\d])([a-f\d])$/i,(function(t,e,n,r){return e+e+n+n+r+r})),n=/^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(e);return n?{r:parseInt(n[1],16),g:parseInt(n[2],16),b:parseInt(n[3],16)}:null}(t);e={r:a.r,g:a.g,b:a.b}}if(/transparent|none|initial|inherit/.test(t)&&(e={r:0,g:0,b:0,a:0}),!/^#|^rgb/.test(t)){var i=document.getElementsByTagName("head")[0];i.style.color=t;var o=getComputedStyle(i,null).color;o=/rgb/.test(o)?o.replace(/[^\d,]/g,"").split(","):[0,0,0],i.style.color="",e={r:parseInt(o[0],10),g:parseInt(o[1],10),b:parseInt(o[2],10)}}return e};function lt(t,e,n){var r={},a=",";return Object.keys(e).forEach((function(a){"a"!==a?r[a]=Q(t[a],e[a],n)>>0||0:t[a]&&e[a]&&(r[a]=(100*Q(t[a],e[a],n)>>0)/100)})),r.a?"rgba("+r.r+a+r.g+a+r.b+a+r.a+")":"rgb("+r.r+a+r.g+a+r.b+")"}function pt(t){this.valuesEnd[t]&&!e[t]&&(e[t]=function(e,n,r,a){e.style[t]=lt(n,r,a)})}var ht={};["color","backgroundColor","outlineColor","borderColor","borderTopColor","borderRightColor","borderBottomColor","borderLeftColor"].forEach((function(t){ht[t]=pt}));var ft=["color","backgroundColor","outlineColor","borderColor","borderTopColor","borderRightColor","borderBottomColor","borderLeftColor"],dt={};ft.forEach((function(t){dt[t]="#000"}));var vt={};ft.forEach((function(t){vt[t]=pt}));var gt={component:"colorProperties",category:"colors",properties:ft,defaultValues:dt,Interpolate:{numbers:Q,colors:lt},functions:{prepareStart:function(t){return M(this.element,t)||f[t]},prepareProperty:function(t,e){return ct(e)},onStart:vt},Util:{trueColor:ct}};function mt(t,e,n){for(var r=[],a=0;a<3;a+=1)r[a]=(100*Q(t[a],e[a],n)>>0)/100+"px";return"drop-shadow("+r.concat(lt(t[3],e[3],n)).join(" ")+")"}function yt(t){return t.replace("-r","R").replace("-s","S")}function bt(t){var e;3===t.length?e=[t[0],t[1],0,t[2]]:4===t.length&&(e=[t[0],t[1],t[2],t[3]]);for(var n=0;n<3;n+=1)e[n]=parseFloat(e[n]);return e[3]=ct(e[3]),e}function wt(t){var e={},n=t.match(/(([a-z].*?)\(.*?\))(?=\s([a-z].*?)\(.*?\)|\s*$)/g),r="none"!==t?n:"none";if(r instanceof Array)for(var a=0,i=r.length;a>0)/100+"%)":"")+(n.blur||r.blur?"blur("+(100*Q(n.blur,r.blur,a)>>0)/100+"em)":"")+(n.saturate||r.saturate?"saturate("+(100*Q(n.saturate,r.saturate,a)>>0)/100+"%)":"")+(n.invert||r.invert?"invert("+(100*Q(n.invert,r.invert,a)>>0)/100+"%)":"")+(n.grayscale||r.grayscale?"grayscale("+(100*Q(n.grayscale,r.grayscale,a)>>0)/100+"%)":"")+(n.hueRotate||r.hueRotate?"hue-rotate("+(100*Q(n.hueRotate,r.hueRotate,a)>>0)/100+"deg)":"")+(n.sepia||r.sepia?"sepia("+(100*Q(n.sepia,r.sepia,a)>>0)/100+"%)":"")+(n.brightness||r.brightness?"brightness("+(100*Q(n.brightness,r.brightness,a)>>0)/100+"%)":"")+(n.contrast||r.contrast?"contrast("+(100*Q(n.contrast,r.contrast,a)>>0)/100+"%)":"")+(n.dropShadow||r.dropShadow?mt(n.dropShadow,r.dropShadow,a):"")})},crossCheck:function(t){var e=this;this.valuesEnd[t]&&Object.keys(this.valuesStart[t]).forEach((function(n){e.valuesEnd[t][n]||(e.valuesEnd[t][n]=e.valuesStart[t][n])}))}},xt={component:"filterEffects",property:"filter",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:Q,blur:Q,saturate:Q,grayscale:Q,brightness:Q,contrast:Q,sepia:Q,invert:Q,hueRotate:Q,dropShadow:{numbers:Q,colors:lt,dropshadow:mt}},functions:Et,Util:{parseDropShadow:bt,parseFilterString:wt,replaceDashNamespace:yt,trueColor:ct}},St={},kt="htmlAttributes",Tt=["fill","stroke","stop-color"];function Ot(t){return t.replace(/[A-Z]/g,"-$&").toLowerCase()}var Mt={prepareStart:function(t,e){var n=this,r={};return Object.keys(e).forEach((function(t){var e=Ot(t).replace(/_+[a-z]+/,""),a=n.element.getAttribute(e);r[e]=Tt.includes(e)?a||"rgba(0,0,0,0)":a||(/opacity/i.test(t)?1:0)})),r},prepareProperty:function(t,e){var n=this,r={};return Object.keys(e).forEach((function(a){var o=Ot(a),s=/(%|[a-z]+)$/,u=n.element.getAttribute(o.replace(/_+[a-z]+/,""));if(Tt.includes(o))i.htmlAttributes[o]=function(e){n.valuesEnd[t]&&n.valuesEnd[t][e]&&!(e in St)&&(St[e]=function(t,e,n,r,a){t.setAttribute(e,lt(n,r,a))})},r[o]=ct(e[a])||f.htmlAttributes[a];else if(null!==u&&s.test(u)){var c=z(u).u||z(e[a]).u,l=/%/.test(c)?"_percent":"_"+c;i.htmlAttributes[o+l]=function(e){n.valuesEnd[t]&&n.valuesEnd[t][e]&&!(e in St)&&(St[e]=function(t,e,n,r,a){var i=e.replace(l,"");t.setAttribute(i,(1e3*Q(n.v,r.v,a)>>0)/1e3+r.u)})},r[o+l]=z(e[a])}else s.test(e[a])&&null!==u&&(null===u||s.test(u))||(i.htmlAttributes[o]=function(e){n.valuesEnd[t]&&n.valuesEnd[t][e]&&!(e in St)&&(St[e]=function(t,e,n,r,a){t.setAttribute(e,(1e3*Q(n,r,a)>>0)/1e3)})},r[o]=parseFloat(e[a]))})),r},onStart:{attr:function(t){!e[t]&&this.valuesEnd[t]&&(e[t]=function(t,n,r,a){Object.keys(r).forEach((function(i){e.attributes[i](t,i,n[i],r[i],a)}))})},attributes:function(t){!e[t]&&this.valuesEnd.attr&&(e[t]=St)}}},_t={component:kt,property:"attr",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},Interpolate:{numbers:Q,colors:lt},functions:Mt,Util:{replaceUppercase:Ot,trueColor:ct,trueDimension:z}};var Ct={prepareStart:function(t){return M(this.element,t)},prepareProperty:function(t,e){return parseFloat(e)},onStart:function(t){t in this.valuesEnd&&!e[t]&&(e[t]=function(e,n,r,a){e.style[t]=(1e3*Q(n,r,a)>>0)/1e3})}},jt={component:"opacityProperty",property:"opacity",defaultValue:1,Interpolate:{numbers:Q},functions:Ct};function It(t,e){return parseFloat(t)/100*e}function At(t){return 2*t.getAttribute("width")+2*t.getAttribute("height")}function Pt(t){var e=t.getAttribute("points").split(" "),n=0;if(e.length>1){var r=function(t){var e=t.split(",");return 2!==e.length||Number.isNaN(1*e[0])||Number.isNaN(1*e[1])?0:[parseFloat(e[0]),parseFloat(e[1])]},a=function(t,e){return void 0!==t&&void 0!==e?Math.sqrt(Math.pow(e[0]-t[0],2)+Math.pow(e[1]-t[1],2)):0};if(e.length>2)for(var i=0;i>0)/100,i=0-(100*Q(e.s,n.s,r)>>0)/100,o=(100*Q(e.e,n.e,r)>>0)/100+i;t.style.strokeDashoffset=i+"px",t.style.strokeDasharray=(100*(o<1?0:o)>>0)/100+"px, "+a+"px"})}},Dt={component:"svgDraw",property:"draw",defaultValue:"0% 0%",Interpolate:{numbers:Q},functions:Ft,Util:{getRectLength:At,getPolyLength:Pt,getLineLength:Lt,getCircleLength:Vt,getEllipseLength:Nt,getTotalLength:Rt,resetDraw:function(t){t.style.strokeDashoffset="",t.style.strokeDasharray=""},getDraw:Ut,percent:It}},Ht={a:7,c:6,h:1,l:2,m:2,r:4,q:4,s:4,t:2,v:1,z:0};function qt(t){var e=t.pathValue[t.segmentStart],n=e.toLowerCase(),r=t.data;for("m"===n&&r.length>2&&(t.segments.push([e,r[0],r[1]]),r=r.slice(2),n="l",e="m"===e?"l":"L");r.length>=Ht[n]&&(t.segments.push([e].concat(r.splice(0,Ht[n]))),Ht[n]););}var Bt="Invalid path value";function Qt(t){var e=t.index,n=t.pathValue.charCodeAt(e);return 48===n?(t.param=0,void(t.index+=1)):49===n?(t.param=1,void(t.index+=1)):void(t.err=Bt+': invalid Arc flag "'+n+'", expecting 0 or 1 at index '+e)}function zt(t){return t>=48&&t<=57}function Xt(t){var e,n=t.max,r=t.pathValue,a=t.index,i=a,o=!1,s=!1,u=!1,c=!1;if(i>=n)t.err=Bt+" at "+i+": missing param "+r[i];else if(43!==(e=r.charCodeAt(i))&&45!==e||(e=(i+=1)=5760&&[5760,6158,8192,8193,8194,8195,8196,8197,8198,8199,8200,8201,8202,8239,8287,12288,65279].indexOf(e)>=0);)t.index+=1}function Wt(t){return t>=48&&t<=57||43===t||45===t||46===t}function $t(t){var e=t.max,n=t.pathValue,r=t.index,a=n.charCodeAt(r),i=Ht[n[r].toLowerCase()];if(t.segmentStart=r,function(t){switch(32|t){case 109:case 122:case 108:case 104:case 118:case 99:case 115:case 113:case 116:case 97:return!0;default:return!1}}(a))if(t.index+=1,Yt(t),t.data=[],i){for(;;){for(var o=i;o>0;o-=1){if(97!=(32|a)||3!==o&&4!==o?Xt(t):Qt(t),t.err.length)return;t.data.push(t.param),Yt(t),t.index=t.max)break;if(!Wt(n.charCodeAt(t.index)))break}qt(t)}else qt(t);else t.err=Bt+": "+n[r]+" not a path command"}function Gt(t){return t.map((function(t){return Array.isArray(t)?[].concat(t):t}))}function Zt(t){this.segments=[],this.pathValue=t,this.max=t.length,this.index=0,this.param=0,this.segmentStart=0,this.data=[],this.err=""}function Kt(t){return Array.isArray(t)&&t.every((function(t){var e=t[0].toLowerCase();return Ht[e]===t.length-1&&"achlmqstvz".includes(e)}))}function Jt(t){if(Kt(t))return Gt(t);var e=new Zt(t);for(Yt(e);e.index7){t[n].shift();for(var r=t[n],a=n;r.length;)e[n]="A",t.splice(a+=1,0,["C"].concat(r.splice(0,6)));t.splice(n,1)}}function re(t,e,n){var r=t[0],a=e.x1,i=e.y1,o=e.x2,s=e.y2,u=t.slice(1).map(Number),c=t;if("TQ".includes(r)||(e.qx=null,e.qy=null),"H"===r)c=["L",t[1],i];else if("V"===r)c=["L",a,t[1]];else if("S"===r){var l=function(t,e,n,r,a){return"CS".includes(a)?{x1:2*t-n,y1:2*e-r}:{x1:t,y1:e}}(a,i,o,s,n),p=l.x1,h=l.y1;e.x1=p,e.y1=h,c=["C",p,h].concat(u)}else if("T"===r){var f=function(t,e,n,r,a){return"QT".includes(a)?{qx:2*t-n,qy:2*e-r}:{qx:t,qy:e}}(a,i,e.qx,e.qy,n),d=f.qx,v=f.qy;e.qx=d,e.qy=v,c=["Q",d,v].concat(u)}else if("Q"===r){var g=u[0],m=u[1];e.qx=g,e.qy=m}return c}var ae={x1:0,y1:0,x2:0,y2:0,x:0,y:0,qx:null,qy:null};function ie(t){if(function(t){return te(t)&&t.every((function(t){return"ACLMQZ".includes(t[0])}))}(t))return Gt(t);for(var e=ee(t),n=Object.assign({},ae),r=[],a=e.length,i="",o="",s=0;s1&&(y*=M=Math.sqrt(M),b*=M);var _=y*y,C=b*b,j=(i===o?-1:1)*Math.sqrt(Math.abs((_*C-_*O*O-C*T*T)/(_*O*O+C*T*T)));d=j*y*O/b+(g+w)/2,v=j*-b*T/y+(m+E)/2,h=(Math.asin((m-v)/b)*Math.pow(10,9)>>0)/Math.pow(10,9),f=(Math.asin((E-v)/b)*Math.pow(10,9)>>0)/Math.pow(10,9),h=gf&&(h-=2*Math.PI),!o&&f>h&&(f-=2*Math.PI)}var I=f-h;if(Math.abs(I)>x){var A=f,P=w,L=E;f=h+x*(o&&f>h?1:-1),k=ue(w=d+y*Math.cos(f),E=v+b*Math.sin(f),y,b,a,0,o,P,L,[f,A,d,v])}I=f-h;var V=Math.cos(h),N=Math.sin(h),R=Math.cos(f),U=Math.sin(f),F=Math.tan(I/4),D=4/3*y*F,H=4/3*b*F,q=[g,m],B=[g+D*N,m-H*V],Q=[w+D*U,E-H*R],z=[w,E];if(B[0]=2*q[0]-B[0],B[1]=2*q[1]-B[1],c)return B.concat(Q,z,k);for(var X=[],Y=0,W=(k=B.concat(Q,z,k)).length;Yi+.001)return{x:n,y:r};var o=le([t,e],[n,r],a/i);return{x:o[0],y:o[1]}}return i}function fe(t,e,n,r){var a=.5,i=[t,e],o=[n,r],s=le(i,o,a),u=le(o,s,a),c=le(s,u,a),l=le(u,c,a),p=le(c,l,a),h=i.concat(s,c,p,[a]),f=he.apply(void 0,h),d=p.concat(l,u,o,[0]),v=he.apply(void 0,d);return[f.x,f.y,v.x,v.y,n,r]}function de(t,e){var n,r=t[0],a=t.slice(1).map((function(t){return+t})),i=a[0],o=a[1],s=e.x1,u=e.y1,c=e.x,l=e.y;switch("TQ".includes(r)||(e.qx=null,e.qy=null),r){case"M":return e.x=i,e.y=o,t;case"A":return n=[s,u].concat(a),["C"].concat(ue.apply(void 0,n));case"Q":return e.qx=i,e.qy=o,n=[s,u].concat(a),["C"].concat(ce.apply(void 0,n));case"L":return["C"].concat(fe(s,u,i,o));case"Z":return["C"].concat(fe(s,u,c,l))}return t}function ve(t){if(function(t){return Kt(t)&&t.every((function(t){return"MC".includes(t[0])}))}(t))return Gt(t);for(var e=oe(ie(t)),n=Object.assign({},ae),r=[],a="",i=e.length,o=0;o=1?e:n)>=1?Math.pow(10,n):1;return t.map((function(t){var e=t.slice(1).map(Number).map((function(t){return t%1==0?t:Math.round(t*r)/r}));return[t[0]].concat(e)}))}(t,e).map((function(t){return t[0]+t.slice(1).join(" ")})).join("")}function ye(t){var e=t.slice(1).map((function(e,n,r){return n?r[n-1].slice(-2).concat(e.slice(1)):t[0].slice(1).concat(e.slice(1))})).map((function(t){return t.map((function(e,n){return t[t.length-n-2*(1-n%2)]}))})).reverse();return[["M"].concat(e[0].slice(0,2))].concat(e.map((function(t){return["C"].concat(t.slice(2))})))}function be(t,e,n,r,a,i,o,s){return 3*((s-e)*(n+a)-(o-t)*(r+i)+r*(t-a)-n*(e-i)+s*(a+t/3)-o*(i+e/3))/20}function we(t){return function(t){var e=0,n=0,r=0;return ve(t).map((function(t){var a,i;return"M"===t[0]?(e=(a=t)[1],n=a[2],0):(r=be.apply(void 0,[e,n].concat(t.slice(1))),i=t.slice(-2),e=i[0],n=i[1],r)})).reduce((function(t,e){return t+e}),0)}(ve(t))>=0}function Ee(t){var e=.5,n=t.slice(0,2),r=t.slice(2,4),a=t.slice(4,6),i=t.slice(6,8),o=le(n,r,e),s=le(r,a,e),u=le(a,i,e),c=le(o,s,e),l=le(s,u,e),p=le(c,l,e);return[["C"].concat(o,c,p),["C"].concat(l,u,i)]}function xe(t){return me(ee(t),0).replace(/(m|M)/g,"|$1").split("|").map((function(t){return t.trim()})).filter((function(t){return t}))}function Se(t,e,n,r,a,i,o,s,u){var c=1-u;return{x:Math.pow(c,3)*t+3*Math.pow(c,2)*u*n+3*c*Math.pow(u,2)*a+Math.pow(u,3)*o,y:Math.pow(c,3)*e+3*Math.pow(c,2)*u*r+3*c*Math.pow(u,2)*i+Math.pow(u,3)*s}}function ke(t,e,n,r,a,i,o,s,u){var c,l=t,p=e,h=0,f=[t,e,h],d=[t,e];if("number"==typeof u&&u<.001)return{x:l,y:p};for(var v=0;v<=100;v+=1){if(h+=pe(d,[l=(c=Se(t,e,n,r,a,i,o,s,v/100)).x,p=c.y]),d=[l,p],"number"==typeof u&&h>=u){var g=(h-u)/(h-f[2]);return{x:d[0]*(1-g)+f[0]*g,y:d[1]*(1-g)+f[1]*g}}f=[l,p,h]}return"number"==typeof u&&u>=h?{x:o,y:s}:h}function Te(t){return ve(xe(t)[0]).map((function(t,e,n){var r=e&&n[e-1].slice(-2).concat(t.slice(1)),a=e?ke.apply(void 0,r):0;return{s:t,ss:e?a?Ee(r):[t,t]:[t],l:a}}))}function Oe(t,e,n){var r=Te(t),a=Te(e),i=r.length,o=a.length,s=r.filter((function(t){return t.l})).length,u=a.filter((function(t){return t.l})).length,c=r.filter((function(t){return t.l})).reduce((function(t,e){return t+e.l}),0)/s||0,l=a.filter((function(t){return t.l})).reduce((function(t,e){return t+e.l}),0)/u||0,p=n||Math.max(i,o),h=[c,l],f=[p-i,p-o],d=0,v=[r,a].map((function(t,e){return t.l===p?t.map((function(t){return t.s})):t.map((function(t,n){return d=n&&f[e]&&t.l>=h[e],f[e]-=d?1:0,d?t.ss:[t.s]})).flat()}));return v[0].length===v[1].length?v:Oe(v[0],v[1],p)}function Me(t){var e=t.length,n=e-1;return t.map((function(r,a){return t.map((function(r,i){var o,s=a+i;return 0===i||t[s]&&"M"===t[s][0]?(o=t[s],["M"].concat(o.slice(-2))):(s>=e&&(s-=n),t[s])}))}))}function _e(t,e){var n=t.length-1,r=[],a=0,i=Me(t);return i.forEach((function(i,o){t.slice(1).forEach((function(r,i){a+=pe(t[(o+i)%n].slice(-2),e[i%n].slice(-2))})),r[o]=a,a=0})),i[r.indexOf(Math.min.apply(null,r))]}var Ce={prepareStart:function(){return this.element.getAttribute("d")},prepareProperty:function(t,e){var n={},r=new RegExp("\\n","ig"),a=null;return e instanceof SVGElement?a=e:/^\.|^#/.test(e)&&(a=P(e)),"object"==typeof e&&e.curve?e:(a&&/path|glyph/.test(a.tagName)?n.original=a.getAttribute("d").replace(r,""):a||"string"!=typeof e||(n.original=e.replace(r,"")),n)},onStart:function(t){!e[t]&&this.valuesEnd[t]&&(e[t]=function(t,e,n,r){for(var a=[],i=e.curve,o=n.curve,s=0,u=o.length;s>0)/1e3)}t.setAttribute("d",1===r?n.original:me(a))})},crossCheck:function(t){if(this.valuesEnd[t]){var e=this.valuesStart[t].curve,n=this.valuesEnd[t].curve;if(!e||!n||e&&n&&"M"===e[0][0]&&e.length!==n.length){var r=Oe(this.valuesStart[t].original,this.valuesEnd[t].original),a=we(r[0])!==we(r[1])?ye(r[0]):Gt(r[0]);this.valuesStart[t].curve=a,this.valuesEnd[t].curve=_e(r[1],a)}}}},je={component:"svgCubicMorph",property:"path",defaultValue:[],Interpolate:{numbers:Q,pathToString:me},functions:Ce,Util:{pathToCurve:ve,pathToAbsolute:ee,pathToString:me,parsePathString:Jt,getRotatedCurve:_e,getRotations:Me,equalizeSegments:Oe,reverseCurve:ye,clonePath:Gt,getDrawDirection:we,segmentCubicFactory:ke,splitCubic:Ee,splitPath:xe,fixPath:oe,getCurveArray:Te}};function Ie(t,e){var n=e.x,r=e.width;return/[a-z]/i.test(t)&&!/px/.test(t)?t.replace(/top|left/,0).replace(/right|bottom/,100).replace(/center|middle/,50):/%/.test(t)?n+parseFloat(t)*r/100:parseFloat(t)}function Ae(t){var e={},n=t&&/\)/.test(t)?t.substring(0,t.length-1).split(/\)\s|\)/):"none";if(n instanceof Array)for(var r=0,a=n.length;r>0)/1e3+(i?","+(1e3*i>>0)/1e3:"")+")":"")+(u?"rotate("+(1e3*u>>0)/1e3+")":"")+(p?"skewX("+(1e3*p>>0)/1e3+")":"")+(h?"skewY("+(1e3*h>>0)/1e3+")":"")+(1!==s?"scale("+(1e3*s>>0)/1e3+")":""))})},crossCheck:function(t){if(this._resetStart&&this.valuesEnd[t]){var e=this.valuesStart[t],n=this.valuesEnd[t],r=Pe.call(this,t,Ae(this.element.getAttribute("transform")));Object.keys(r).forEach((function(t){e[t]=r[t]}));var a=this.element.ownerSVGElement,i=a.createSVGTransformFromMatrix(a.createSVGMatrix().translate(-e.origin[0],-e.origin[1]).translate("translate"in e?e.translate[0]:0,"translate"in e?e.translate[1]:0).rotate(e.rotate||0).skewX(e.skewX||0).skewY(e.skewY||0).scale(e.scale||1).translate(+e.origin[0],+e.origin[1]));e.translate=[i.matrix.e,i.matrix.f],Object.keys(e).forEach((function(t){t in n&&"origin"!==t||(n[t]=e[t])}))}}},Ve={component:"svgTransformProperty",property:"svgTransform",defaultOptions:{transformOrigin:"50% 50%"},defaultValue:{translate:0,rotate:0,skewX:0,skewY:0,scale:1},Interpolate:{numbers:Q},functions:Le,Util:{parseStringOrigin:Ie,parseTransformString:Ae,parseTransformSVG:Pe}},Ne=!!function(){var t=!1;try{var e=Object.defineProperty({},"passive",{get:function(){return t=!0}});document.addEventListener("DOMContentLoaded",(function t(){document.removeEventListener("DOMContentLoaded",t,e)}),e)}catch(t){throw Error("Passive events are not supported")}return t}()&&{passive:!0},Re="onmouseleave"in document?["mouseenter","mouseleave"]:["mouseover","mouseout"],Ue="ontouchstart"in window||"msMaxTouchPoints"in navigator?"touchstart":"mousewheel",Fe=navigator&&/(EDGE|Mac)/i.test(navigator.userAgent)?document.body:document.documentElement;function De(t){this.scrolling&&t.preventDefault()}function He(){var t=this.element;return t===Fe?{el:document,st:document.body}:{el:t,st:t}}function qe(t,e){e[t](Re[0],De,Ne),e[t](Ue,De,Ne)}function Be(){var t=He.call(this);"scroll"in this.valuesEnd&&!t.el.scrolling&&(t.el.scrolling=1,qe("addEventListener",t.el),t.st.style.pointerEvents="none")}function Qe(){var t=He.call(this);"scroll"in this.valuesEnd&&t.el.scrolling&&(t.el.scrolling=0,qe("removeEventListener",t.el),t.st.style.pointerEvents="")}var ze={prepareStart:function(){return this.element=!("scroll"in this.valuesEnd)||this.element&&this.element!==window?this.element:Fe,this.element===Fe?window.pageYOffset||Fe.scrollTop:this.element.scrollTop},prepareProperty:function(t,e){return parseInt(e,10)},onStart:function(t){t in this.valuesEnd&&!e[t]&&(this.element=!("scroll"in this.valuesEnd)||this.element&&this.element!==window?this.element:Fe,Be.call(this),e[t]=function(t,e,n,r){t.scrollTop=Q(e,n,r)>>0})},onComplete:function(){Qe.call(this)}},Xe={component:"scrollProperty",property:"scroll",defaultValue:0,Interpolate:{numbers:Q},functions:ze,Util:{preventScroll:De,scrollIn:Be,scrollOut:Qe,getScrollTargets:He,toggleScrollEvents:qe}};function Ye(t){this.valuesEnd[t]&&!e[t]&&(e[t]=function(e,n,r,a){for(var i=[],o="textShadow"===t?3:4,s=3===o?n[3]:n[4],u=3===o?r[3]:r[4],c=!!(n[5]&&"none"!==n[5]||r[5]&&"none"!==r[5])&&" inset",l=0;l>0)/1e3+"px");e.style[t]=c?lt(s,u,a)+i.join(" ")+c:lt(s,u,a)+i.join(" ")})}var We={};["boxShadow","textShadow"].forEach((function(t){We[t]=Ye}));var $e=["boxShadow","textShadow"];function Ge(t,e){var n;3===t.length?n=[t[0],t[1],0,0,t[2],"none"]:4===t.length?n=/inset|none/.test(t[3])?[t[0],t[1],0,0,t[2],t[3]]:[t[0],t[1],t[2],0,t[3],"none"]:5===t.length?n=/inset|none/.test(t[4])?[t[0],t[1],t[2],0,t[3],t[4]]:[t[0],t[1],t[2],t[3],t[4],"none"]:6===t.length&&(n=t);for(var r=0;r<4;r+=1)n[r]=parseFloat(n[r]);return n[4]=ct(n[4]),n="boxShadow"===e?n:n.filter((function(t,e){return[0,1,2,4].includes(e)})),n}var Ze={};$e.forEach((function(t){Ze[t]=Ye}));var Ke={component:"shadowProperties",properties:$e,defaultValues:{boxShadow:"0px 0px 0px 0px rgb(0,0,0)",textShadow:"0px 0px 0px rgb(0,0,0)"},Interpolate:{numbers:Q,colors:lt},functions:{prepareStart:function(t){var e=M(this.element,t);return/^none$|^initial$|^inherit$|^inset$/.test(e)?f[t]:e},prepareProperty:function(t,e){var n=e;if("string"==typeof n){var r="none",a=n.match(/(\s?(?:#(?:[\da-f]{3}){1,2}|rgba?\(\d{1,3},\s*\d{1,3},\s*\d{1,3}\))\s?)/gi);r=/inset/.test(n)?"inset":r,n=Ge(n=(n=/inset/.test(n)?n.replace(/(\s+inset|inset+\s)/g,""):n).replace(a[0],"").split(" ").concat([a[0].replace(/\s/g,"")],[r]),t)}else n instanceof Array&&(n=Ge(n,t));return n},onStart:Ze},Util:{processShadowArray:Ge,trueColor:ct}},Je={};function tn(t){this.valuesEnd[t]&&!e[t]&&(e[t]=function(e,n,r,a){e.style[t]=W(n.v,r.v,r.u,a)})}["fontSize","lineHeight","letterSpacing","wordSpacing"].forEach((function(t){Je[t]=tn}));var en=["fontSize","lineHeight","letterSpacing","wordSpacing"],nn={};en.forEach((function(t){nn[t]=tn}));var rn={component:"textProperties",category:"textProperties",properties:en,defaultValues:{fontSize:0,lineHeight:0,letterSpacing:0,wordSpacing:0},Interpolate:{units:W},functions:{prepareStart:function(t){return M(this.element,t)||f[t]},prepareProperty:function(t,e){return z(e)},onStart:nn},Util:{trueDimension:z}},an=String("abcdefghijklmnopqrstuvwxyz").split(""),on=String("abcdefghijklmnopqrstuvwxyz").toUpperCase().split(""),sn=String("~!@#$%^&*()_+{}[];'<>,./?=-").split(""),un=String("0123456789").split(""),cn=an.concat(on,un),ln=cn.concat(sn),pn={alpha:an,upper:on,symbols:sn,numeric:un,alphanumeric:cn,all:ln},hn={text:function(t){if(!e[t]&&this.valuesEnd[t]){var n=this._textChars,r=pn[d.textChars];n in pn?r=pn[n]:n&&n.length&&(r=n),e[t]=function(t,e,n,a){var i="",o="",s=""===n?" ":n,u=e.substring(0),c=n.substring(0),l=r[Math.random()*r.length>>0];" "===e?(o=c.substring(Math.min(a*c.length,c.length)>>0,0),t.innerHTML=a<1?o+l:s):" "===n?(i=u.substring(0,Math.min((1-a)*u.length,u.length)>>0),t.innerHTML=a<1?i+l:s):(i=u.substring(u.length,Math.min(a*u.length,u.length)>>0),o=c.substring(0,Math.min(a*c.length,c.length)>>0),t.innerHTML=a<1?o+l+i:s)}}},number:function(t){t in this.valuesEnd&&!e[t]&&(e[t]=function(t,e,n,r){t.innerHTML=Q(e,n,r)>>0})}};function fn(t,e){var n,r;if("string"==typeof t)return(r=document.createElement("SPAN")).innerHTML=t,r.className=e,r;if(!t.children.length||t.children.length&&t.children[0].className!==e){var a=t.innerHTML;(n=document.createElement("SPAN")).className=e,n.innerHTML=a,t.appendChild(n),t.innerHTML=n.outerHTML}else t.children.length&&t.children[0].className===e&&(n=t.children[0]);return n}function dn(t,e){var n=[],r=t.children.length;if(r){for(var a,i=[],o=t.innerHTML,s=0,u=void 0,c=void 0,l=void 0;s>0)/1e3;return r}var gn="undefined"!=typeof DOMMatrix?DOMMatrix:null,mn="transformMatrix";var yn={BackgroundPosition:Y,BorderRadius:tt,BoxModel:ot,ClipProperty:ut,ColorProperties:gt,FilterEffects:xt,HTMLAttributes:_t,OpacityProperty:jt,SVGDraw:Dt,SVGCubicMorph:je,SVGTransform:Ve,ScrollProperty:Xe,ShadowProperties:Ke,TextProperties:rn,TextWriteProperties:{component:"textWriteProperties",category:"textWrite",properties:["text","number"],defaultValues:{text:" ",number:"0"},defaultOptions:{textChars:"alpha"},Interpolate:{numbers:Q},functions:{prepareStart:function(){return this.element.innerHTML},prepareProperty:function(t,e){return"number"===t?parseFloat(e):""===e?" ":e},onStart:hn},Util:{charSet:pn,createTextTweens:function(t,e,n){if(t.playing)return!1;var r=n||{};r.duration=1e3,"auto"===n.duration?r.duration="auto":Number.isFinite(1*n.duration)&&(r.duration=1*n.duration);var a=I.tween,i=function(t,e){var n=dn(t,"text-part"),r=dn(fn(e),"text-part");return t.innerHTML="",t.innerHTML+=n.map((function(t){return t.className+=" oldText",t.outerHTML})).join(""),t.innerHTML+=r.map((function(t){return t.className+=" newText",t.outerHTML.replace(t.innerHTML,"")})).join(""),[n,r]}(t,e),o=i[0],s=i[1],u=[].slice.call(t.getElementsByClassName("oldText")).reverse(),c=[].slice.call(t.getElementsByClassName("newText")),l=[],p=0;return(l=(l=l.concat(u.map((function(t,e){return r.duration="auto"===r.duration?75*o[e].innerHTML.length:r.duration,r.delay=p,r.onComplete=null,p+=r.duration,new a(t,{text:t.innerHTML},{text:""},r)})))).concat(c.map((function(n,i){return r.duration="auto"===r.duration?75*s[i].innerHTML.length:r.duration,r.delay=p,r.onComplete=i===s.length-1?function(){t.innerHTML=e,t.playing=!1}:null,p+=r.duration,new a(n,{text:""},{text:s[i].innerHTML},r)})))).start=function(){t.playing||(l.forEach((function(t){return t.start()})),t.playing=!0)},l}}},MatrixTransform:{component:mn,property:"transform",defaultValue:{perspective:400,translate3d:[0,0,0],translateX:0,translateY:0,translateZ:0,rotate3d:[0,0,0],rotateX:0,rotateY:0,rotateZ:0,skew:[0,0],skewX:0,skewY:0,scale3d:[1,1,1],scaleX:1,scaleY:1,scaleZ:1},functions:{prepareStart:function(t,e){var n={},r=this.element.transformMatrix;return r?Object.keys(r).forEach((function(t){n[t]=r[t]})):Object.keys(e).forEach((function(t){n[t]="perspective"===t?e[t]:f.transform[t]})),n},prepareProperty:function(t,e){if("object"==typeof e&&!e.length){var n,r={},a={},i={},o={},s={},u=[{translate3d:a},{rotate3d:i},{skew:s},{scale3d:o}];return Object.keys(e).forEach((function(t){if(/3d/.test(t)&&"object"==typeof e[t]&&e[t].length)n=e[t].map((function(e){return"scale3d"===t?parseFloat(e):parseInt(e,10)})),r[t]="scale3d"===t?[n[0]||1,n[1]||1,n[2]||1]:[n[0]||0,n[1]||0,n[2]||0];else if(/[XYZ]/.test(t)){var u={};/translate/.test(t)?u=a:/rotate/.test(t)?u=i:/scale/.test(t)?u=o:/skew/.test(t)&&(u=s),u[t.replace(/translate|rotate|scale|skew/,"").toLowerCase()]=/scale/.test(t)?parseFloat(e[t]):parseInt(e[t],10)}else"skew"===t?(n=e[t].map((function(t){return parseInt(t,10)||0})),r[t]=[n[0]||0,n[1]||0]):r[t]=parseInt(e[t],10)})),u.forEach((function(t){var e=Object.keys(t)[0],n=t[e];Object.keys(n).length&&!r[e]&&(r[e]="scale3d"===e?[n.x||1,n.y||1,n.z||1]:"skew"===e?[n.x||0,n.y||0]:[n.x||0,n.y||0,n.z||0])})),r}throw Error('KUTE.js - "'+e+'" is not valid/supported transform function')},onStart:{transform:function(t){gn&&this.valuesEnd[t]&&!e[t]&&(e[t]=function(e,n,r,a){var i=new gn,o={};Object.keys(r).forEach((function(t){o[t]="perspective"===t?Q(n[t],r[t],a):vn(n[t],r[t],a)})),o.perspective&&(i.m34=-1/o.perspective),i=o.translate3d?i.translate(o.translate3d[0],o.translate3d[1],o.translate3d[2]):i,i=o.rotate3d?i.rotate(o.rotate3d[0],o.rotate3d[1],o.rotate3d[2]):i,o.skew&&(i=o.skew[0]?i.skewX(o.skew[0]):i,i=o.skew[1]?i.skewY(o.skew[1]):i),i=o.scale3d?i.scale(o.scale3d[0],o.scale3d[1],o.scale3d[2]):i,e.style[t]=i.toString()})},CSS3Matrix:function(t){gn&&this.valuesEnd.transform&&(e[t]||(e[t]=gn))}},onComplete:function(t){this.valuesEnd[t]&&(this.element.transformMatrix=Object.assign({},this.valuesEnd[t]))},crossCheck:function(t){this.valuesEnd[t]&&this.valuesEnd[t].perspective&&!this.valuesStart[t].perspective&&(this.valuesStart[t].perspective=this.valuesEnd[t].perspective)}},Interpolate:{perspective:Q,translate3d:vn,rotate3d:vn,skew:vn,scale3d:vn}}};Object.keys(yn).forEach((function(t){var e=yn[t];yn[t]=new B(e)}));return{Animation:B,Components:yn,Tween:R,fromTo:function(t,e,n,r){var a=r||{};return new H(P(t),e,n,a)},to:function(t,e,n){var r=n||{};return r.resetStart=e,new D(P(t),e,e,r)},TweenCollection:U,ProgressBar:F,allFromTo:function(t,e,n,r){var a=r||{};return new U(P(t,!0),e,n,a)},allTo:function(t,e,n){var r=n||{};return r.resetStart=e,new U(P(t,!0),e,e,r)},Objects:w,Util:E,Easing:A,CubicBezier:t,Render:p,Interpolate:a,Process:j,Internals:T,Selector:P,Version:"2.2.2"}})); diff --git a/src/kute.min.js b/src/kute.min.js new file mode 100644 index 0000000..2887b84 --- /dev/null +++ b/src/kute.min.js @@ -0,0 +1,2 @@ +// KUTE.js Standard v2.2.2 | thednp © 2021 | MIT-License +!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):(t="undefined"!=typeof globalThis?globalThis:t||self).KUTE=e()}(this,(function(){"use strict";var t=function(t,e,n,r,a){var i=this;this.cx=3*t,this.bx=3*(n-t)-this.cx,this.ax=1-this.cx-this.bx,this.cy=3*e,this.by=3*(r-e)-this.cy,this.ay=1-this.cy-this.by;var o=function(t){return i.sampleCurveY(i.solveCurveX(t))};return Object.defineProperty(o,"name",{writable:!0}),o.name=a||"cubic-bezier("+[t,e,n,r]+")",o};t.prototype.sampleCurveX=function(t){return((this.ax*t+this.bx)*t+this.cx)*t},t.prototype.sampleCurveY=function(t){return((this.ay*t+this.by)*t+this.cy)*t},t.prototype.sampleCurveDerivativeX=function(t){return(3*this.ax*t+2*this.bx)*t+this.cx},t.prototype.solveCurveX=function(t){var e,n,r,a,i,o,s=1e-5;for(r=t,o=0;o<32;o+=1){if(a=this.sampleCurveX(r)-t,Math.abs(a)(n=1))return n;for(;ea?e=r:n=r,r=.5*(n-e)+e}return r};Object.assign(t,{Version:"1.0.18"});var e={},n=[],r="undefined"!=typeof global?global:"undefined"!=typeof window?window.self:{},a={},i={},o="undefined"==typeof self&&"undefined"!=typeof process&&process.hrtime?function(){var t=process.hrtime();return 1e3*t[0]+t[1]/1e6}:"undefined"!=typeof self&&void 0!==self.performance&&void 0!==self.performance.now?self.performance.now.bind(self.performance):"undefined"!=typeof Date&&Date.now?Date.now:function(){return(new Date).getTime()},s={};s.now=o;var u=0,c=function(t){for(var e=0;e1?1:n;var i=this._easing(n);return Object.keys(this.valuesEnd).forEach((function(t){e[t](r.element,r.valuesStart[t],r.valuesEnd[t],i)})),this._onUpdate&&this._onUpdate.call(this),1!==n||(this._onComplete&&this._onComplete.call(this),this.playing=!1,this.close(),void 0!==this._chain&&this._chain.length&&this._chain.map((function(t){return t.start()})),!1)},j.tween=N;var q=function(t){function n(){for(var e=this,n=[],r=arguments.length;r--;)n[r]=arguments[r];t.apply(this,n),this.valuesStart={},this.valuesEnd={};var a=n.slice(1),i=a[0],o=a[1],s=a[2];return T.call(this,o,"end"),this._resetStart?this.valuesStart=i:T.call(this,i,"start"),this._resetStart||Object.keys(g).forEach((function(t){Object.keys(g[t]).forEach((function(n){g[t][n].call(e,n)}))})),this.paused=!1,this._pauseTime=null,this._repeat=s.repeat||d.repeat,this._repeatDelay=s.repeatDelay||d.repeatDelay,this._repeatOption=this._repeat,this.valuesRepeat={},this._yoyo=s.yoyo||d.yoyo,this._reversed=!1,this}return t&&(n.__proto__=t),n.prototype=Object.create(t&&t.prototype),n.prototype.constructor=n,n.prototype.start=function(e){var n=this;return this._resetStart&&(this.valuesStart=this._resetStart,S.call(this),Object.keys(g).forEach((function(t){Object.keys(g[t]).forEach((function(e){g[t][e].call(n,e)}))}))),this.paused=!1,this._yoyo&&Object.keys(this.valuesEnd).forEach((function(t){n.valuesRepeat[t]=n.valuesStart[t]})),t.prototype.start.call(this,e),this},n.prototype.stop=function(){return t.prototype.stop.call(this),!this.paused&&this.playing&&(this.paused=!1,this.stopChainedTweens()),this},n.prototype.close=function(){return t.prototype.close.call(this),this._repeatOption>0&&(this._repeat=this._repeatOption),this._yoyo&&!0===this._reversed&&(this.reverse(),this._reversed=!1),this},n.prototype.resume=function(){return this.paused&&this.playing&&(this.paused=!1,void 0!==this._onResume&&this._onResume.call(this),L.call(this),this._startTime+=e.Time()-this._pauseTime,E(this),u||c()),this},n.prototype.pause=function(){return!this.paused&&this.playing&&(M(this),this.paused=!0,this._pauseTime=e.Time(),void 0!==this._onPause&&this._onPause.call(this)),this},n.prototype.reverse=function(){var t=this;Object.keys(this.valuesEnd).forEach((function(e){var n=t.valuesRepeat[e];t.valuesRepeat[e]=t.valuesEnd[e],t.valuesEnd[e]=n,t.valuesStart[e]=t.valuesRepeat[e]}))},n.prototype.update=function(t){var n,r=this,a=void 0!==t?t:e.Time();if(a1?1:n;var i=this._easing(n);return Object.keys(this.valuesEnd).forEach((function(t){e[t](r.element,r.valuesStart[t],r.valuesEnd[t],i)})),this._onUpdate&&this._onUpdate.call(this),1!==n||(this._repeat>0?(Number.isFinite(this._repeat)&&(this._repeat-=1),this._startTime=a,Number.isFinite(this._repeat)&&this._yoyo&&!this._reversed&&(this._startTime+=this._repeatDelay),this._yoyo&&(this._reversed=!this._reversed,this.reverse()),!0):(this._onComplete&&this._onComplete.call(this),this.playing=!1,this.close(),void 0!==this._chain&&this._chain.length&&this._chain.forEach((function(t){return t.start()})),!1))},n}(N);j.tween=q;var V=function(t,e,n,r){var a=this,i=j.tween;this.tweens=[];var o=r||{};o.delay=o.delay||d.delay;var s=[];return Array.from(t).forEach((function(t,r){if(s[r]=o||{},s[r].delay=r>0?o.delay+(o.offset||d.offset):o.delay,!(t instanceof Element))throw Error("KUTE - "+t+" is not instanceof Element");a.tweens.push(new i(t,e,n,s[r]))})),this.length=this.tweens.length,this};V.prototype.start=function(t){var n=void 0===t?e.Time():t;return this.tweens.map((function(t){return t.start(n)})),this},V.prototype.stop=function(){return this.tweens.map((function(t){return t.stop()})),this},V.prototype.pause=function(){return this.tweens.map((function(t){return t.pause()})),this},V.prototype.resume=function(){return this.tweens.map((function(t){return t.resume()})),this},V.prototype.chain=function(t){var e=this.tweens[this.length-1];if(t instanceof V)e.chain(t.tweens);else{if(!(t instanceof j.tween))throw new TypeError("KUTE.js - invalid chain value");e.chain(t)}return this},V.prototype.playing=function(){return this.tweens.some((function(t){return t.playing}))},V.prototype.removeTweens=function(){this.tweens=[]},V.prototype.getMaxDuration=function(){var t=[];return this.tweens.forEach((function(e){t.push(e._duration+e._delay+e._repeat*e._repeatDelay)})),Math.max(t)};var H=j.tween;var Q=j.tween;var F=function(t){try{if(t.component in f)throw Error("KUTE - "+t.component+" already registered");if(t.property in h)throw Error("KUTE - "+t.property+" already registered")}catch(t){throw Error(t)}var e=this,n=t.component,r={prepareProperty:y,prepareStart:v,onStart:i,onComplete:m,crossCheck:g},o=t.category,s=t.property,u=t.properties&&t.properties.length||t.subProperties&&t.subProperties.length;return f[n]=t.properties||t.subProperties||t.property,"defaultValue"in t?(h[s]=t.defaultValue,e.supports=s+" property"):t.defaultValues&&(Object.keys(t.defaultValues).forEach((function(e){h[e]=t.defaultValues[e]})),e.supports=(u||s)+" "+(s||o)+" properties"),t.defaultOptions&&Object.assign(d,t.defaultOptions),t.functions&&Object.keys(r).forEach((function(e){e in t.functions&&("function"==typeof t.functions[e]?(r[e][n]||(r[e][n]={}),r[e][n][o||s]||(r[e][n][o||s]=t.functions[e])):Object.keys(t.functions[e]).forEach((function(a){r[e][n]||(r[e][n]={}),r[e][n][a]||(r[e][n][a]=t.functions[e][a])})))})),t.Interpolate&&(Object.keys(t.Interpolate).forEach((function(e){var n=t.Interpolate[e];"function"!=typeof n||a[e]?Object.keys(n).forEach((function(t){"function"!=typeof n[t]||a[e]||(a[e]=n[t])})):a[e]=n})),b[n]=t.Interpolate),t.Util&&Object.keys(t.Util).forEach((function(e){w[e]||(w[e]=t.Util[e])})),e},U=function(t,e){for(var n,r=parseInt(t,10)||0,a=["px","%","deg","rad","em","rem","vh","vw"],i=0;i.99||a<.01?(10*D(n,r,a)>>0)/10:D(n,r,a)>>0)+"px"})}var Z={};["top","left","width","height"].forEach((function(t){Z[t]=X}));var B=["top","left","width","height"],R={};B.forEach((function(t){R[t]=X}));var Y={component:"essentialBoxModel",category:"boxModel",properties:B,defaultValues:{top:0,left:0,width:0,height:0},Interpolate:{numbers:D},functions:{prepareStart:function(t){return O(this.element,t)||h[t]},prepareProperty:function(t,e){var n=U(e),r="height"===t?"offsetHeight":"offsetWidth";return"%"===n.u?n.v*this.element[r]/100:n.v},onStart:R},Util:{trueDimension:U}},z=function(t){var e;if(/rgb|rgba/.test(t)){var n=t.replace(/\s|\)/,"").split("(")[1].split(","),r=n[3]?n[3]:null;r||(e={r:parseInt(n[0],10),g:parseInt(n[1],10),b:parseInt(n[2],10)}),e={r:parseInt(n[0],10),g:parseInt(n[1],10),b:parseInt(n[2],10),a:parseFloat(r)}}if(/^#/.test(t)){var a=function(t){var e=t.replace(/^#?([a-f\d])([a-f\d])([a-f\d])$/i,(function(t,e,n,r){return e+e+n+n+r+r})),n=/^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(e);return n?{r:parseInt(n[1],16),g:parseInt(n[2],16),b:parseInt(n[3],16)}:null}(t);e={r:a.r,g:a.g,b:a.b}}if(/transparent|none|initial|inherit/.test(t)&&(e={r:0,g:0,b:0,a:0}),!/^#|^rgb/.test(t)){var i=document.getElementsByTagName("head")[0];i.style.color=t;var o=getComputedStyle(i,null).color;o=/rgb/.test(o)?o.replace(/[^\d,]/g,"").split(","):[0,0,0],i.style.color="",e={r:parseInt(o[0],10),g:parseInt(o[1],10),b:parseInt(o[2],10)}}return e};function K(t,e,n){var r={},a=",";return Object.keys(e).forEach((function(a){"a"!==a?r[a]=D(t[a],e[a],n)>>0||0:t[a]&&e[a]&&(r[a]=(100*D(t[a],e[a],n)>>0)/100)})),r.a?"rgba("+r.r+a+r.g+a+r.b+a+r.a+")":"rgb("+r.r+a+r.g+a+r.b+")"}function $(t){this.valuesEnd[t]&&!e[t]&&(e[t]=function(e,n,r,a){e.style[t]=K(n,r,a)})}var W={};["color","backgroundColor","outlineColor","borderColor","borderTopColor","borderRightColor","borderBottomColor","borderLeftColor"].forEach((function(t){W[t]=$}));var G=["color","backgroundColor","outlineColor","borderColor","borderTopColor","borderRightColor","borderBottomColor","borderLeftColor"],J={};G.forEach((function(t){J[t]="#000"}));var tt={};G.forEach((function(t){tt[t]=$}));var et={component:"colorProperties",category:"colors",properties:G,defaultValues:J,Interpolate:{numbers:D,colors:K},functions:{prepareStart:function(t){return O(this.element,t)||h[t]},prepareProperty:function(t,e){return z(e)},onStart:tt},Util:{trueColor:z}},nt={},rt="htmlAttributes",at=["fill","stroke","stop-color"];function it(t){return t.replace(/[A-Z]/g,"-$&").toLowerCase()}var ot={prepareStart:function(t,e){var n=this,r={};return Object.keys(e).forEach((function(t){var e=it(t).replace(/_+[a-z]+/,""),a=n.element.getAttribute(e);r[e]=at.includes(e)?a||"rgba(0,0,0,0)":a||(/opacity/i.test(t)?1:0)})),r},prepareProperty:function(t,e){var n=this,r={};return Object.keys(e).forEach((function(a){var o=it(a),s=/(%|[a-z]+)$/,u=n.element.getAttribute(o.replace(/_+[a-z]+/,""));if(at.includes(o))i.htmlAttributes[o]=function(e){n.valuesEnd[t]&&n.valuesEnd[t][e]&&!(e in nt)&&(nt[e]=function(t,e,n,r,a){t.setAttribute(e,K(n,r,a))})},r[o]=z(e[a])||h.htmlAttributes[a];else if(null!==u&&s.test(u)){var c=U(u).u||U(e[a]).u,l=/%/.test(c)?"_percent":"_"+c;i.htmlAttributes[o+l]=function(e){n.valuesEnd[t]&&n.valuesEnd[t][e]&&!(e in nt)&&(nt[e]=function(t,e,n,r,a){var i=e.replace(l,"");t.setAttribute(i,(1e3*D(n.v,r.v,a)>>0)/1e3+r.u)})},r[o+l]=U(e[a])}else s.test(e[a])&&null!==u&&(null===u||s.test(u))||(i.htmlAttributes[o]=function(e){n.valuesEnd[t]&&n.valuesEnd[t][e]&&!(e in nt)&&(nt[e]=function(t,e,n,r,a){t.setAttribute(e,(1e3*D(n,r,a)>>0)/1e3)})},r[o]=parseFloat(e[a]))})),r},onStart:{attr:function(t){!e[t]&&this.valuesEnd[t]&&(e[t]=function(t,n,r,a){Object.keys(r).forEach((function(i){e.attributes[i](t,i,n[i],r[i],a)}))})},attributes:function(t){!e[t]&&this.valuesEnd.attr&&(e[t]=nt)}}},st={component:rt,property:"attr",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},Interpolate:{numbers:D,colors:K},functions:ot,Util:{replaceUppercase:it,trueColor:z,trueDimension:U}};var ut={prepareStart:function(t){return O(this.element,t)},prepareProperty:function(t,e){return parseFloat(e)},onStart:function(t){t in this.valuesEnd&&!e[t]&&(e[t]=function(e,n,r,a){e.style[t]=(1e3*D(n,r,a)>>0)/1e3})}},ct={component:"opacityProperty",property:"opacity",defaultValue:1,Interpolate:{numbers:D},functions:ut},lt=String("abcdefghijklmnopqrstuvwxyz").split(""),pt=String("abcdefghijklmnopqrstuvwxyz").toUpperCase().split(""),ft=String("~!@#$%^&*()_+{}[];'<>,./?=-").split(""),ht=String("0123456789").split(""),dt=lt.concat(pt,ht),yt=dt.concat(ft),vt={alpha:lt,upper:pt,symbols:ft,numeric:ht,alphanumeric:dt,all:yt},gt={text:function(t){if(!e[t]&&this.valuesEnd[t]){var n=this._textChars,r=vt[d.textChars];n in vt?r=vt[n]:n&&n.length&&(r=n),e[t]=function(t,e,n,a){var i="",o="",s=""===n?" ":n,u=e.substring(0),c=n.substring(0),l=r[Math.random()*r.length>>0];" "===e?(o=c.substring(Math.min(a*c.length,c.length)>>0,0),t.innerHTML=a<1?o+l:s):" "===n?(i=u.substring(0,Math.min((1-a)*u.length,u.length)>>0),t.innerHTML=a<1?i+l:s):(i=u.substring(u.length,Math.min(a*u.length,u.length)>>0),o=c.substring(0,Math.min(a*c.length,c.length)>>0),t.innerHTML=a<1?o+l+i:s)}}},number:function(t){t in this.valuesEnd&&!e[t]&&(e[t]=function(t,e,n,r){t.innerHTML=D(e,n,r)>>0})}};function mt(t,e){var n,r;if("string"==typeof t)return(r=document.createElement("SPAN")).innerHTML=t,r.className=e,r;if(!t.children.length||t.children.length&&t.children[0].className!==e){var a=t.innerHTML;(n=document.createElement("SPAN")).className=e,n.innerHTML=a,t.appendChild(n),t.innerHTML=n.outerHTML}else t.children.length&&t.children[0].className===e&&(n=t.children[0]);return n}function bt(t,e){var n=[],r=t.children.length;if(r){for(var a,i=[],o=t.innerHTML,s=0,u=void 0,c=void 0,l=void 0;s>0)/1e3+n+")"}function wt(t,e,n,r){for(var a=[],i=0;i<3;i+=1)a[i]=(t[i]||e[i]?(1e3*(t[i]+(e[i]-t[i])*r)>>0)/1e3:0)+n;return"translate3d("+a.join(",")+")"}function Et(t,e,n,r){var a="";return a+=t[0]||e[0]?"rotateX("+(1e3*(t[0]+(e[0]-t[0])*r)>>0)/1e3+n+")":"",a+=t[1]||e[1]?"rotateY("+(1e3*(t[1]+(e[1]-t[1])*r)>>0)/1e3+n+")":"",a+=t[2]||e[2]?"rotateZ("+(1e3*(t[2]+(e[2]-t[2])*r)>>0)/1e3+n+")":""}function Mt(t,e,n){return"scale("+(1e3*(t+(e-t)*n)>>0)/1e3+")"}function _t(t,e,n,r){var a=[];return a[0]=(t[0]===e[0]?e[0]:(1e3*(t[0]+(e[0]-t[0])*r)>>0)/1e3)+n,a[1]=t[1]||e[1]?(t[1]===e[1]?e[1]:(1e3*(t[1]+(e[1]-t[1])*r)>>0)/1e3)+n:"0","skew("+a.join(",")+")"}function kt(t,e){return parseFloat(t)/100*e}function Ct(t){return 2*t.getAttribute("width")+2*t.getAttribute("height")}function Ot(t){var e=t.getAttribute("points").split(" "),n=0;if(e.length>1){var r=function(t){var e=t.split(",");return 2!==e.length||Number.isNaN(1*e[0])||Number.isNaN(1*e[1])?0:[parseFloat(e[0]),parseFloat(e[1])]},a=function(t,e){return void 0!==t&&void 0!==e?Math.sqrt(Math.pow(e[0]-t[0],2)+Math.pow(e[1]-t[1],2)):0};if(e.length>2)for(var i=0;i>0)/100,i=0-(100*D(e.s,n.s,r)>>0)/100,o=(100*D(e.e,n.e,r)>>0)/100+i;t.style.strokeDashoffset=i+"px",t.style.strokeDasharray=(100*(o<1?0:o)>>0)/100+"px, "+a+"px"})}};function Lt(t,e,n){if(t[n].length>7){t[n].shift();for(var r=t[n],a=n;r.length;)e[n]="A",t.splice(a+=1,0,["C"].concat(r.splice(0,6)));t.splice(n,1)}}var Nt={a:7,c:6,h:1,l:2,m:2,r:4,q:4,s:4,t:2,v:1,z:0};function qt(t){var e=t.pathValue[t.segmentStart],n=e.toLowerCase(),r=t.data;for("m"===n&&r.length>2&&(t.segments.push([e,r[0],r[1]]),r=r.slice(2),n="l",e="m"===e?"l":"L");r.length>=Nt[n]&&(t.segments.push([e].concat(r.splice(0,Nt[n]))),Nt[n]););}var Vt="Invalid path value";function Ht(t){var e=t.index,n=t.pathValue.charCodeAt(e);return 48===n?(t.param=0,void(t.index+=1)):49===n?(t.param=1,void(t.index+=1)):void(t.err=Vt+': invalid Arc flag "'+n+'", expecting 0 or 1 at index '+e)}function Qt(t){return t>=48&&t<=57}function Ft(t){var e,n=t.max,r=t.pathValue,a=t.index,i=a,o=!1,s=!1,u=!1,c=!1;if(i>=n)t.err=Vt+" at "+i+": missing param "+r[i];else if(43!==(e=r.charCodeAt(i))&&45!==e||(e=(i+=1)=5760&&[5760,6158,8192,8193,8194,8195,8196,8197,8198,8199,8200,8201,8202,8239,8287,12288,65279].indexOf(e)>=0);)t.index+=1}function Dt(t){return t>=48&&t<=57||43===t||45===t||46===t}function Xt(t){var e=t.max,n=t.pathValue,r=t.index,a=n.charCodeAt(r),i=Nt[n[r].toLowerCase()];if(t.segmentStart=r,function(t){switch(32|t){case 109:case 122:case 108:case 104:case 118:case 99:case 115:case 113:case 116:case 97:return!0;default:return!1}}(a))if(t.index+=1,Ut(t),t.data=[],i){for(;;){for(var o=i;o>0;o-=1){if(97!=(32|a)||3!==o&&4!==o?Ft(t):Ht(t),t.err.length)return;t.data.push(t.param),Ut(t),t.index=t.max)break;if(!Dt(n.charCodeAt(t.index)))break}qt(t)}else qt(t);else t.err=Vt+": "+n[r]+" not a path command"}function Zt(t){return t.map((function(t){return Array.isArray(t)?[].concat(t):t}))}function Bt(t){this.segments=[],this.pathValue=t,this.max=t.length,this.index=0,this.param=0,this.segmentStart=0,this.data=[],this.err=""}function Rt(t){return Array.isArray(t)&&t.every((function(t){var e=t[0].toLowerCase();return Nt[e]===t.length-1&&"achlmqstvz".includes(e)}))}function Yt(t){if(Rt(t))return Zt(t);var e=new Bt(t);for(Ut(e);e.index1&&(m*=O=Math.sqrt(O),b*=O);var T=m*m,S=b*b,I=(i===o?-1:1)*Math.sqrt(Math.abs((T*S-T*C*C-S*k*k)/(T*C*C+S*k*k)));d=I*m*C/b+(v+x)/2,y=I*-b*k/m+(g+w)/2,f=(Math.asin((g-y)/b)*Math.pow(10,9)>>0)/Math.pow(10,9),h=(Math.asin((w-y)/b)*Math.pow(10,9)>>0)/Math.pow(10,9),f=vh&&(f-=2*Math.PI),!o&&h>f&&(h-=2*Math.PI)}var j=h-f;if(Math.abs(j)>E){var A=h,P=x,L=w;h=f+E*(o&&h>f?1:-1),_=ee(x=d+m*Math.cos(h),w=y+b*Math.sin(h),m,b,a,0,o,P,L,[h,A,d,y])}j=h-f;var N=Math.cos(f),q=Math.sin(f),V=Math.cos(h),H=Math.sin(h),Q=Math.tan(j/4),F=4/3*m*Q,U=4/3*b*Q,D=[v,g],X=[v+F*q,g-U*N],Z=[x+F*H,w-U*V],B=[x,w];if(X[0]=2*D[0]-X[0],X[1]=2*D[1]-X[1],c)return X.concat(Z,B,_);for(var R=[],Y=0,z=(_=X.concat(Z,B,_)).length;Yi+.001)return{x:n,y:r};var o=re([t,e],[n,r],a/i);return{x:o[0],y:o[1]}}return i}function oe(t,e,n,r){var a=.5,i=[t,e],o=[n,r],s=re(i,o,a),u=re(o,s,a),c=re(s,u,a),l=re(u,c,a),p=re(c,l,a),f=i.concat(s,c,p,[a]),h=ie.apply(void 0,f),d=p.concat(l,u,o,[0]),y=ie.apply(void 0,d);return[h.x,h.y,y.x,y.y,n,r]}function se(t,e){var n,r=t[0],a=t.slice(1).map((function(t){return+t})),i=a[0],o=a[1],s=e.x1,u=e.y1,c=e.x,l=e.y;switch("TQ".includes(r)||(e.qx=null,e.qy=null),r){case"M":return e.x=i,e.y=o,t;case"A":return n=[s,u].concat(a),["C"].concat(ee.apply(void 0,n));case"Q":return e.qx=i,e.qy=o,n=[s,u].concat(a),["C"].concat(ne.apply(void 0,n));case"L":return["C"].concat(oe(s,u,i,o));case"Z":return["C"].concat(oe(s,u,c,l))}return t}var ue=4;function ce(t,e){var n=ue;if(!1===e||!1===n)return Zt(t);var r=(n=e>=1?e:n)>=1?Math.pow(10,n):1;return t.map((function(t){var e=t.slice(1).map(Number).map((function(t){return t%1==0?t:Math.round(t*r)/r}));return[t[0]].concat(e)}))}function le(t,e){return ce(t,e).map((function(t){return t[0]+t.slice(1).join(" ")})).join("")}function pe(t,e,n,r,a,i,o,s,u){var c=1-u;return{x:Math.pow(c,3)*t+3*Math.pow(c,2)*u*n+3*c*Math.pow(u,2)*a+Math.pow(u,3)*o,y:Math.pow(c,3)*e+3*Math.pow(c,2)*u*r+3*c*Math.pow(u,2)*i+Math.pow(u,3)*s}}function fe(t,e,n,r,a,i,o,s,u){var c,l=t,p=e,f=0,h=[t,e,f],d=[t,e];if("number"==typeof u&&u<.001)return{x:l,y:p};for(var y=0;y<=100;y+=1){if(f+=ae(d,[l=(c=pe(t,e,n,r,a,i,o,s,y/100)).x,p=c.y]),d=[l,p],"number"==typeof u&&f>=u){var v=(f-u)/(f-h[2]);return{x:d[0]*(1-v)+h[0]*v,y:d[1]*(1-v)+h[1]*v}}h=[l,p,f]}return"number"==typeof u&&u>=f?{x:o,y:s}:f}function he(t,e,n,r,a,i,o,s,u,c){var l,p=[t,e],f=p[0],h=p[1],d=ee(t,e,n,r,a,i,o,s,u),y=0,v=[],g=[],m=0;if("number"==typeof c&&c<.001)return{x:f,y:h};for(var b=0,x=d.length;b=c)return fe.apply(void 0,g.concat([c-y]));y+=m,f=(l=v.slice(-2))[0],h=l[1]}return"number"==typeof c&&c>=y?{x:s,y:u}:y}function de(t,e,n,r,a,i,o){var s=1-o;return{x:Math.pow(s,2)*t+2*s*o*n+Math.pow(o,2)*a,y:Math.pow(s,2)*e+2*s*o*r+Math.pow(o,2)*i}}function ye(t,e,n,r,a,i,o){var s,u=t,c=e,l=0,p=[t,e,l],f=[t,e];if("number"==typeof o&&o<.001)return{x:u,y:c};for(var h=0;h<=100;h+=1){if(l+=ae(f,[u=(s=de(t,e,n,r,a,i,h/100)).x,c=s.y]),f=[u,c],"number"==typeof o&&l>=o){var d=(l-o)/(l-p[2]);return{x:f[0]*(1-d)+p[0]*d,y:f[1]*(1-d)+p[1]*d}}p=[u,c,l]}return"number"==typeof o&&o>=l?{x:a,y:i}:l}function ve(t,e){for(var n,r,a,i=0,o=!0,s=[],u="M",c=0,l=0,p=0,f=0,h=0,d=Jt(Gt(t)),y=0,v=d.length;y=e)return ie.apply(void 0,s.concat([e-i]));i+=c}else if("A"===u){if(c=he.apply(void 0,s),e&&i+c>=e)return he.apply(void 0,s.concat([e-i]));i+=c}else if("C"===u){if(c=fe.apply(void 0,s),e&&i+c>=e)return fe.apply(void 0,s.concat([e-i]));i+=c}else if("Q"===u){if(c=ye.apply(void 0,s),e&&i+c>=e)return ye.apply(void 0,s.concat([e-i]));i+=c}else if("Z"===u){if(s=[l,p,f,h],c=ie.apply(void 0,s),e&&i+c>=e)return ie.apply(void 0,s.concat([e-i]));i+=c}l=(r="Z"!==u?a.slice(-2):[f,h])[0],p=r[1]}return e&&e>=i?{x:l,y:p}:i}function ge(t){return ve(t)}function me(t,e){return ve(t,e)}function be(t){for(var e,n=t.length,r=-1,a=t[n-1],i=0;++r>0)/1e3)}return a}function we(t,e){var n,r,a=Gt((n=le(t),le(Kt(n),0).replace(/(m|M)/g,"|$1").split("|").map((function(t){return t.trim()})).filter((function(t){return t})))[0]),i=ge(a),o=[],s=3;e&&!Number.isNaN(e)&&+e>0&&(s=Math.max(s,Math.ceil(i/e)));for(var u=0;u0&&o.reverse(),{polygon:o,skipBisect:!0}}function Ee(t,e){var n=Gt(t);return function(t){var e=[],n=t.length,r=[],a="";if(!t.length||"M"!==t[0][0])return!1;for(var i=0;ie;)r=re(n,r,.5),t.splice(a+1,0,r)}function Ce(t){return Array.isArray(t)&&t.every((function(t){return Array.isArray(t)&&2===t.length&&!Number.isNaN(t[0])&&!Number.isNaN(t[1])}))}function Oe(t,e){var n,r,a;if("string"==typeof t)a=(n=Ee(t,e)).polygon,r=n.skipBisect;else if(!Array.isArray(t))throw Error(Vt+": "+t);var i=[].concat(a);if(!Ce(i))throw Error(Vt+": "+i);return i.length>1&&ae(i[0],i[i.length-1])<1e-9&&i.pop(),!r&&e&&!Number.isNaN(e)&&+e>0&&ke(i,e),i}function Te(t,e,n){var r=n||d.morphPrecision,a=Oe(t,r),i=Oe(e,r),o=a.length-i.length;return _e(a,o<0?-1*o:0),_e(i,o>0?o:0),Me(a,i),[ce(a),ce(i)]}var Se={prepareStart:function(){return this.element.getAttribute("d")},prepareProperty:function(t,e){var n={},r=new RegExp("\\n","ig"),a=null;return e instanceof SVGPathElement?a=e:/^\.|^#/.test(e)&&(a=P(e)),"object"==typeof e&&e.polygon?e:(a&&["path","glyph"].includes(a.tagName)?n.original=a.getAttribute("d").replace(r,""):a||"string"!=typeof e||(n.original=e.replace(r,"")),n)},onStart:function(t){!e[t]&&this.valuesEnd[t]&&(e[t]=function(t,e,n,r){var a=e.polygon,i=n.polygon,o=i.length;t.setAttribute("d",1===r?n.original:"M"+xe(a,i,o,r).join("L")+"Z")})},crossCheck:function(t){if(this.valuesEnd[t]){var e=this.valuesStart[t].polygon,n=this.valuesEnd[t].polygon;if(!e||!n||e&&n&&e.length!==n.length){var r=Te(this.valuesStart[t].original,this.valuesEnd[t].original,this._morphPrecision?parseInt(this._morphPrecision,10):d.morphPrecision),a=r[0],i=r[1];this.valuesStart[t].polygon=a,this.valuesEnd[t].polygon=i}}}},Ie={EssentialBoxModel:Y,ColorsProperties:et,HTMLAttributes:st,OpacityProperty:ct,TextWriteProp:{component:"textWriteProperties",category:"textWrite",properties:["text","number"],defaultValues:{text:" ",number:"0"},defaultOptions:{textChars:"alpha"},Interpolate:{numbers:D},functions:{prepareStart:function(){return this.element.innerHTML},prepareProperty:function(t,e){return"number"===t?parseFloat(e):""===e?" ":e},onStart:gt},Util:{charSet:vt,createTextTweens:function(t,e,n){if(t.playing)return!1;var r=n||{};r.duration=1e3,"auto"===n.duration?r.duration="auto":Number.isFinite(1*n.duration)&&(r.duration=1*n.duration);var a=j.tween,i=function(t,e){var n=bt(t,"text-part"),r=bt(mt(e),"text-part");return t.innerHTML="",t.innerHTML+=n.map((function(t){return t.className+=" oldText",t.outerHTML})).join(""),t.innerHTML+=r.map((function(t){return t.className+=" newText",t.outerHTML.replace(t.innerHTML,"")})).join(""),[n,r]}(t,e),o=i[0],s=i[1],u=[].slice.call(t.getElementsByClassName("oldText")).reverse(),c=[].slice.call(t.getElementsByClassName("newText")),l=[],p=0;return(l=(l=l.concat(u.map((function(t,e){return r.duration="auto"===r.duration?75*o[e].innerHTML.length:r.duration,r.delay=p,r.onComplete=null,p+=r.duration,new a(t,{text:t.innerHTML},{text:""},r)})))).concat(c.map((function(n,i){return r.duration="auto"===r.duration?75*s[i].innerHTML.length:r.duration,r.delay=p,r.onComplete=i===s.length-1?function(){t.innerHTML=e,t.playing=!1}:null,p+=r.duration,new a(n,{text:""},{text:s[i].innerHTML},r)})))).start=function(){t.playing||(l.forEach((function(t){return t.start()})),t.playing=!0)},l}}},TransformFunctions:{component:"transformFunctions",property:"transform",subProperties:["perspective","translate3d","translateX","translateY","translateZ","translate","rotate3d","rotateX","rotateY","rotateZ","rotate","skewX","skewY","skew","scale"],defaultValues:{perspective:400,translate3d:[0,0,0],translateX:0,translateY:0,translateZ:0,translate:[0,0],rotate3d:[0,0,0],rotateX:0,rotateY:0,rotateZ:0,rotate:0,skewX:0,skewY:0,skew:[0,0],scale:1},functions:{prepareStart:function(t){var e=C(this.element);return e[t]?e[t]:h[t]},prepareProperty:function(t,e){var n=["X","Y","Z"],r={},a=[],i=[],o=[],s=["translate3d","translate","rotate3d","skew"];return Object.keys(e).forEach((function(t){var u="object"==typeof e[t]&&e[t].length?e[t].map((function(t){return parseInt(t,10)})):parseInt(e[t],10);if(s.includes(t)){var c="translate"===t||"rotate"===t?t+"3d":t;r[c]="skew"===t?u.length?[u[0]||0,u[1]||0]:[u||0,0]:"translate"===t?u.length?[u[0]||0,u[1]||0,u[2]||0]:[u||0,0,0]:[u[0]||0,u[1]||0,u[2]||0]}else if(/[XYZ]/.test(t)){var l=t.replace(/[XYZ]/,""),p="skew"===l?l:l+"3d",f="skew"===l?2:3,h=[];"translate"===l?h=a:"rotate"===l?h=i:"skew"===l&&(h=o);for(var d=0;d>0)/1e3)+n,a[1]=t[1]||e[1]?(t[1]===e[1]?e[1]:(1e3*(t[1]+(e[1]-t[1])*r)>>0)/1e3)+n:"0","translate("+a.join(",")+")"},rotate:function(t,e,n,r){return"rotate("+(1e3*(t+(e-t)*r)>>0)/1e3+n+")"},scale:Mt,skew:_t}},SVGDraw:{component:"svgDraw",property:"draw",defaultValue:"0% 0%",Interpolate:{numbers:D},functions:Pt,Util:{getRectLength:Ct,getPolyLength:Ot,getLineLength:Tt,getCircleLength:St,getEllipseLength:It,getTotalLength:jt,resetDraw:function(t){t.style.strokeDashoffset="",t.style.strokeDasharray=""},getDraw:At,percent:kt}},SVGMorph:{component:"svgMorph",property:"path",defaultValue:[],Interpolate:xe,defaultOptions:{morphPrecision:10},functions:Se,Util:{addPoints:_e,bisect:ke,getPolygon:Oe,validPolygon:Ce,getInterpolationPoints:Te,pathStringToPolygon:Ee,distanceSquareRoot:ae,midPoint:re,approximatePolygon:we,rotatePolygon:Me,pathToString:le,pathToCurve:function(t){if(function(t){return Rt(t)&&t.every((function(t){return"MC".includes(t[0])}))}(t))return Zt(t);for(var e=Jt(Gt(t)),n=Object.assign({},Wt),r=[],a="",i=e.length,o=0;o0?1:-1)*Math.floor(Math.abs(t)):t}(r);return Math.min(Math.max(t,0),n)},function(r){var n=this,o=Object(r);if(null==r)throw new TypeError("Array.from requires an array-like object - not null or undefined");var i,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&&(i=arguments[2])}for(var u,f=e(o.length),p=t(n)?Object(new n(f)):new Array(f),c=0;c>>0;if("function"!=typeof r)throw new TypeError(r+" is not a function");for(arguments.length>1&&(t=arguments[1]),n=new Array(i),e=0;e>>0,o=0;o>>0;if("function"!=typeof r&&"[object Function]"!==Object.prototype.toString.call(r))throw new TypeError;for(arguments.length>1&&(n=t),e=0;e=0?e=i:(e=n+i)<0&&(e=0);e0?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=0?n=o:(n=e+o)<0&&(n=0);n + + + + + + + + + + + + + + KUTE.js SVG Cubic Morph + + + + + + + + + + + + + +
+ + + +
+

SVG Cubic Morph

+

The component that also covers SVG morphing, with similar functionality as for the SVG Morph component, but with a different + implementation for value processing and animation setup.

+
+ +
+
+
+
+

Overview

+

Animate SVG paths with cubic-bezier path commands and improved performance.

+
+
+

The KUTE.js SVG Cubic Morph component enables animation for the d (description) presentation attribute and is the latest in all the SVG + components.

+

The component will process paths and out of the box will provide the closest possible interpolation by default. It also implements a series of solutions from + Paper.js to determine paths draw direction and automatically reverse one of them for most accurate presentation and as a result + the previously featured options reverseFirstPath and reverseSecondPath have been deprecated.

+

The main difference with the SVG Morph component is the fact that this components is converting all path commands to cubicBezierTo, giving it + the upper hand over the original morph component in many regards. While the other component will spend time to process the path data and create polygons, this component should + deliver the animation faster and using considerably less power.

+

All path processing is powered by our SVGPathCommander starting KUTE.js version 2.0.14, which aims to modernize and + improve the path processing and enable transition from closed to and from un-closed shapes.

+
+
+
+
+ + +
+

Basic Example

+

The first morphing animation example is a transition from a rectangle into a star, just like for the other component.

+ +
<svg id="morph-example" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 600 600">
+<path id="rectangle" class="bg-lime" d="M38.01,5.653h526.531c17.905,0,32.422,14.516,32.422,32.422v526.531 c0,17.905-14.517,32.422-32.422,32.422H38.01c-17.906,0-32.422-14.517-32.422-32.422V38.075C5.588,20.169,20.104,5.653,38.01,5.653z"/>
+<path id="star" style="visibility:hidden" d="M301.113,12.011l99.25,179.996l201.864,38.778L461.706,380.808 l25.508,203.958l-186.101-87.287L115.01,584.766l25.507-203.958L0,230.785l201.86-38.778L301.113,12.011"/>
+</svg>
+
+ +

Now we can apply both .to() and fromTo() methods:

+ +
// the fromTo() method
+var tween = KUTE.fromTo('#rectangle', {path: '#rectangle' }, { path: '#star' }).start();
+// OR
+// the to() method will take the path's d attribute value and use it as start value
+var tween = KUTE.to('#rectangle', { path: '#star' }).start();
+// OR
+// simply pass in a valid path string without the need to have two paths in your SVG
+var tween = KUTE.to('#rectangle', { path: 'M301.113,12.011l99.25,179.996l201.864,38.778L461.706,380.808l25.508,203.958l-186.101-87.287L115.01,584.766l25.507-203.958L0,230.785l201.86-38.778L301.113,12.011' }).start();
+
+ +

By default, the component will process the paths as authored and deliver its best without any option required, like for the first red rectangle below which applies to any of the above invocations:

+ +
+ + + + + + + + +
+ Start +
+
+ +

Some takeaways:

+
    +
  • The olive shape and its corresponding end shape are both the originals, un-edited shapes.
  • +
  • The blue shape and its corresponding end shape have been edited by removing their Z path commands and by adding additional curve points using a vector graphics editor to match + the amount of points in both shapes.
  • +
+

In this example we focus on experimentation to discover ways to optimize the morph animation so that the points travel optimal distance. Keep in mind that the Z path command is actually a shorthand + for L (line path command), sometimes it's required to close the shape, however the path processing tools will remove it or replace it when converting path segments to C cubic-bezier.

+

Each morph animation as well as each pair of shapes can have its own quirks. You can use the technique on your shapes to optimize the animation to your style. Chris Coyier wrote + an excelent article in which he explains the terminology and workflow on how SVG morphing animation works with simple examples.

+ + +

Morphing Unclosed Shapes To Closed Shapes

+

The next set of morphing animations is a transition from a line into a circle and showcases the component's behavior when both shapes are closed (they have the Z path command) or one or another is not, + but first let's create the necessary markup and scripting:

+ +
<svg id="morph-example" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 600 600">
+<path id="line" fill="transparent" stroke="orange" stroke-linejoin="round" stroke-width="10" d="M10 300 L580 300"/>
+<path id="star" style="visibility:hidden" d="M10,300a290,290 0 1,0 580,0a290,290 0 1,0 -580,0z"/>
+</svg>
+
+ +
// the fromTo() method
+var tween = KUTE.fromTo('#line', {path: '#line' }, { path: '#circle' }).start();
+// OR
+// the to() method will take the path's d attribute value and use it as start value
+var tween = KUTE.to('#line', { path: '#circle' }).start();
+
+ +
+ + + + + + + + +
+ Start +
+
+ +

As you can see, the functionality of this component is very different from the svgMorph component in the sense that it will + morph shapes as authored. If you replay the above animations, here are a few takeaways:

+
    +
  • the orange line is closed, this makes the last Z path command behave like a regular line;
  • +
  • the green line is not closed, and the presentation looks quite different compared to the other example as well as + the other cubicMorph component.
  • +
+

This is the visual presentation you can expect with this component. Keep in mind that stroke attributes like stroke-linejoin such can have + a small impact on your animation, particularly on start and end points.

+ + +

Subpath Example

+

In other cases, you may want to morph paths that have subpaths. Let's have a look at the following SVG:

+ +
<svg id="multi-morph-example" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024">
+<path id="w1" d="M412.23 511.914c-47.708-24.518-94.086-36.958-137.88-36.958-5.956 0-11.952 0.18-17.948 0.708-55.88 4.624-106.922 19.368-139.75 30.828-8.708 3.198-17.634 6.576-26.83 10.306l-89.822 311.394c61.702-22.832 116.292-33.938 166.27-33.938 80.846 0 139.528 30.208 187.992 61.304 22.962-77.918 78.044-266.09 94.482-322.324-11.95-7.284-24.076-14.57-36.514-21.32z 
+  m116.118 79.156l-90.446 314.148c26.832 15.372 117.098 64.05 186.212 64.05 55.792 0 118.252-14.296 190.834-43.792l86.356-301.976c-58.632 18.922-114.876 28.52-167.464 28.52-95.95 0-163.114-31.098-205.492-60.95z 
+  m-235.526-222.28c77.118 0.798 134.152 30.208 181.416 60.502l92.752-317.344c-19.546-11.196-70.806-39.094-107.858-48.6-24.386-5.684-50.02-8.616-77.204-8.616-51.796 0.976-108.388 13.946-172.888 39.8l-88.44 310.596c64.808-24.436 120.644-36.34 172.086-36.34 0.046 0.002 0.136 0.002 0.136 0.002z 
+  m731.178-170.666c-58.814 22.832-116.208 34.466-171.028 34.466-91.686 0-159.292-31.802-203.094-62.366l-91.95 318.236c61.746 39.708 128.29 59.878 198.122 59.878 56.948 0 115.94-13.68 175.462-40.688l-0.182-2.222 3.734-0.886 88.936-306.418z"/>
+<path id="w2" d="M0 187.396l367.2-50.6v354.798h-367.2v-304.2z 
+  m0 649.2v-299.798h367.2v350.398z 
+  m407.6 56v-355.798h488.4v423.2z 
+  m0-761.2l488.4-67.4v427.6h-488.4v-360.2z"/>
+</svg>
+
+ +

Similar to the svgMorph component, this component doesn't provide multipath processing so we should split the sub-paths into multiple <path> shapes, analyze and associate them + in a way that corresponding shapes are close and their points travel the least possible distance.

+ +

Now since we've worked with these paths before, the first example below showcases how the svgCubicMorph component handles it by default. The following example was made possible by editing the shapes with a vector + graphics editor. The last example showcases a creative use of association between starting and end shapes. Let's have a look:

+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Start +
+
+

Make sure to check the markup here as well as the svgCubicMorph.js for a full code review.

+ +

Intersecting Paths Example

+

The same preparation apply here, however the outcome is a bit different with cubic-bezier path commands, as shown in the first example. For the next two examples, the shapes have been edited for a better outcome. + Let's have a look:

+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Start +
+
+ +

So the technique involves creating <mask> elements, splitting multipath shapes into multiple <path> shapes, matching the amount of starting and ending shapes by duplicating + an existing shape or by sampling another shape for the same purpose, editing shapes for more accurate point-to-point animation, as well as various options to optimize the visual presentation.

+ +

That's it, you now mastered the SVG Cubic Morph component.

+ +

Notes

+
    +
  • Starting with KUTE.js version 2.0.14 the component implements SVGPathCommander for path processing, solving previous issues with over-optimized + path strings among other issues. You can try the SVGPathCommander demo page to prepare your path strings.
  • +
  • Since animation works only with path SVGElements, you might need a shapeToPath utility.
  • +
  • Both SVG morph components will always animate first sub-path from both starting and ending shapes, and for any case hopefully this demo will guide you in mastering the technique.
  • +
  • In some cases your start and end shapes don't have a very close size, you can use your vector graphics editor of choice or SVGPathCommander + to apply a scale transformation to your shapes' path commands.
  • +
  • The SVG Cubic Morph component WILL NOT animate paths with sub-paths as of KUTE.js version 2.0.14, hopefuly this guide will help you break the ice.
  • +
  • Compared to svgMorph, this component can be more memory efficient, as we're dealing with significantly less interpolation points which translates directly + into better performance and the shapes never show any sign of "polygon artifacts".
  • +
  • Some shapes can be broken even if the browser won't throw any error so make sure you double check with your SVG editor of choice.
  • +
  • In some cases the duplicated curve bezier points don't produce the best morphing animation, but you can edit the shapes and add your own additional path commands between the existing ones + so that the component will work with actual points that travel less and produce a much more "natural" morphing animation.
  • +
  • The edited shapes can be found in the assets/img folder of this demo, make sure to check them out.
  • +
  • Make sure to check the svgCubicMorph.js for a full code review.
  • +
  • This component is affected by the the fill-rule="evenodd" specific SVG attribute, you must make sure you check your shapes in that regard as well.
  • +
  • This component is bundled with the demo/src/kute-extra.js file.
  • +
+
+ + + + +
+ + + + + + + + + + + + + diff --git a/svgDraw.html b/svgDraw.html new file mode 100644 index 0000000..4855c12 --- /dev/null +++ b/svgDraw.html @@ -0,0 +1,165 @@ + + + + + + + + + + + + + + + KUTE.js SVG Draw + + + + + + + + + + + + +
+ + + +
+

SVG Draw

+

The component that animates the stroke of any SVGElement and updates the style attribute of the target.

+
+ +
+
+
+
+

Overview

+

Animate the stroke for most SVGElement shapes, with a very easy to use coordinates system.

+
+
+

The KUTE.js SVG Draw component enables animation for the stroke-dasharray and stroke-dashoffset properties via CSS style.

+

The component uses the SVG standard .getTotalLength() method for <path> shapes, while for the other shapes it uses some helper methods to calculate the values + required for animation.

+

It can work with <path>, <glyph>, <circle>, <ellipse>, <rect>, <line>, <polyline> and <polygon> + shapes. It uses a very simple to use coordinates system so you can set up animations right away.

+

As you will see in the examples below, there is alot of flexibility in terms of input values.

+
+
+
+
+ +
+

Examples

+ +
// draw the stroke from 0-10% to 90-100%
+var tween1 = KUTE.fromTo('selector1',{draw:'0% 10%'}, {draw:'90% 100%'});
+
+// draw the stroke from zero to full path length
+var tween2 = KUTE.fromTo('selector1',{draw:'0% 0%'}, {draw:'0% 100%'});
+
+// draw the stroke from full length to 50%
+var tween3 = KUTE.fromTo('selector1',{draw:'0% 100%'}, {draw:'50% 50%'});
+
+ + +

Based on the above sample code, let's see some animation going on:

+ +
+ + + + + + + +
+ Start +
+
+ + +

Notes

+
    +
  • Remember that the draw property also accepts absolute values, eg. draw: '0 150'; the .to() method takes 0% 100% + as start value for your tweens when stroke-dasharray and stroke-dashoffset properties are not set.
  • +
  • Sometimes the stroke on some shapes may not completely close, you might want to consider values outside the [0-100] percent range.
  • +
  • The SVG Draw component can be combined with any other SVG based component to create even more complex animations for SVG elements.
  • +
  • This component is bundled with the default distribution kute.js and the demo/src/kute-extra.js file.
  • +
+
+ + + + + +
+ + + + + + + + + + + + + + + diff --git a/svgMorph.html b/svgMorph.html new file mode 100644 index 0000000..a23fdf1 --- /dev/null +++ b/svgMorph.html @@ -0,0 +1,427 @@ + + + + + + + + + + + + + + KUTE.js SVG Morph + + + + + + + + + + + + +
+ + + +
+

SVG Morph

+

The component that covers SVG morphing, an animation that's close to impossible with CSS3 transitions and not supported in some legacy browsers. It comes packed + with tools and options to improve performance and visual presentation.

+
+ +
+
+
+
+

Overview

+

Animate SVG paths with line-to path commands, improve visual presentation and optimize performance on any device.

+
+
+

The KUTE.js SVG Morph component enables animation for the d (description) presentation attribute and is one of the most important in all the + SVG components.

+

It only works with inline SVGPathElement shapes and the presentation is always the same when shapes are closed or not (their d attribute ends with Z path command). + On initialization or animation start, depending on the chosen KUTE.js method, it will sample a number of points + along the two paths based on a default / given sample size and will create two arrays of coordinates we need for interpolation.

+

This component was originally inspired by a D3.js path morphing example and now implements a set of + D3 polygon geometric operations and other functionalities from flubber to + produce the coordinates for a very consistent morphing animation.

+

While in some cases you might be able to create SVG morphing animations via CSS3 transition, this component was developed to provide various solutions for working + with complex shapes, bringing convenience, resources and clarity to one of the most complex types of animation.

+

All path processing is powered by our SVGPathCommander starting KUTE.js version 2.0.14, which aims to modernize and + improve the path processing and enable you to prepare your path strings in Node.js.

+
+
+ +
+
+

Options

+

The easy way to optimize morphing animation for every device in a single option.

+
+
+

The SVG Morph component comes with a simple option to optimize animation on every device. Previous versions used to have additional options required for processing, + but now the component will handle all that for you.

+ +
    +
  • morphPrecision: Number option allows you to set the sampling size of the shapes in pixels. The lesser value the better visual but the more power consumption + and less performance. The default value is 10 but the processing functions will determine the best possible outcome depending on shapes' path commands.
  • +
+
+
+
+
+ +
+ +

Basic Example

+

The first morphing animation example is a transition from a rectangle into a star, the first path is the start shape and the second is the end shape; we can also add some ID to the + paths so we can easily target them with our code.

+ +
<svg id="morph-example" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 600 600">
+  <path id="rectangle" d="M38.01,5.653h526.531c17.905,0,32.422,14.516,32.422,32.422v526.531 c0,17.905-14.517,32.422-32.422,32.422H38.01c-17.906,0-32.422-14.517-32.422-32.422V38.075C5.588,20.169,20.104,5.653,38.01,5.653z"/>
+  <path id="star" style="visibility:hidden" d="M301.113,12.011l99.25,179.996l201.864,38.778L461.706,380.808 l25.508,203.958l-186.101-87.287L115.01,584.766l25.507-203.958L0,230.785l201.86-38.778L301.113,12.011"/>
+</svg>
+
+ +

Now we can apply both .to() and fromTo() methods:

+ +
// the fromTo() method
+var tween = KUTE.fromTo('#rectangle', {path: '#rectangle' }, { path: '#star' }).start();
+// OR
+// the to() method will take the path's d attribute value and use it as start value
+var tween = KUTE.to('#rectangle', { path: '#star' }).start();
+// OR
+// simply pass in a valid path string without the need to have two paths in your SVG
+var tween = KUTE.to('#rectangle', { path: 'M301.113,12.011l99.25,179.996l201.864,38.778L461.706,380.808l25.508,203.958l-186.101-87.287L115.01,584.766l25.507-203.958L0,230.785l201.86-38.778L301.113,12.011' }).start();
+
+ +

For all the above tween objects the animation should look like this:

+ +
+ + + + +
+ Start +
+
+ +

That's it, let move on to the next example.

+ +

Morphing Unclosed Shapes To Closed Shapes

+

The next morphing animation example is a transition from a line into a circle and this example showcases the component's behavior when one of the paths is not closed (it doesn't have the Z path command), + while the other is closed. In all cases, the component will always consider shapes to be closed, so let's have a look at the example:

+ +
<svg id="morph-example" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 600 600">
+  <path id="line" fill="transparent" stroke="orange" stroke-linejoin="round" stroke-width="10" d="M10 300L590 300"/>
+  <path id="circle" style="visibility:hidden" d="M10,300a290,290 0 1,0 580,0a290,290 0 1,0 -580,0z"/>
+</svg>
+
+ +

Now we can apply both .to() and fromTo() methods:

+ +
// the fromTo() method
+var tween = KUTE.fromTo('#line', {path: '#line' }, { path: '#circle' }).start();
+// OR
+// the to() method will take the path's d attribute value and use it as start value
+var tween = KUTE.to('#line', { path: '#circle' }).start();
+
+ +
+ + + + + + + + +
+ Start +
+
+ +

The functionality of this component is different from the svgCubicMorph component in the sense that it will animate the shapes as if they are both and always closed. + In the above example, the orange line is closed while the green line is not, and the animation is the same.

+ + +

Polygon Paths

+

When your paths are only lineto, vertical-lineto and horizontal-lineto based shapes (the d attribute consists of only L, V and + H path commands), the SVG Morph component will work differently from previous versions, it will sample points as for any other non-polygon shapes. In this case you can set a higher + morphPrecision value to optimize performance.

+ +
// let's morph a triangle into a star
+var tween1 = KUTE.to('#triangle', { path: '#star' }).start();
+
+// or same path into a square
+var tween2 = KUTE.to('#triangle', { path: '#square' }).start();
+
+ +
+ + + + + + + + + + + + + +
+ Start +
+
+

Did you catch the cat?

+ + +

Subpath Example

+

In other cases, you may want to morph paths that have subpaths. Let's have a look at the following paths:

+
<svg id="multi-morph-example" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024">
+  <path id="w1" d="M412.23 511.914c-47.708-24.518-94.086-36.958-137.88-36.958-5.956 0-11.952 0.18-17.948 0.708-55.88 4.624-106.922 19.368-139.75 30.828-8.708 3.198-17.634 6.576-26.83 10.306l-89.822 311.394c61.702-22.832 116.292-33.938 166.27-33.938 80.846 0 139.528 30.208 187.992 61.304 22.962-77.918 78.044-266.09 94.482-322.324-11.95-7.284-24.076-14.57-36.514-21.32z 
+  m116.118 79.156l-90.446 314.148c26.832 15.372 117.098 64.05 186.212 64.05 55.792 0 118.252-14.296 190.834-43.792l86.356-301.976c-58.632 18.922-114.876 28.52-167.464 28.52-95.95 0-163.114-31.098-205.492-60.95z 
+  m-235.526-222.28c77.118 0.798 134.152 30.208 181.416 60.502l92.752-317.344c-19.546-11.196-70.806-39.094-107.858-48.6-24.386-5.684-50.02-8.616-77.204-8.616-51.796 0.976-108.388 13.946-172.888 39.8l-88.44 310.596c64.808-24.436 120.644-36.34 172.086-36.34 0.046 0.002 0.136 0.002 0.136 0.002z 
+  m731.178-170.666c-58.814 22.832-116.208 34.466-171.028 34.466-91.686 0-159.292-31.802-203.094-62.366l-91.95 318.236c61.746 39.708 128.29 59.878 198.122 59.878 56.948 0 115.94-13.68 175.462-40.688l-0.182-2.222 3.734-0.886 88.936-306.418z"/>
+  <path id="w2" d="M0 187.396l367.2-50.6v354.798h-367.2v-304.2z 
+  m0 649.2v-299.798h367.2v350.398z 
+  m407.6 56v-355.798h488.4v423.2z 
+  m0-761.2l488.4-67.4v427.6h-488.4v-360.2z"/>
+</svg>
+
+

As you can see, both these paths have subpaths, and this component will only animate the first subpath from both paths. To animate them all there are a few easy steps required in preparation for + the animation, so here's a quick guide:

+
    +
  1. Use the SVGPathCommander demo page to split your path string into multiple sub-path strings. It's important to do this to avoid + inconsistencies with shapes having relative sub-path commands. You can also use the utility to reverse paths if required.
  2. +
  3. Create a new <path> shape for each sub-path string from M to Z path commands. See the sample code below.
  4. +
  5. Give the new paths an id="uniqueID" attribute so you can target them easily. You could use relevant namespace to help you better understand positioning. EG: id="square-top-left" + or id="left-eye"
  6. +
  7. In the browser console inspect with your mouse all paths from both starting and ending shapes and determine which shapes should morph to which. The idea is to produce a morphing animation + where points from each shape travel the least possible distance, however this is where you can get creative, as shown in one of the examples below.
  8. +
  9. If the number of the starting and ending shapes are not equal, you can consider either duplicating one of the subpath shapes close to it's corresponding subpath shape or creating a sample + shape close to the corresponding subpath shape.
  10. +
  11. Update the id attribute for all starting and ending shapes to match positions and make it easier to work with the tween objects.
  12. +
  13. Optional: set a fill attribute for each new shape if you like, normally you coulnd't have done it with the original paths.
  14. +
  15. Create your tween objects and get to animating and tweaking.
  16. +
+ +

For our example here, this is the end result markup for the shapes to be used for morphing animation:

+ +
<svg id="multi-morph-example" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 550 550">
+  <path id="w11" d="M206.115,255.957c-23.854-12.259-47.043-18.479-68.94-18.479c-2.978,0-5.976,0.09-8.974,0.354 c-27.94,2.312-53.461,9.684-69.875,15.414c-4.354,1.599-8.817,3.288-13.415,5.152L0,414.096 c30.851-11.416,58.146-16.969,83.135-16.969c40.423,0,69.764,15.104,93.996,30.652c11.481-38.959,39.022-133.045,47.241-161.162 C218.397,262.975,212.334,259.332,206.115,255.957z"/>
+  <path id="w12" d="M264.174,295.535l-45.223,157.074c13.416,7.686,58.549,32.024,93.105,32.024 c27.896,0,59.127-7.147,95.417-21.896l43.179-150.988c-29.316,9.461-57.438,14.26-83.732,14.26 C318.945,326.01,285.363,310.461,264.174,295.535z"/>
+  <path id="w13" d="M146.411,184.395c38.559,0.399,67.076,15.104,90.708,30.251l46.376-158.672c-9.772-5.598-35.403-19.547-53.929-24.3c-12.193-2.842-25.01-4.308-38.602-4.308c-25.898,0.488-54.194,6.973-86.444,19.9 L60.3,202.564c32.404-12.218,60.322-18.17,86.043-18.17C146.366,184.395,146.411,184.395,146.411,184.395L146.411,184.395z"/>
+  <path id="w14" d="M512,99.062c-29.407,11.416-58.104,17.233-85.514,17.233c-45.844,0-79.646-15.901-101.547-31.183L278.964,244.23 c30.873,19.854,64.146,29.939,99.062,29.939c28.474,0,57.97-6.84,87.73-20.344l-0.091-1.111l1.867-0.443L512,99.062z"/>
+
+  <path id="w21" style="visibility:hidden" d="M192 471.918l-191.844-26.297-0.010-157.621h191.854z"/>
+  <path id="w22" style="visibility:hidden" d="M479.999 288l-0.063 224-255.936-36.008v-187.992z"/>
+  <path id="w23" style="visibility:hidden" d="M0.175 256l-0.175-156.037 192-26.072v182.109z"/>
+  <path id="w24" style="visibility:hidden" d="M224 69.241l255.936-37.241v224h-255.936z"/>
+</svg>
+
+ +

The graphic on the left side of the below example is exactly for the above markup, no option needed out of the box nearly perfect animation, while the right side example showcases a different or + perhaps more creative example of this morph animation:

+ + +
+ + + + + + + + + + + + + + + + + + + + + + +
+ Start +
+
+ +

As you can imagine, it's quite hard if not impossible to code something that would do all this work automatically. Perhaps in the future we could have dedicated AI powered APIs to train and + program for this work, but until then, it's up to you to learn, observe, adapt and tweak in order to get the most out of this component.

+ +

Intersecting Paths Example

+

The last morph example is a bit more complex as the paths have intersecting subpaths with different positions as well as different amounts of subpaths. In this case you can manually clone one or + more subpaths in a way that the number of starting shapes is equal to the number of ending shapes, as well as making sure the starting shapes are close to their corresponding ending shapes; at this point + you should be just like in the previous examples.

+ +

You need to inspect the markup here in your browser console to get an idea on how the shapes have been arranged, we have used a <mask> where we included the subpaths of both start + and end shape, just to get the same visual as the original paths.

+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Start +
+
+ +

The example on the left side showcases the cloning of one of the shapes to match the amount of starting and ending shapes, while the right side showcases + using a sample shape, somewhere close to its corresponding end shape, a much better animation, but in the end, it's up to you and your need on preparing, + analyzing as well as deciding on how to optimize these cases.

+ + +

Notes

+
    +
  • Starting with KUTE.js version 2.0.14 the component implements SVGPathCommander for path processing, solving previous issues with over-optimized + path strings among other issues. You can try the SVGPathCommander demo page to prepare your path strings.
  • +
  • Since SVG Morph animation works only with path elements, you might need a convertToPath feature, so + grab one here.
  • +
  • Both SVG morph components will always animate first sub-path from both starting and ending shapes, and for any case hopefully this demo will guide you in mastering the technique.
  • +
  • In some cases your start and end shapes don't have a very close size, you can use your vector graphics editor of choice or SVGPathCommander + to apply a scale transformation to your shapes' path commands.
  • +
  • The morphing animation is expensive so try to optimize the number of morph animations that run at the same time. When morphing sub-paths/multi-paths instead of cloning shapes to have same number + of shapes in both starting and ending shapes, you should also consider a fade and/or scale animation to improve the overal animation performance, mobile devices still don't do very much, regardless + of the advertising.
  • +
  • Large displays would need best resolution possible so a small morphPrecision value (1-10) would be required, assuming performant hardware are powering the displays. For small displays + you can get quite comfortable with almost any value, including the default value.
  • +
  • With all the tools at hand, you can also try to use a morphPrecision value for every resolution. Take some time to experiement, you might find a better morphPrecision + value you can use for any particular device and / or resolution.
  • +
  • The animation performance is the same for both .to() and .fromTo() methods, but the ones that use the second method will start faster, because the values have been prepared + already and for the first method the processing of the two paths happens on tween .start() call, thus delaying the animation, so keep that in mind when working with syncing multiple tweens, + the .to() based morph will always start later. Of course this assumes the you cache the tween objects first and start the animation later, if not (you start the animation on object creation), + both methods will be delayed.
  • +
  • The SVG Morph component uses approximatelly the same algorithm as D3.js for determining the coordinates for interpolation, the visual presentation is more natural compared to the other component, + it might be a better solution for your applications.
  • +
  • Make sure to check the svgMorph.js for a full code review.
  • +
  • This component should ignore the fill-rule="evenodd" specific SVG attribute, but you can make sure you check your shapes in that regard as well.
  • +
  • This component is bundled with the official kute.js distribution file.
  • +
+
+ + + + +
+ + + + + + + + + + + + diff --git a/svgTransform.html b/svgTransform.html new file mode 100644 index 0000000..d20d663 --- /dev/null +++ b/svgTransform.html @@ -0,0 +1,389 @@ + + + + + + + + + + + + + + KUTE.js SVG Transform + + + + + + + + + + + + + +
+ + + +
+

SVG Transform

+

The component that covers transform animation on SVGElement targets, solves browser inconsistencies and provides a similar visual presentation as + with other transform based components on non-SVGElements targets.

+
+ +
+
+
+
+

Overview

+

Animate 2D transform functions on SVG elements on any SVG enabled browser.

+
+
+

The KUTE.js SVG Transform component enables animation for the transform presentation attribute on any SVGElement target.

+

The SVG Transform is an important part of the SVG components for some reasons:

+
    +
  • It was developed to solve most browser inconsistencies of the time for transform animation. Nowadays modern browsers are Chromium browsers that work with regular 2D + transform functions.
  • +
  • The unique way to normalize translation to produce the kind of animation that is just as consistent as for CSS3 transforms on non-SVGElement targets.
  • +
  • The value processing is consistent with the current W3 draft.
  • +
+

Keep in mind that the transform attribute accepts no measurement units such as degrees or pixels, but it expects rotation / skew angles to be in degrees, and + translations in lengths measured around the parent <svg> element viewBox attribute.

+
+
+
+
+

Options

+

Keep everything under control and handle any situation with a single option.

+
+
+

The only component that keeps the transformOrigin option because it's required to compute transform functions in the SVG + cooordinates system.

+ +
    +
  • transformOrigin: ['50%','50%'] sets the much needed origin. Eg: transformOrigin:[50,50]. The default + value is 50% 50% of the target element box, which is contrary with the SVG draft.
  • +
+ +

Keep in mind that the component will disregard the current SVG default origin of 0px 0px of the target's parent, even if the browsers' default + transformOrigin have been normalized over the years.

+ +

The transformOrigin tween option can also be used to set coordinates of a parent <svg> element (in the second example below). + Values like top left values are also accepted, but will take the target element's box as a reference, not the parent's box.

+
+
+
+
+ +
+ +

2D Transform

+
    +
  • translate function applies horizontal and / or vertical translation. EG. translate:150 to translate a shape 150px to the right or + translate:[-150,200] to move the element to the left by 150px and to bottom by 200px. IE9+
  • +
  • rotate function applies rotation to a shape on the Z axis. Eg. rotate:150 will rotate a shape clockwise by 150 degrees around it's own center or around + the transformOrigin: '450 450' set tween option coordinate of the parent element. IE9+
  • +
  • skewX function used to apply a skew transformation on the X axis. Eg. skewX:25 will skew a shape by 25 degrees. IE9+
  • +
  • skewY function used to apply a skew transformation on the Y axis. Eg. skewY:25 will skew a shape by 25 degrees. IE9+
  • +
  • scale function used to apply a single value size transformation. Eg. scale:0.5 will scale a shape to half of it's initial size. IE9+
  • +
  • matrix function is not supported, but the Transform Matrix covers you there, if you'll read below.
  • +
+ +

Examples

+

As explained with the Transform Matrix component, the DOMMatrix API will replace webkitCSSMatrix and SVGMatrix and on this page we intend to put + the two components head to head, the elements on the left will be using Transform Matrix component and equivalent 2D transform functions, while the elements on the right will be using 2D functions of + the SVG Transform component.

+ +

The SVG Transform component comes with a reliable set of scripts that work on all browsers, making use of the SVGMatrix API for some origin calculation, the transform presentation + attribute and the svgTransform tween property with a familiar and very easy notation:

+ +
// using the svgTransform property works in all SVG enabled browsers
+var tween2 = KUTE.to(
+'shape',                // target
+{                       // to
+  svgTransform: { 
+    translate: [150,100], 
+    rotate: 45, 
+    skewX: 15, skewY: 20, 
+    scale: 1.5 
+  }
+}
+);
+
+// transformMatrix can animate SVGElement targets on modern browsers
+// requires adding styling like `transform-origin:50% 50%` to the target element
+var tween1 = KUTE.to(
+'shape',            // target
+{                   // to
+  transform: { 
+    translate3d: [150,100,0], 
+    rotate3d: [0,0,45], 
+    skew: [15,20], 
+    scale3d: [1.5,1.5,1] 
+  } 
+}
+);
+
+ +

Let's see some examples and explain each case.

+ + +

SVG Rotation

+

Our first chapter of the SVG transform is all about rotations, perhaps the most important part here. The svgTransform will only accept single value + for the angle value rotate: 45, the rotation will go around the shape's center point by default, again, contrary to the browsers' default value and you can set a transformOrigin + tween option to override the behavior.

+

The argument for this implementation is that this is something you would expect from regular HTML elements rotation and probably most needed, not to mention the amount of savings in the codebase department. + Let's have a look at a quick demo:

+ +
+ + + + + +
+ Start +
+
+

For the first element, the Transform Matrix creates the rotation animation via rotate3d[0,0,360] tween property around the element center coordinate, as we've set transform-origin:25% 50% + to the element's style; this animation doesn't work in IE browsers, while in older versions Firefox the animation is inconsistent. The second element uses the rotate: 360 function of the SVG Transform + component and the rotation animation is around the element's own central point without any option, an animation that DO WORK in all SVG enabled browsers.

+ +

When for non-SVG elements' transform we could have used values such as center bottom as transformOrigin (also not supported in all modern browsers for SVGs), the entire processing falls + to the browser, however when it comes to SVGs our component here will compute the transformOrigin tween option value accordingly to use the shape's .getBBox() value to determine + for instance the coordinates for 25% 75% position or center top.

+ +

In other cases you may want to rotate shapes around the center point of the parent <svg> or <g> element, and we use it's .getBBox() to determine the 50% 50% + coordinate, so here's how to deal with it:

+ +
// rotate around parent svg's "50% 50%" coordinate as transform-origin
+// get the bounding box of the parent element
+var svgBB = element.ownerSVGElement.getBBox(); // returns an object of the parent <svg> element
+
+// we need to know the current translate position of the element [x,y]
+// in our case is:
+var translation = [580,0];
+
+// determine the X point of transform-origin for 50%
+var OriginX = svgBB.width * 50 / 100 - translation[0];
+
+// determine the Y point of transform-origin for 50%
+var OriginY = svgBB.height * 50 / 100 - translation[1];
+
+// set your rotation tween with "50% 50%" transform-origin of the parent <svg> element
+var rotationTween = KUTE.to(element, {svgTransform: {rotate: 150}}, { transformOrigin: [OriginX, OriginY]} );
+
+ +
+ + + + + +
+ Start +
+
+

Same as the above example, the first element is rotated by the Transform Matrix component and is using transform-origin: 50% 50%; styling, while the second element is rotated by the SVG Transform + component with the above calculated transform-origin.

+ +

SVG Translation

+

In this example we'll have a look at translations, so when setting translate: [150,0], the first value is X (horizontal) coordinate to which the shape will translate to and the second value is + Y (vertical) coordinate for translation. When translate: 150 notation is used, the script will understand that's the X value and the Y value is 0 just like for the regular HTML elements + transformation. Let's have a look at a quick demo:

+ +
+ + + + + +
+ Start +
+
+

The first element uses the Transform Matrix translate3d: [580,0,0] function, while the second tween uses the translate: [0,0] as svgTransform value. + For the second example the values are unitless and are relative to the viewBox attribute.

+ +

SVG Skew

+

For skews we have: skewX: 25 or skewY: -25 as SVGs don't support the skew: [X,Y] function. Here's a quick demo:

+
+ + + + + +
+ Start +
+
+

The first tween skews the shape on both X and Y axes in a chain via Transform Matrix skew:[-15,-15] function and the second tween skews the shape on X and Y axes via the svgTransform functions skewX:15 and + skewY:15 tween properties. You will notice translation kicking in to set the transform origin.

+ +

SVG Scaling

+

Another transform example for SVGs is the scale. Unlike translations, for scale animation the component only accepts single value like scale: 1.5, for both X (horizontal) axis and Y (vertical) axis, + to keep it simple and even if SVGs do support scale(X,Y). But because the scaling on SVGs depends very much on the shape's position, the script will always try to adjust the translation to + make the animation look as we would expect. A quick demo:

+
+ + + + + +
+ Start +
+
+

The first tween scales the shape at scale: 1.5 via Transform Matrix component and it's scale3d:[1.5,1.5,1] function, and the second tween scales down the shape at scale: 0.5 + value via svgTransform. If you inspect the elements, you will notice for the second shape translation is involved, and this is to keep transform-origin at an expected + 50% 50% of the shape box. A similar case as with the skews.

+ +

Mixed SVG Transform Functions

+

Our last transform example for SVG Transform is the mixed transformation. Just like for the other examples the component will try to adjust the rotation transform-origin to make it look as you would expect it + from regular HTML elements. Let's combine 3 functions at the same time and see what happens:

+
+ + + + + +
+ Start +
+
+

Both shapes are scaled at scale: 1.5, translated to translate: 250 and skewed at skewX: -15. If you inspect the elements, you will notice the second shape's translation is + different from what we've set in the tween object, and this is to keep transform-origin at an expected 50% 50% value. This means that the component will also compensate rotation transform origin + when skews are used, so that both CSS3 transform property and SVG transform attribute have an identical animation.

+ +

Chained SVG Transform

+

The SVG Transform does not work with SVG specific chained transform functions right away (do not confuse with tween chain), Ana Tudor explains best here, + but if your SVG elements only use this feature to set a custom transform-origin, it should look like this:

+ +
<svg>
+  <circle transform="translate(150,150) rotate(45) scale(1.2) translate(-150,-150)" r="20"></circle>
+</svg>
+
+ +

In this case I would recommend using the values of the first translation as transformOrigin for your tween built with the .fromTo() method like so:

+ +
// a possible workaround for animating a SVG element that uses chained transform functions
+KUTE.fromTo(element,
+  {svgTransform : { translate: 0, rotate: 45, scale: 0.5 }}, // we asume the current translation is zero on both X & Y axis
+  {svgTransform : { translate: 450, rotate: 0, scale: 1.5 }}, // we will translate the X to a 450 value and scale to 1.5
+  {transformOrigin: [150,150]} // tween options use the transform-origin of the target SVG element
+).start();
+
+ +

Before you hit the Start button, make sure to check the transform attribute value. The below tween will reset the element's transform attribute to original value when the animation is complete.

+
+ + + + +
+ Start +
+
+

This way we make sure to count the real current transform-origin and produce a consistent animation with the SVG coordinate system, just as the above example showcases.

+ +

Notes

+
    +
  • The SVG Transform component is successfuly handling all possible combinations of transform functions, and always uses same order of transform functions: translate, + rotate, skewX, skewY and scale to keep animation consistent and with same aspect as for CSS3 transforms on non-SVG elements.
  • +
  • Keep in mind that the SVG transform functionss will use the center of a shape as transform origin by default, contrary to the SVG draft.
  • +
  • Keep in mind the adjustments required for rotations, remember the .getBBox() method, it's really useful to set custom transform-origin.
  • +
  • By default browsers use overflow: hidden for <svg> so child elements are partialy/completely hidden while animating. You might want to set overflow: visible + or some browser specific tricks if that is the case.
  • +
  • When using viewBox="0 0 500 500" attribute for <svg> and no width and/or height attribute(s), means that you expect the SVG to be scalable and most + Internet Explorer versions simply don't work. You might want to check this tutorial.
  • +
  • In other cases when you need maximum control and precision or when shapes are already affected by translation, you might want to use the .fromTo() method with all proper values.
  • +
  • Also the svgTransform tween property does not support 3D transforms, because they are not supported in all SVG enabled browsers.
  • +
  • The SVG Transform component cannot work with the transformOrigin set to an SVGElement via CSS, you must always use a it's specific option.
  • +
  • The component can be combined with the HTML Attributes component to enable even more advanced/complex animations for SVG elements.
  • +
  • Keep in mind that older browsers like Internet Explorer 8 and below as well as stock browser from Android 4.3 and below do not support inline SVG + so make sure to fiter your SVG tweens properly.
  • +
  • While you can still use regular CSS3 transforms for SVG elements, everything is fine with Google Chrome, Opera and other webkit browsers, but older Firefox versions struggle with the percentage based + transformOrigin values and ALL Internet Explorer versions have no implementation for CSS3 transforms on SVG elements, which means that the SVG Transform component will become a fallback + component to handle legacy browsers in the future. Guess who's taking over :)
  • +
  • This component is bundled with the demo/src/kute-extra.js file.
  • + +
+ +
+ + + + +
+ + + + + + + + + + + + + + + + diff --git a/textProperties.html b/textProperties.html new file mode 100644 index 0000000..8408346 --- /dev/null +++ b/textProperties.html @@ -0,0 +1,158 @@ + + + + + + + + + + + + + + KUTE.js Text Properties + + + + + + + + + + + + + + +
+ + + +
+

Text Properties

+

The component that animates the typographic CSS properties of a target content Element on most browsers.

+
+ +
+
+
+
+

Overview

+

Animate the text size or spacing properties of a target text element.

+
+
+

The KUTE.js Text Properties component enables animation for typography related CSS properties of content element targets + on most browsers.

+

This small and simple component can be used to create various attention seekers for any content elements such as HTMLHeadingElement, + HTMLParagraphElement, HTMLUListElement or any other, as well as for entire content blocks.

+

You can either animate an entire string or content block or split your string into words or letters and create a simple animation with one or more of + the following properties:

+
+
+
+
+ +
+ +

Supported Properties

+ +
    +
  • fontSize allows you to animate the font-size for a target element.
  • +
  • lineHeight allows you to animate the line-height for a target element.
  • +
  • letterSpacing allows you to animate the letter-spacing for a target element.
  • +
  • wordSpacing allows you to animate the word-spacing for a target element.
  • +
+ +

Example

+ +
let tween1 = KUTE.to('selector1',{fontSize:'200%'})
+let tween2 = KUTE.to('selector1',{lineHeight:24})
+let tween3 = KUTE.to('selector1',{letterSpacing:50})
+let tween3 = KUTE.to('selector1',{wordSpacing:50})
+
+ +
+

Howdy!

+ +
+ +

Notes

+
    +
  • Be sure to check the textProperties.js for a more in-depth review of the above example.
  • +
  • Similar to box model properties, the text properties are also layout modifiers, they will push the layout around forcing unwanted re-paint work. To avoid re-paint, you + can use a fixed height for the target element's container, as we used in our example here, or set your text to position:absolute.
  • +
  • The component is only included in the demo/src/kute-extra.js file.
  • +
+ +
+ + + + +
+ + + + + + + + + + + + + + diff --git a/textWrite.html b/textWrite.html new file mode 100644 index 0000000..a02e592 --- /dev/null +++ b/textWrite.html @@ -0,0 +1,261 @@ + + + + + + + + + + + + + + KUTE.js TextWrite + + + + + + + + + + + + + + +
+ + + +
+

Text Write

+

The component that enables a unique animation by manipulating the string content of Element targets on most browsers.

+
+ +
+
+
+
+

Overview

+

Manipulate string contents with ease.

+
+
+

The KUTE.js Text Write component enables animation for content Element targets by manipulating their string contents.

+

The component provides two properties:

+
    +
  • number: NUMBER - interpolate the string numbers by increasing or decreasing their values
  • +
  • text: STRING - will add/remove a content string one character at a time
  • +
+

This text property comes with an additional tween option called textChars for the scrambling text character, with the following expected values:

+
    +
  • alpha use lowercase alphabetical characters, the default value
  • +
  • upper use UPPERCASE alphabetical characters
  • +
  • numeric use numerical characters
  • +
  • symbols use symbols such as #, $, %, etc.
  • +
  • all use all alpha numeric and symbols.
  • +
  • YOUR CUSTOM STRING use your own custom characters; eg: 'KUTE.JS IS #AWESOME'.
  • +
+

It's always best to wrap your target number in a <span id="uniqueID"> for the number property and content targets should be split into + multiple parts for the text property if the target has child contents, as we will see in the examples below.

+
+
+
+
+ + +
+ +

Examples

+ +

The effect of these two properties is very popular, so let's go through some quick examples to explain the workflow for the best possible outcome. We will try to focus + on the text property a bit more because we can optimize the content targets for a great visual experience.

+ +

Number Property

+

As discussed above, the target number need to be wrapped in a tag of choice with a unique ID so we can target it.

+ +
// the target number is wrapped in a <span> tag with a unique ID
+<p class="text-example">Total number of lines: <span id="myNumber">0</span></p>
+
+ +
// sample tween object with "number" property
+// this assumes it will start from current number which is different from 1550
+var myNumberTween = KUTE.to('#myNumber', {number: 1550}); 
+
+ +

The above should work like this:

+
+

Total number of lines: 0

+ +
+ Start +
+
+

The button action will toggle the valuesEnd value for the number property, because tweening a number to itself would produce no effect.

+ +

Text Property

+

Let's try a quick example and analyze the current outcome. Be aware that the example involves using child contents for the values, which is something we need to + handle in a special way to optimize the visual experience.

+ +
// sample tween object with "text" property
+var myTextTween = KUTE.to('selector', {text: 'A text string with <span>child content</span> should do.'});
+
+ +
+

Click the Start button on the right.

+
+ Start +
+
+ +

So targets with child elements don't animate very well it seems. We could probably split the starting content and end content into multiple parts, set a tween object + for each parth with delays and other settings, but Text Write component comes with a powerful utility you can use to ease your work in these instances.

+

The createTextTweens() utility will do it all for you: split text strings, set tween objects, but let's see some sample code:

+ +
// grab the parent of the content segments
+var textTarget = document.getElementById('textExample');
+
+// make a reversed array with its child contents
+var tweenObjects = KUTE.Util.createTextTweens(
+  textTarget,
+  'This text has a <a href="index.html">link to homepage</a> inside.',
+  options
+);
+
+// start whenever you want
+tweenObjects.start();
+
+ +

Now let's see how we doin:

+ +
+

Click the Start button on the right.

+
+ Start +
+
+ +

There are some considerations for the createTextTweens(target,newText,options) utility:

+
    +
  • The utility will replace the target content with <span> parts or the children's tagNames, then for the newText content will + create similar parts but empty. Also the number of the parts of the target content doesn't have to be the same as for the new content.
  • +
  • The utility returns an Array of tween objects, which is similar but independent from tweenCollection objects.
  • +
  • The returned Array itself has a start() "method" you can call on all the tweens inside.
  • +
  • The utility will assign playing: boolean property to the target content Element to prevent unwanted animation interruptions or glitches.
  • +
  • While you can set various tween options like easing, duration or the component specific textChars option, the delay option is handled + by the utility automatically.
  • +
  • The utility has a special handling for the duration tween option. You can either set a fixed duration like 1000, which isn't recommended, or auto + which will allow the utility the ability to determine a duration for each text part
  • +
  • When the animation of the last text part is complete, the target content Element.innerHTML will be set to the original un-split newText.
  • +
  • Using yoyo tween option is not recommended because it doesn't produce a desirable effect.
  • +
  • The utility will not work properly with targets that have a deeper structure than level 1, which means that for instance + <span>Text <span>sample</span></span> may not be processed properly.
  • +
+ +

Combining Both

+
+
+
+

0

+
+
+

Clicks so far

+
+
+
+ Start +
+
+

In this example we've used the textChars option with symbols and all values respectively, but combining the two text properties and some other KUTE.js + features can really spice up some content. Have fun!

+ +

Notes

+
    +
  • Keep in mind that the yoyo tween option will NOT un-write / delete the string character by character for the text property, but will write the previous text instead.
  • +
  • For a full code review, check out the ./assets/js/textWrite.js example source code.
  • +
  • The component is only included in the demo/src/kute-extra.js file.
  • +
+ +
+ + + + +
+ + + + + + + + + + + + + + diff --git a/transformFunctions.html b/transformFunctions.html new file mode 100644 index 0000000..73add18 --- /dev/null +++ b/transformFunctions.html @@ -0,0 +1,300 @@ + + + + + + + + + + + + + + + KUTE.js Transform Functions + + + + + + + + + + + + + +
+ + + +
+

Transform Functions

+

The component covers most important 2D and 3D transform functions as described in the W3 specification, + completelly reworked for improved performance and faster value processing.

+
+ +
+ +
+ +
+
+

Overview

+

The component to cover animation for most transform functions with improved performance and faster value processing.

+
+
+

The KUTE.js Transform Functions enables animation for the CSS3 transform style on Element targets on modern browsers. For specific legacy browsers there is another + component called Transform Legacy you will find in the source folders.

+

Starting with KUTE.js version 2.0, you can set the perspective function as a tween property, while you can still rely on a parent's perspective but for less performance.

+

All the previous perspective related options have been removed. The transform CSS3 property itself no longer uses preffixes like webkit, moz or ms since all major + browsers are standardized.

+

In comparison with previous versions, the component expects that input values are already px and deg based and no longer transforms % percent based values into px based or rad + based angles into deg based. This makes the execution faster and more consistent.

+

The component will stack all transform functions for translations, rotations and skews to shorthand functions to optimize performance and minimize value processing.

+
+
+
+
+ +
+ +

3D Transform

+
    +
  • perspective function creates a 3D perspective for a target element transformation. EG. perspective:400 to apply a 400px perspective. IE10+
  • +
  • translateX function is for horizontal translation. EG. translateX:150 to translate an element 150px to the right. IE10+
  • +
  • translateY function is for vertical translation. EG. translateY:-250 to translate an element 250px towards the top. IE10+
  • +
  • translateZ function is for translation on the Z axis in a given 3D field. EG. translateZ:-250 to translate an element 250px away from the viewer, making it + appear smaller. This function can be combined with the perspective function to take effect or the parent's perspective; the smaller perspective value, the deeper translation. IE10+
  • +
  • translate3d function is for movement on all axes in a given 3D field. EG. translate3d:[-150,200,150] to translate an element 150px to the left, 200px + to the bottom and 150px closer to the viewer, making it larger. When third value is used, it requires using a perspective. IE10+
  • +
  • rotateX function rotates an element on the X axis in a given 3D field. Eg. rotateX:250 will rotate an element clockwise by 250 degrees. Requires perspective. + IE10+
  • +
  • rotateY function rotates an element on the Y axis in a given 3D field. Eg. rotateY:-150 will rotate an element counter-clockwise by 150 degrees. + Requires perspective. IE10+
  • +
  • rotateZ function rotates an element on the Z axis and is the equivalent of the 2D rotation. Eg. rotateZ:-150 will rotate an element counter-clockwise by 150 degrees on X axis. + IE10+
  • +
  • rotate3d is a tween property, which combines the above rotateX, rotateY, and rotateZ functions and rotates an element on all axes. Obviously this is NOT + equivalent with the rotate3d(vectorX,vectorY,vectorZ,angle) shorthand function, this is only an optimization implemented for performance reasons and hopefully for your convenience. + Eg. rotate3d:[-150,80,90] will rotate an element counter-clockwise by 150 degrees on X axis, 80 degrees on Y axis and 90 degrees on Z axis. The X and Y axis require a perspective. + IE10+
  • +
  • matrix3d and scale3d functions are not supported by this component, but they are implemented in the transformMatrix component.
  • +
+ +

2D Transform

+
    +
  • translate function is for movement on X and Y axis. EG. translate:[-150,200] to translate an element 150px to the left, 200px to the bottom. IE9+
  • +
  • rotate function rotates an element on the Z axis. Eg. rotate:250 will rotate an element clockwise by 250 degrees. IE9+
  • +
  • skewX function will apply a shear on X axis to a target element. Eg. skewX:50 will skew a target element on X axis by 50 degrees. IE9+
  • +
  • skewY function will apply a shear on X axis to a target element. Eg. skewX:50 will skew a target element on X axis by 50 degrees. IE9+
  • +
  • skew function will apply a shear on both X and Y axes to a target element. Eg. skew:[50,50] will skew a target element on X axis by 50 degrees and 50 degrees on Y axis. + IE9+
  • +
  • scale function will scale a target element on all axes. Eg. scale:1.5 will scale up a target element by 50%. IE9+
  • +
  • matrix is not supported by this component, but is implemented in the transformMatrix component.
  • +
+

As a quick note, all input values for translate, rotate or single axis translation, skew or rotation will be all stacked into translate3d, + skew and rotate3d respectivelly; this is to further improve performance on modern browsers.

+ +

Translations

+ +
var tween1 = KUTE.fromTo('selector1',{translate:0},{translate:250}); // or translate:[x,y] for both axes
+var tween2 = KUTE.fromTo('selector2',{translateX:0},{translateX:-250});
+var tween3 = KUTE.fromTo('selector3',{translate3d:[0,0,0]},{translate3d:[0,250,0]});
+var tween4 = KUTE.fromTo('selector4',{perspective:400,translateY:0},{perspective:200,translateY:-100});
+
+ +
+
2D
+
X
+
Y
+
Z
+ +
+ Start +
+
+ +

As you can see in your browsers console, for all animations translate3d is used, as explained above. Also the first example that's using the 2D translate for both vertical + and horizontal axis even if we only set X axis.

+ +

Rotations

+ +
var tween1 = KUTE.fromTo('selector1',{rotate:0},{rotate:-720});
+var tween2 = KUTE.fromTo('selector2',{rotateX:0},{rotateX:200});
+var tween3 = KUTE.fromTo('selector3',{perspective:100,rotate3d:[0,0,0]},{perspective:100,rotate3d:[0,160,0]});
+var tween4 = KUTE.fromTo('selector4',{rotateZ:0},{rotateZ:360});
+
+ +
+
2D
+
X
+
Y
+
Z
+
+ Start +
+
+

The rotateX and rotateY are 3D based rotations, so they require a perspective in order to make the browser render proper 3D layers, but in the example they animate different because only the second, Y axis, uses the + perspective function. The rotation on Z axis does not require a perspective. Unlike translations, you can stack all axis rotation for your animation, but we will see that in a later example.

+ +

Skews

+ +
var tween1 = KUTE.fromTo('selector1',{skewX:0},{skewX:20});
+var tween2 = KUTE.fromTo('selector2',{skew:[0,0]},{skew:[0,45]});
+
+ +
+
X
+
Y
+ +
+ Start +
+
+ +

Mixed Transformations

+

The current specification does not support animating different transform properties with multiple tween objects at the same time, you must stack them all together into a single object. See the example below:

+ +
var tween1 = KUTE.fromTo('selector1',{rotateX:0},{rotateX:20}).start();
+var tween2 = KUTE.fromTo('selector1',{skewY:0},{skewY:45}).start();
+
+

If you check the test here, you will notice that only the skewY is going to work and no rotation. Now let's do this properly.

+ +
var tween1 = KUTE.fromTo(
+'selector1', // element
+{pespective:200,translateX:0, rotateX:0, rotateY:0, rotateZ:0}, // from
+{pespective:200,translateX:250, rotateX:360, rotateY:15, rotateZ:5} // to
+);
+var tween2 = KUTE.fromTo(
+'selector2', // element
+{translateX:0, rotateX:0, rotateY:0, rotateZ:0}, // from
+{translateX:-250, rotateX:360, rotateY:15, rotateZ:5} // to
+);
+
+ +
+
self perspective 200px
+
parent perspective 400px
+ +
+ Start +
+
+

Note in this example, the first tween object uses the element's perspective while the second relies on the parent's perspective.

+ +

Chained Transformations

+

KUTE.js has the ability to stack transform functions in a way to improve performance and optimize your workflow. In that idea the .to() method can be the right choice for most of your + animation needs and especially to link animations together because it has the ability to check the current values of the transform functions found in the element's inline styling, mostly from previous + tween animation, and use them as start values for the next animation. OK now, let's see a side by side comparison with 4 elements:

+ +
+
FROMTO
+
FROMTO
+
TO
+
TO
+ +
+ Start +
+
+

Observations

+
    +
  • The example hopefully demostrates what the animation looks like with different combinations of transform functions and how to combine them to optimize your workflow, size on disk as well as to how + to link animations together in a smooth continuous animation.
  • +
  • No matter the input values, the component will always stack translations into translate3d and rotations into rotate3d.
  • +
  • The first box uses a regular .fromTo() object, from point A to B and back to A, with the exact coordinates we want.
  • +
  • The second box is also using .fromTo() object, but using all values for all tweens at all times, so we gave the animation a sense of continuity.
  • +
  • The third box uses the .to() method, and will try and continue animation from last animation, but we "forgot" to keep track on the rotation of the X axis.
  • +
  • The last box also uses the .to() method, and uses all values and reproduce the animation of the second box exactly, but with nearly half the code.
  • +
+ +

Notes

+
    +
  • This demo page should work with IE10+ browsers.
  • +
  • The order of the transform functions/properties is always the same: perspective, translation, rotation, skew, scale, this way we can prevent + jumpy/janky animations; one of the reasons is consistency in all transform based components and another reason is the order of execution from the draft for + matrix3d recomposition.
  • +
  • Tests reveal that an element's own perspective produce better performance than using the parent's perspective. Also having both will severelly punish the animation performance, so keep that in mind + when you work on optimizing your code.
  • +
  • Use single axis transform functions like translateX when you want to animate the Y and Z axes back to ZERO, but in a convenient way.
  • +
  • Use shorthand functions like translate3d when you want to animate / keep multiple axes.
  • +
  • Shorthand functions like translate3d or rotate3d tween property generally not only improve performance, but will also minimize the code size. Eg. translateX:150, + translateY:200, translateZ:50 => translate3d:[150,200,50] is quite the difference.
  • +
  • On larger amount of elements animating chains, the .fromTo() method is fastest, and you will have more work to do as well, but will eliminate any delay / syncronization issue that may occur.
  • +
  • This component is bundled with the demo/src/kute-base.js and the standard kute.js distribution files.
  • +
+ +
+ + + + +
+ + + + + + + + + + + + + + + + diff --git a/transformMatrix.html b/transformMatrix.html new file mode 100644 index 0000000..7e07fd3 --- /dev/null +++ b/transformMatrix.html @@ -0,0 +1,214 @@ + + + + + + + + + + + + + + KUTE.js Transform Matrix + + + + + + + + + + + + +
+ + + +
+

Transform Matrix

+

The component covers all 3D transform functions and renders the update with either matrix() or matrix3d() functions, depending on the + functions used and their values. The notation is also fairly easy to use and familiar with other components.

+
+ +
+
+
+
+

Overview

+

The brand new component to enable complex transform animations of the future.

+
+
+

The KUTE.js Transform Matrix component covers animation for the CSS3 transform style on Element targets but with a different implementation.

+
    +
  • The notation is a little different because we have a different supported functions/properties set, and the value processing function needs to differentiate the two components.
  • +
  • The 2D transform functions like rotate, translate or scale have been dropped to enable faster value processing and improved performance. The component is + geared towards the future of web development with this very specific purpose in mind.
  • +
  • Most importantly we have the update function which implements the DOMMatrix() API for smooth animation at no + performance cost, which is different from other libraries that use a webkitCSSMatrix polyfill and lose performance.
  • +
  • The script is robust, simple and light in size. It's simply a matter of taste when choosing between the two transform components.
  • +
+

The component was developed for more complex transform animations and comes with additional supported transform functions. According to the W3 draft, the DOMMatrix API will merge the + fallback webkitCSSMatrix API and the SVGMatrix API together, so awesome is yet to come.

+

Due to execution complexity and other performance considerations, and similar to the Transform Functions component, this component provides support for a custom + rotate3d[X,Y,Z] tween property which is different from CSS3 standard rotate3d(x,y,z,Angle) shorthand function.

+

In certain situations you can also use functions like scaleX, rotateY or translateZ for convenience, but the component will always stack translations + into translate3d, all scale axes into scale3d, all rotations into rotate3d and both skews into skew.

+
+
+
+
+ +
+ +

3D Transform

+
    +
  • perspective function creates a 3D perspective for a target element transformation. EG. perspective:400 to apply a 400px perspective.
  • +
  • translateX function is for horizontal translation. EG. translateX:150 to translate an element 150px to the right.
  • +
  • translateY function is for vertical translation. EG. translateY:-250 to translate an element 250px towards the top.
  • +
  • translateZ function is for translation on the Z axis in a given 3D field. EG. translateZ:-250 to translate an element 250px away from the viewer, making it + appear smaller. This function can be combined with the perspective function to take effect or the parent's perspective; the smaller perspective value, the deeper translation.
  • +
  • translate3d shorthand function is for translation on all the axes in a given 3D field. EG. translate3d:[-150,200,150] to translate an element 150px to the left, 200px + to the bottom and 150px closer to the viewer, making it larger. When third value is used, it requires using a perspective.
  • +
  • rotateX function rotates an element on the X axis in a given 3D field. Eg. rotateX:250 will rotate an element clockwise by 250 degrees on X axis. + Requires perspective.
  • +
  • rotateY function rotates an element on the Y axis in a given 3D field. Eg. rotateY:-150 will rotate an element counter-clockwise by 150 degrees on Y axis. + Requires perspective.
  • +
  • rotateZ function rotates an element on the Z axis and is the equivalent of the 2D rotation. Eg. rotateZ:90 will rotate an element clockwise by 90 degrees on Z axis.
  • +
  • rotate3d is a tween property, which combines the above rotateX, rotateY, and rotateZ functions and rotates an element on all axes. + Eg. rotate3d:[250,-150,90] will produce the same effect as the three above combined. The X and Y axes require a perspective.
  • +
  • skewX function will apply a shear to a target element on the X axis. Eg. skewX:50 will skew the element by 50 degrees on X axis.
  • +
  • skewY function will apply a shear to a target element on the Y axis. Eg. skewY:-50 will skew the element by -50 degrees on Y axis.
  • +
  • skew shorthand function will apply a shear on both X and Y axes to a target element. Eg. skew:[50,50] will skew a target element on X axis by 50 degrees and 50 degrees on Y axis.
  • +
  • scaleX function will scale the X axis of a target element. Eg. scaleX:1.5 will increase the scale of a target element on the X axis by 50%.
  • +
  • scaleY function will scale the Y axis of a target element. Eg. scaleY:0.5 will decrease the scale of a target element on the Y axis by 50%.
  • +
  • scaleZ function will scale the Z axis of a target element. Eg. scaleZ:0.75 will decrease the scale of a target element on the Z axis by 25%.
  • +
  • scale3d function will scale a target element on all axes. Eg. scale3d:[1.5,0.5,0.75] will produce the same effect as the 3 above combined.
  • +
  • matrix and matrix3d are not supported CSS3 transform functions or tween properties, but the results of the update function.
  • +
+ +

Example

+

Now let's have a look at the notation and a quick example:

+ +
let mx1 = KUTE.to('el1', { transform: { translate3d:[-50,-50,-50]} })
+let mx2 = KUTE.to('el2', { transform: { perspective: 100, translateX: -50, rotateX:-180} })
+let mx3 = KUTE.to('el3', { transform: { translate3d:[-50,-50,-50], skew:[-15,-15]} })
+let mx4 = KUTE.to('el4', { transform: { translate3d:[-50,-50,-50], rotate3d:[0,-360,0], scaleX:0.5 } })
+
+ +
+
MX1
+
MX2
+
MX3
+
MX4
+ +
+ Start +
+
+

So the second element uses it's own perspective but notice that the parent perspective also apply. This case must be avoided in order to keep performance in check: one perspective is best.

+ +

Chained Transformations

+

Similar to the other component the Transform Matrix component will produce the same visual experience, but with the matrix3d function.

+
+
FROMTO
+
FROMTO
+
TO
+
TO
+ +
+ Start +
+
+ +

Notes

+
    +
  • This demo page should work with IE10+ browsers.
  • +
  • Why no support for the matrix3d function? Simple: we can of course interpolate 16 numbers super fast, but we won't be able to rotate on any axis above 360 degrees. + As explained here, surely for performance reasons the browsers won't try and compute angles above 360 degrees, but simplify + the number crunching because a 370 degree rotation is visualy identical with a 10 degree rotation.
  • +
  • The component does NOT include any scripting for matrix decomposition/unmatrix, after several years of research I came to the conclusion that there is no such thing to be reliable. + Such a feature would allow us to kick start animations directly from matrix3d string/array values, but considering the size of this component, I let you draw the conclusions.
  • +
  • Despite the "limitations", we have some tricks available: the fromTo() method will never fail and it's much better when performance and sync are a must, and for to() + method we're storing the values from previous animations to have them ready and available for the next chained animation.
  • +
  • The performance of this component is identical with the Transform Functions component, which is crazy and awesome, all that thanks to the DOMMatrix API built into our modern browsers. + If that's not good enough, the API will automatically switch from matrix3d to matrix and vice-versa whenever needed to save power. Neat?
  • +
  • The rotate3d property makes alot more sense for this component since the DOMMatrix rotate(angleX,angleY,angleZ) method works exactly the same, while the + rotate3d(vectorX,vectorY,vectorZ,angle) function is a thing of the past, according to Chris Coyier nobody use it.
  • +
  • Since the component fully utilize the DOMMatrix API, with fallback to each browser compatible API, some browsers still have in 2020 an incomplete CSS3Matrix API, for instance privacy browsers + like Tor won't work with rotations properly for some reason, other proprietary mobile browsers may suffer from same symptoms. In these cases a correction polyfill is required.
  • +
  • This component is bundled with the demo/src/kute-extra.js distribution file.
  • +
+
+ + + + +
+ + + + + + + + + + + + + +