376 lines
22 KiB
HTML
376 lines
22 KiB
HTML
<!DOCTYPE html>
|
|
<!--[if IE 7]><html class="ie ie7" lang="en"><![endif]-->
|
|
<!--[if IE 8]><html class="ie ie8" lang="en"><![endif]-->
|
|
<!--[if IE 9]><html class="ie ie9" lang="en"><![endif]-->
|
|
|
|
<!--[if !IE ]><!-->
|
|
<html lang="en">
|
|
<!--<![endif]-->
|
|
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1.0">
|
|
<meta name="description" content="A detailed guide how to extend or customize the functionality KUTE.js, with an example for a box-shadow plugin.">
|
|
<meta name="keywords" content="kute,kute.js,animation,javascript animation,tweening engine,animation engine,extend,box-shadow animation,Javascript,Native Javascript,vanilla javascript,jQuery">
|
|
<meta name="author" content="dnp_theme">
|
|
<link rel="shortcut icon" href="./assets/img/favicon.png">
|
|
|
|
<title>Extending KUTE.js | Javascript Animation Engine</title>
|
|
|
|
<!-- RESET CSS -->
|
|
<link type="text/css" href="./assets/css/reset.css" rel="stylesheet">
|
|
|
|
<!-- DEMO KUTE CSS -->
|
|
<link type="text/css" href="./assets/css/kute.css" rel="stylesheet">
|
|
|
|
<!-- Ion Icons -->
|
|
<link type="text/css" href="http://code.ionicframework.com/ionicons/2.0.1/css/ionicons.min.css" rel="stylesheet">
|
|
|
|
<!-- Synthax highlighter -->
|
|
<link href="./assets/css/prism.css" rel="stylesheet">
|
|
|
|
<!-- Polyfill -->
|
|
<script src="./assets/js/minifill.js"></script>
|
|
<!--[if lt IE 9]>
|
|
<script src="https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script>
|
|
<![endif]-->
|
|
|
|
<!-- legacy browsers support via polyfill, you must remove the above when used
|
|
<script src="https://cdn.polyfill.io/v2/polyfill.js?features=default,getComputedStyle|gated"> </script> -->
|
|
|
|
</head>
|
|
|
|
<body>
|
|
<div class="site-wrapper">
|
|
|
|
<div class="navbar-wrapper">
|
|
<div class="content-wrap">
|
|
<a href="index.html"><h1>KUTE.<span>js</span></h1></a>
|
|
<ul class="nav">
|
|
<li class="btn-group"><a href="#" data-function="toggle">Features <span class="caret"></span></a>
|
|
<ul class="subnav">
|
|
<li><a href="features.html">Feature Overview</a></li>
|
|
<li><a href="properties.html">Supported Properties</a></li>
|
|
</ul>
|
|
</li>
|
|
<li class="btn-group">
|
|
<a href="#" data-function="toggle">Examples <span class="caret"></span></a>
|
|
<ul class="subnav">
|
|
<li><a href="examples.html">Core Engine</a></li>
|
|
<li><a href="css.html">CSS Plugin </a></li>
|
|
<li><a href="svg.html">SVG Plugin </a></li>
|
|
<li><a href="text.html">Text Plugin </a></li>
|
|
<li><a href="attr.html">Attributes Plugin </a></li>
|
|
</ul>
|
|
</li>
|
|
<li class="btn-group active">
|
|
<a href="#" data-function="toggle">API <span class="caret"></span></a>
|
|
<ul class="subnav">
|
|
<li><a href="start.html">Getting Started</a></li>
|
|
<li><a href="api.html">Public Methods</a></li>
|
|
<li><a href="options.html">Tween Options</a></li>
|
|
<li><a href="easing.html">Easing Functions</a></li>
|
|
<li class="active"><a href="extend.html">Extend Guide</a></li>
|
|
</ul>
|
|
</li>
|
|
<li><a href="about.html">About</a></li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="content-wrap">
|
|
|
|
<h2>Extend Guide</h2>
|
|
<p>KUTE.js is a very flexible animation engine that allows you to extend beyond measure. In this tutorial we'll dig into what's best to do to extend/customize the functionality of KUTE.js core, plugins and features.</p>
|
|
|
|
<h3>Basic Plugin Template</h3>
|
|
<p>The best way to extend, no matter what you would like to achieve is to use a specific closure, here's an example:</p>
|
|
<pre><code class="language-javascript">/* KUTE.js - The Light Tweening Engine
|
|
* by dnp_theme
|
|
* package - pluginName
|
|
* desc - what your plugin does
|
|
* pluginName by yourNickname aka YOUR NAME
|
|
* Licensed under MIT-License
|
|
*/
|
|
|
|
(function (root,factory) {
|
|
if (typeof define === 'function' && define.amd) {
|
|
define(['kute.js'], factory);
|
|
} else if(typeof module == 'object' && typeof require == 'function') {
|
|
module.exports = factory(require('kute.js'));
|
|
} else if ( typeof root.KUTE !== 'undefined' ) {
|
|
factory(root.KUTE);
|
|
} else {
|
|
throw new Error("pluginName require KUTE.js.");
|
|
}
|
|
}(this, function (KUTE) {
|
|
// your code goes here
|
|
|
|
// in this function body
|
|
|
|
// the plugin returns this
|
|
return this;
|
|
}));
|
|
</code></pre>
|
|
<p>As suggested in the above template, your function body could be written with one or more of the examples below.</p>
|
|
|
|
<h3>Extend Tween Control</h3>
|
|
<p>In some cases, you may want to extend with additional tween control methods, so before you do that, make sure to check <code>Tween</code> function to get the internal references notation:</p>
|
|
<pre><code class="language-javascript">//add a reference to _tween function
|
|
var Tween = KUTE.Tween;
|
|
|
|
// let's add a timescale method
|
|
Tween.prototype.timescale = function(factor){
|
|
this.options.duration *= factor;
|
|
return this;
|
|
}
|
|
|
|
// or let's add a reverse method
|
|
Tween.prototype.reverse = function(){
|
|
for (var p in this.valuesEnd) {
|
|
var tmp = this.valuesRepeat[p]; // we cache this object first
|
|
this.valuesRepeat[p] = this.valuesEnd[p];
|
|
this.valuesEnd[p] = tmp;
|
|
this.valuesStart[p] = this.valuesRepeat[p];
|
|
}
|
|
return this;
|
|
}
|
|
|
|
// go back in time
|
|
Tween.prototype.seek = function (time) {
|
|
this._startTime -= time;
|
|
return this;
|
|
};
|
|
|
|
// how about a restart method
|
|
Tween.prototype.restart = function(){
|
|
if (this.playing) {
|
|
this.stop();
|
|
this.start();
|
|
}
|
|
return this;
|
|
}
|
|
|
|
// methods to queue callbacks with ease
|
|
Tween.prototype.onUpdate = function(){
|
|
this.options.update = arguments;
|
|
return this;
|
|
}
|
|
|
|
</code></pre>
|
|
<p>For some reasons these methods aren't included into the core/plugins by default, but let you decide what you need and how to customize the animation engine for your very specific need.</p>
|
|
|
|
<h3>Support For Additional CSS Properties</h3>
|
|
<p>KUTE.js core engine and plugins cover what I consider to be most essential for animation, but you may have a different opinion. In case you may want to know how to animate properties that are not currently supported, stick to this guide and
|
|
you'll master it real quick, it's very easy.</p>
|
|
<p>We need basically 3 functions:</p>
|
|
<ul>
|
|
<li><code>KUTE.prepareStart['propertyName']</code> <kbd class="bg-red">required</kbd> a function to get the current value of the property required for the <code>.to()</code> method;</li>
|
|
<li><code>KUTE.parseProperty['propertyName']</code> <kbd class="bg-red">required</kbd> a function to process the user value / current value to have it ready to tween;</li>
|
|
<li><code>KUTE.dom['propertyName']</code> <kbd class="bg-red">required</kbd> a <strong>domUpdate</strong> function that will update the property value into the DOM;</li>
|
|
<li><code>KUTE.crossCheck['propertyName']</code> <kbd class="bg-green">optional</kbd> a function to help you set proper values when for instance startValues unit is different than endValues unit; so far this is used for CSS3/SVG transforms
|
|
and SVG Morph, but it can be extended for many properties such as box-model properties or border-radius properties;</li>
|
|
<li>also <kbd class="bg-green">optional</kbd> additional functions that will help with value processing.</li>
|
|
</ul>
|
|
<p>So let's add support for <kbd class="bg-olive">boxShadow</kbd>! It should be a medium difficulty guide most developers can follow and the purpose of this guide is to showcase how easy it actually is to extend KUTE.js. So grab the above template
|
|
and let's break it down to pieces:</p>
|
|
<pre><code class="language-javascript">// add a reference to global and KUTE object
|
|
var g = typeof global !== 'undefined' ? global : window, K = KUTE,
|
|
// add a reference to KUTE utilities
|
|
prepareStart = K.prepareStart, getCurrentStyle = K.getCurrentStyle,
|
|
property = K.property, parseProperty = K.parseProperty, trueColor = K.truC,
|
|
DOM = K.dom, color = g.Interpolate.color, unit = g.Interpolate.unit; // interpolation functions
|
|
|
|
// the preffixed boxShadow property, mostly for legacy browsers
|
|
// maybe the browser is supporting the property with its vendor preffix
|
|
// box-shadow: none|h-shadow v-shadow blur spread color |inset|initial|inherit;
|
|
var _boxShadow = property('boxShadow'); // note we're using the KUTE.property() autopreffix utility
|
|
var colRegEx = /(\s?(?:#(?:[\da-f]{3}){1,2}|rgba?\(\d{1,3},\s*\d{1,3},\s*\d{1,3}\))\s?)/gi; // a full RegEx for color strings
|
|
|
|
// for browsers that don't support the property, use a filter
|
|
// if (!(_boxShadow in document.body.style)) {return;}
|
|
</code></pre>
|
|
<p>Now we have access to the KUTE object, prototypes and it's utility functions, let's write a <code>prepareStart</code> function that will read the current <code>boxShadow</code> value:</p>
|
|
<pre><code class="language-javascript">// for the .to() method, you need to prepareStart the boxShadow property
|
|
// which means you need to read the current computed value
|
|
// if the current computed value is not acceptable, use a default value
|
|
prepareStart['boxShadow'] = function(property,value){
|
|
var cssBoxShadow = getCurrentStyle(this.element,'boxShadow'); // where getCurrentStyle() is an accurate method to read the current property value
|
|
return /^none$|^initial$|^inherit$|^inset$/.test(cssBoxShadow) ? '0px 0px 0px 0px rgb(0,0,0)' : cssBoxShadow; // if the current value is not valid, use a default one
|
|
}
|
|
|
|
// note that in some cases the window.getComputedStyle(this.element,null) can be faster or more appropriate
|
|
// we are using a hybrid function that's trying to get proper colors and other stuff
|
|
// some legacy browsers lack in matters of accuracy so the KUTE.js core methods would suffice
|
|
|
|
// also to read the current value of an attribute, replace first line of the above function body with this
|
|
// var attrValue = element.getAttribute(property);
|
|
// and return the value or a default value, mostly rgb(0,0,0) for colors, 1 for opacity, or 0 for most other types
|
|
</code></pre>
|
|
|
|
<p>Since KUTE.js 1.6.0, the tween object is bound to all property parsing utilities, meaning that you have access to <code>this</code> that has the target element, options, start and end values and everything else. This is extremely useful if
|
|
you want to optimize and/or extend particular properties values, the dom update functions, and even override the property name</p>
|
|
|
|
<p>Now we need an utility function that makes sure the structure looks right for the DOM update function.</p>
|
|
<pre><code class="language-javascript">// utility function to process values accordingly
|
|
// numbers must be floats/integers and color must be rgb object
|
|
var processBoxShadowArray = function(shadow){
|
|
var newShadow;
|
|
// properly process the shadow based on amount of values
|
|
if (shadow.length === 3) { // [h-shadow, v-shadow, color]
|
|
newShadow = [shadow[0], shadow[1], 0, 0, shadow[2], 'none'];
|
|
} else if (shadow.length === 4) { // [h-shadow, v-shadow, color, inset] | [h-shadow, v-shadow, blur, color]
|
|
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'];
|
|
} else if (shadow.length === 5) { // [h-shadow, v-shadow, blur, color, inset] | [h-shadow, v-shadow, blur, spread, color]
|
|
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'];
|
|
} else if (shadow.length === 6) { // ideal [h-shadow, v-shadow, blur, spread, color, inset]
|
|
newShadow = shadow;
|
|
}
|
|
|
|
// make sure the numbers are ready to tween
|
|
for (var i=0; i<4; i++){
|
|
newShadow[i] = parseFloat(newShadow[i]);
|
|
}
|
|
// make sure color is an rgb object
|
|
newShadow[4] = trueColor(newShadow[4]); // where K.truC()/trueColor is a core method to return the true color in rgb object format
|
|
return newShadow;
|
|
};
|
|
</code></pre>
|
|
|
|
<p>Next we'll need to write a <code>parseProperty</code> function that will prepare the property value and build an Object or Array of values ready to tween. This function also registers the <code>KUTE.dom['boxShadow']</code> function into the
|
|
KUTE object, and this way we avoid filling the main object with unnecessary functions, just to keep performance tight.</p>
|
|
|
|
<pre><code class="language-javascript">// the parseProperty for boxShadow
|
|
// registers the window.dom['boxShadow'] function
|
|
// returns an array of 6 values in the following format
|
|
// [horizontal, vertical, blur, spread, color: {r:0,g:0,b:0}, inset]
|
|
parseProperty['boxShadow'] = function(property,value,element){
|
|
// the DOM update function for boxShadow registers here
|
|
// we only enqueue it if the boxShadow property is used to tween
|
|
if ( !('boxShadow' in DOM) ) {
|
|
DOM['boxShadow'] = function(element,property,startValue,endValue,progress) { // element, propertyName, valuesStart.boxShadow, valuesEnd.boxShadow, progress
|
|
|
|
// let's start with the numbers | set unit | also determine inset
|
|
var numbers = [], px = 'px', // the unit is always px
|
|
inset = startValue[5] !== 'none' || endValue[5] !== 'none' ? ' inset' : false;
|
|
|
|
for (var i=0; i<4; i++){ // for boxShadow coordinates we do the math for an array of numbers
|
|
numbers.push( unit(startValue[i], endValue[i], px, progress) );
|
|
}
|
|
|
|
// now we handle the color
|
|
var colorValue = color(startValue[4],endValue[4],progress);
|
|
|
|
// last piece of the puzzle, the DOM update
|
|
element.style[_boxShadow] = inset ? colorValue + numbers.join(' ') + inset : colorValue + numbers.join(' ');
|
|
};
|
|
}
|
|
|
|
// processProperty for boxShadow, builds basic structure with ready to tween values
|
|
if (typeof value === 'string'){
|
|
var shadowColor, inset = 'none';
|
|
// 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"
|
|
shadowColor = value.match(colRegEx);
|
|
value = value.replace(shadowColor[0],'').split(' ').concat([shadowColor[0].replace(/\s/g,'')],[inset]);
|
|
|
|
// now we can use the above specific utitlity
|
|
value = processBoxShadowArray(value);
|
|
} else if (value instanceof Array){
|
|
value = processBoxShadowArray(value);
|
|
}
|
|
return value;
|
|
}
|
|
</code></pre>
|
|
|
|
<p>And now, we are ready to tween both <code>.to()</code> and <code>.fromTo()</code> methods:</p>
|
|
<pre><code class="language-javascript">// tween to a string value
|
|
var myBSTween1 = KUTE.to('selector', {boxShadow: '15px 25px #069'}).start();
|
|
|
|
// or a fromTo with string and array, hex and rgb
|
|
var myBSTween2 = KUTE.fromTo('selector', {boxShadow: [15, 25, 0, '#069']}, {boxShadow: '0px 0px rgb(0,0,0)'}).start();
|
|
|
|
// maybe you want to animate an inset boxShadow?
|
|
var myBSTween3 = KUTE.fromTo('selector', {boxShadow: [5, 5, 0, '#069', 'inset']}, {boxShadow: '0px 0px rgb(0,0,0)'}).start();
|
|
</code></pre>
|
|
<p>You are now ready to demo!</p>
|
|
<div id="boxShadow" class="featurettes">
|
|
<div class="example-item example-box bg-lime"></div>
|
|
|
|
<div class="example-buttons">
|
|
<a class="btn btn-pink" href="javascript:void(0)">Start</a>
|
|
</div>
|
|
</div>
|
|
<p>This plugin should be compatible with IE9+ and anything that supports <code>boxShadow</code> CSS property. As you can see it can handle pretty much anything you throw at it, but it requires at least 3 values: h-shadow, v-shadow, and color
|
|
because Safari doesn't work without a color. Also this plugin won't be able to handle multiple instances of <code>boxShadow</code> for same element, because the lack of support on legacy browsers, also the color cannot be RGBA, but hey,
|
|
it supports both outline and inset shadows and you can fork it anyway to your liking.</p>
|
|
<p>If you liked this tutorial, feel free to write your own, a great idea would be for <code>textShadow</code>, it's very similar to the above example plugin.</p>
|
|
|
|
<h3>Utility Methods</h3>
|
|
<ul>
|
|
<li><kbd class="bg-lime">KUTE.selector(selector,multi)</kbd> is the selector utility that uses <code>getElementById</code> or <code>querySelector</code> when <code>multi</code> argument is <strong>null</strong> or <strong>false</strong>, BUT
|
|
when <strong>true</strong>, <code>querySelectorAll</code> is used and returns a HTMLCollection object.</li>
|
|
<li><kbd class="bg-lime">KUTE.property(propertyName)</kbd> is the <strong>autoPrefix</strong> function that returns the property with the right vendor prefix, but only if required; on legacy browsers that don't support the property, the function
|
|
returns <strong>undefinedPropertyName</strong> and that would be an easy way to detect support for that property on most legacy browsers: <pre><code class="language-javascript">if (/undefined/.test(KUTE.property('propertyName')) ) { /* legacy browsers */ } else { /* modern browsers */ }</code></pre></li>
|
|
<li><kbd class="bg-lime">KUTE.getPrefix()</kbd> returns a vendor preffix even if the browser supports a specific preffixed property or not.</li>
|
|
<li><kbd class="bg-lime">KUTE.getCurrentStyle(element,property)</kbd> a hybrid <code>getComputedStyle</code> function to get the current value of the property required for the <code>.to()</code> method; it actually checks in <code>element.style</code>,
|
|
<code>element.currentStyle</code> and <code>window.getComputedStyle(element,null)</code> to make sure it won't miss the property value;</li>
|
|
<li><kbd class="bg-lime">KUTE.truD(value)</kbd> a function that accepts String and Number and returns a <code>{v: 150, u: 'px'}</code> object for any box model or a single numeric value based property and make it ready to tween. When a second
|
|
parameter is set to <i>true</i> it will return an object with value and unit specific for rotation angles and skews.</li>
|
|
<li><kbd class="bg-lime">KUTE.truC(color)</kbd> a function that returns an <code>{r: 150, g: 150, b: 0}</code> color object ready to tween; if the color value is a <a href="http://www.w3schools.com/colors/colors_names.asp" target="_blank">web safe color</a>,
|
|
the IE9+ browsers will be able to return the rgb object we need.</li>
|
|
<li><kbd class="bg-lime">KUTE.htr(hex)</kbd> a function that accepts HEX formatted colors and returns an <code>{r: 150, g: 150, b: 0}</code> color object;</li>
|
|
<li><kbd class="bg-lime">KUTE.rth(r,g,b)</kbd> a function that accepts numeric values for red, blue and green and returns a HEX format <code>#006699</code> color string.</li>
|
|
<li><kbd class="bg-lime">Interpolate.number</kbd> is most essential interpolation tool when developing plugins for various properties not supported in the core.</li>
|
|
<li><kbd class="bg-lime">Interpolate.unit</kbd> is used mainly for box model properties, text properties, and generally anything that's a string based valued. Like <code>width: 250px</code></li>
|
|
<li><kbd class="bg-lime">Interpolate.color</kbd> is a very fast interpolation function for colors, as used in the above example.</li>
|
|
<li><kbd class="bg-lime">Interpolate.coords</kbd> is SVG Plugin only, but you can have a look anytime when you're out of ideas.</li>
|
|
</ul>
|
|
|
|
<ul id="share" class="nav">
|
|
<li>Share </li>
|
|
<li class="hidden-xs"><a target="_blank" href="https://www.facebook.com/sharer/sharer.php?u=http://thednp.github.io/kute.js/index.html" title="Share KUTE.js on Facebook"><span class="ion-social-facebook-outline icon"></span></a></li>
|
|
<li class="hidden-xs"><a target="_blank" href="https://twitter.com/home?status=Spread the word about %23KUTEJS animation engine by @dnp_theme and download here http://thednp.github.io/kute.js/index.html" title="Share KUTE.js on Twitter"><span class="icon ion-social-twitter-outline"></span></a></li>
|
|
<li class="hidden-xs"><a target="_blank" href="https://plus.google.com/share?url=http://thednp.github.io/kute.js/index.html" title="Share KUTE.js on Google+"><span class="icon ion-social-googleplus-outline"></span></a></li>
|
|
</ul>
|
|
|
|
</div>
|
|
|
|
<!-- FOOTER -->
|
|
<footer>
|
|
<div class="content-wrap">
|
|
<p class="pull-right"><a id="toTop" href="#">Back to top</a></p>
|
|
<p>© 2007 - 2016 · <a href="http://themeforest.net/user/dnp_theme?ref=dnp_theme">dnp_theme</a>.</p>
|
|
</div>
|
|
</footer>
|
|
|
|
</div>
|
|
<!-- /.site-wrapper -->
|
|
|
|
|
|
|
|
<!-- JavaScript
|
|
================================================== -->
|
|
<!-- Placed at the end of the document so the pages load faster -->
|
|
|
|
<!-- highlighter -->
|
|
<script src="./assets/js/prism.js" type="text/javascript"></script>
|
|
|
|
<!--<script src="http://cdn.jsdelivr.net/kute.js/1.6.0/kute.min.js"></script> KUTE CDN -->
|
|
|
|
<script src="./src/kute.min.js"></script>
|
|
<!-- KUTE.js core -->
|
|
<script src="./assets/js/scripts.js"></script>
|
|
<!-- some stuff -->
|
|
<script src="./assets/js/kute-bs.js"></script>
|
|
<!-- the boxShadow plugin -->
|
|
<script src="./assets/js/extend.js"></script>
|
|
<!-- examples with boxShadow plugin -->
|
|
</body>
|
|
|
|
</html>
|