├── .gitignore ├── CHANGELOG.md ├── GPL-LICENSE.txt ├── MIT-LICENSE.txt ├── README.md ├── bower.json ├── jquery.easing.1.3.js ├── jquery.quicksand.js ├── jquery.transform2d.js └── package.json /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | mydemos/ 3 | /jquery.quicksand.*.js 4 | *.diff -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | jQuery Quicksand plugin release notes 2 | ===================================== 3 | 4 | v1.6.0 5 | ------ 6 | 7 | - added bower.json file 8 | - *breaking change*: changed the way `adjustWidth` and `adjustHeight` 9 | settings work. `false` leaves these values alone (useful on responsive 10 | pages), `'call'` sets it before or after the call (determined 11 | automatically), `'auto'` sets them to `auto` 12 | 13 | v1.5.0 14 | ------ 15 | 16 | - added package.json file and published the library as npm package 17 | - the plugin is now exported using Universal Module Definition 18 | - *potentially breaking*: modernized approach to CSS3 scaling, 19 | we're now using a different dependency: 20 | (jquery.transform2d.js)[jquery.transform2d.js] 21 | - improved jQuery compatibility, we're now compatible with 1.6.1+, 22 | 2.0+, 3.0+ 23 | - Quicksand's cloning now includes all event handlers, making it effortless 24 | to integrate with other enhancement plugins. `enhancement` parameter should 25 | no longer be needed in most cases 26 | - fixed `this` scoping issue in callback function 27 | 28 | This release was meda possible by: 29 | 30 | - [liamim](https://github.com/liamim) 31 | - [seethroughtrees](https://github.com/seethroughtrees) 32 | - [seanhussey](https://github.com/seanhussey) 33 | - [Connum](https://github.com/Connum) 34 | 35 | v1.4.0 36 | ------ 37 | 38 | - detect IE versions more reliably so that we don't skip animation for 39 | capable IE versions 40 | - added support for animating canvas elements 41 | - replaced browser detection with feature detection for CSS3 scaling 42 | - improved compatibility so that the plugin works with jQuery 1.3+, 43 | including 1.10 and 2.0 44 | - fixed transform animation not animating in 45 | 46 | This release was meda possible by: 47 | [`kachkaev":https://github.com/kachkaev, "`awshout](https://github.com/awshout), 48 | [`Shotster":https://github.com/Shotster, "`iblamefish](https://github.com/iblamefish) 49 | 50 | v1.3.0 51 | ------ 52 | 53 | - added `maxWidth` option 54 | - added `atomic` option 55 | - added `adjustWidth` option 56 | - tweaked the plugin for better compatibility with responsive designs 57 | - made the plugin re-use existing DOM elements instead of replacing 58 | them with new collection 59 | - added Internet Explorer 10 and Windows 8 tweaks 60 | - minor bug fixes and improvements 61 | 62 | This release was made possible by: 63 | [`arlm":https://github.com/arlm, "`databyte](https://github.com/databyte), 64 | [`dalyons":https://github.com/dalyons, "`beedesk](https://github.com/beedesk), 65 | [`kiwiupover":https://github.com/kiwiupover and "`JonMcL](https://github.com/JonMcL). 66 | 67 | v1.2.2 68 | ------ 69 | 70 | \- fixed Internet Explorer NaN issue\ 71 | - fixed blinking in webkit-based browsers caused by callback race 72 | conditions 73 | 74 | v1.2.1 75 | ------ 76 | 77 | \- added `enhancement` option, making it possible to easily integrate 78 | Quicksand with visual enhancements such as font replacement 79 | 80 | v1.2 81 | ---- 82 | 83 | \- replacing jQuery .css calls with raw JavaScript .style and removing 84 | temporary container improved performance (15--40 faster) when preparing 85 | the animation 86 | 87 | \- solved initial animation slowdowns 88 | 89 | \- added custom attribute functions: instead of providing HTML attribute 90 | with unique string, you can specify a function to extract the value: 91 | \$('\#source').quicksand('\#dest li', {attribute: function(v) { return 92 | \$(v).find('img').attr('src')} }); please note that although looks 93 | handy, it could be a bit more laggy 94 | 95 | \- added useScaling option so that you can disable scaling animation for 96 | performance reasons (helps a lot on slower machines) 97 | 98 | \- extended adjustHeight capabilities: from now you can set it to 99 | dynamic, auto or false\ 100 | - greatly improved smoothness of the animation 101 | 102 | v1.1 103 | ---- 104 | 105 | \- Fixed issues with position: relative 106 | 107 | \- Fixed occasional flickering 108 | 109 | \- Moderate performance improvements 110 | 111 | \- Disabled any animation in IE6 for more safety\ 112 | - Added support for jQuery 1.3 113 | 114 | v1.0 115 | ---- 116 | 117 | First, feature-complete release of jQuery Quicksand plugin. 118 | -------------------------------------------------------------------------------- /GPL-LICENSE.txt: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc. 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | -------------------------------------------------------------------------------- /MIT-LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2010 Jacek Galanciak, https://razorjack.net 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Quicksand 2 | ========= 3 | 4 | jQuery plugin for reordering and filtering items with a nice shuffling 5 | animation like the one [in this 6 | video](https://viddler.com/v/420ee8e4). 7 | 8 | Project site: . 9 | 10 | Demos & docs 11 | ------------ 12 | 13 | Please visit [the project site](https://razorjack.net/quicksand) for live 14 | demos & documentation. 15 | 16 | Usage 17 | ----- 18 | 19 | The following example would filter items in \#content with shuffling 20 | animation, leaving only CMYK colors. 21 | 22 | ```javascript 23 | $("#content").quicksand($("#data > li"), 24 | { 25 | // all the parameters have sensible defaults 26 | // and in most cases can be optional 27 | duration: 1000, 28 | easing: "swing", 29 | attribute: "data-id", 30 | } 31 | ); 32 | ``` 33 | 34 | The markup could look like this: 35 | 36 | ```html 37 |
    38 |
  1. Red
  2. 39 |
  3. Green
  4. 40 |
  5. Blue
  6. 41 |
  7. Black
  8. 42 |
  9. White
  10. 43 |
  11. Yellow
  12. 44 |
  13. Cyan
  14. 45 |
  15. Magenta
  16. 46 |
47 | 48 | 54 | ``` 55 | 56 | Please note that you need to set additional attribute (you can specify 57 | which one in options hash, by default it's data-id) so that the plugin 58 | can identify same elements within source and destination collections. 59 | 60 | No additional CSS is required for the plugin to work. However, you 61 | should avoid styling the subject container using id selectors ---- 62 | please use class instead. 63 | 64 | - the subject (`#content`) is a container visible for the user 65 | - the first parameter ( `$("#data > li")` ) should contain a 66 | collection which will replace contents of the subject 67 | - `duration` - specifies duration (in milliseconds) of the animation 68 | effect, default: `750` 69 | - `easing` - animation easing effect, default: `'swing'` 70 | - `attribute` - attribute containing unique value able to identify 71 | same item within source and destination collection, default: 72 | `'data-id'` 73 | - `adjustHeight` - `'dynamic'` to animate height of the container 74 | (when the target collection is larger or smaller than source 75 | collection), `'call'` to automatically set height before or after 76 | the animation, `false` to keep container height constant; default: 77 | `'call'` 78 | - `adjustWidth` - Adjusts the width of container to fit all the items, 79 | `'call'` for automatically adjusting before or after the animation 80 | (determined automatically), `'dynamic'` for width adjustment animation, 81 | `'auto'` to set the value to `'auto'`, `false` for not doing absolutely 82 | anything about it (useful on responsive pages). 83 | - `useScaling` - use CSS3 scaling effect, default: false 84 | - `enhancement` - function that performs custom visual enhancements 85 | (e.g. font replacements) on newly created items, default: `function() {}`. 86 | Since version 1.5 of this plugin, this shouldn't be required in most cases 87 | as elements created by jquery.quicksand are deep-cloned, which preserves 88 | their enhancements and attached events. 89 | 90 | You can specify a callback function as an optional last argument. 91 | 92 | Dependencies 93 | ------------ 94 | 95 | - jQuery 1.6.1+. jQuery 2 and jQuery 3 is fully supported 96 | - Optional: jQuery Easing (http://gsgd.co.uk/sandbox/jquery/easing/) 97 | for additional easing options 98 | - Optional: jquery.transform2d.js from 99 | [jquery.transform.js](https://github.com/louisremi/jquery.transform.js) for 100 | CSS3 scaling effect. 101 | 102 | Browser compatibility 103 | --------------------- 104 | 105 | Tested under: 106 | 107 | - Safari 4 108 | - Chrome 4 109 | - Firefox 3.5 110 | - Opera 10.15 111 | - Internet Explorer 7 112 | - Internet Explorer 8 113 | 114 | No Internet Explorer 6 support is planned. Ever. The plugin, however, 115 | does not break your web application on IE6, it just skips the animation 116 | and replaces the collection immediately. 117 | 118 | I recommend using the plugin with HTML5 markup but any other option is 119 | fine. 120 | 121 | Custom attribute function 122 | ------------------------- 123 | 124 | If you don't like HTML5 data-\* attributes, you can specify a function 125 | instead. 126 | 127 | ```javascript 128 | $("#content").quicksand($("#data > li"), 129 | { 130 | // all the parameters have sensible defaults 131 | // and in most cases can be optional 132 | duration: 1000, 133 | easing: "swing", 134 | attribute: "data-id", 135 | attribute: function(v) { 136 | // different src of img means: different object 137 | return $(v).find('img').attr('src'); 138 | } 139 | } 140 | ); 141 | ``` 142 | 143 | Integration with enhancement plugins 144 | ------------------------------------ 145 | 146 | (This option shouldn't be needed if you use Quicksand v1.5 or higher) 147 | 148 | Quicksand works fine with other plugins. Please note that: 149 | 150 | - when your items have functional enhancements (e.g. tooltips), 151 | remember to use callback to apply them on newly cloned objects: 152 | 153 | ```javascript 154 | $("#content").quicksand($("#data > li"), 155 | { 156 | duration: 1000, 157 | }, function() { // callback function 158 | $('#content a').tooltip(); 159 | } 160 | ); 161 | ``` 162 | 163 | - when your items are visually enhanced (e.g. font replacement), use 164 | `enhancement` function to refresh/apply during the animation 165 | 166 | ```javascript 167 | $("#content").quicksand($("#data > li"), 168 | { 169 | duration: 1000, 170 | enhancement: function() { 171 | Cufon.refresh('#content span'); 172 | } 173 | } 174 | ); 175 | ``` 176 | 177 | Licensing 178 | --------- 179 | 180 | Copyright © 2010 [Jacek Galanciak](https://razorjack.net). 181 | 182 | Dual licensed under [MIT](MIT-LICENSE.txt) and [GPL version 2](GPL-LICENSE.txt) license. 183 | 184 | In simple language: you don't get any warranty or promise of future maintenance 185 | of this library, but you can modify the code or use this library, without any limitations, in 186 | commercial products, including commercial website templates. 187 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "jquery-quicksand", 3 | "version": "1.6.0", 4 | "main": "jquery-quicksand.js", 5 | "description": "jQuery plugin. Reorder and filter items with a nice shuffling animation.", 6 | "license": "(MIT OR GPL-2.0)", 7 | "main": "jquery.quicksand.js", 8 | "homepage": "https://razorjack.net/quicksand", 9 | "repository": { 10 | "type": "git", 11 | "url": "https://github.com/razorjack/quicksand.git" 12 | }, 13 | "authors": { 14 | "name": "Jacek Galanciak", 15 | "homepage": "https://razorjack.net/" 16 | }, 17 | "dependencies": { 18 | "jquery": ">=1.6.1" 19 | }, 20 | "ignore": [ 21 | "**/*.md", 22 | "**/*.txt" 23 | ], 24 | "keywords": [ 25 | "frontend", 26 | "animation", 27 | "filtering", 28 | "reordering" 29 | ], 30 | "moduleType": [ 31 | "amd", 32 | "globals" 33 | ] 34 | } 35 | -------------------------------------------------------------------------------- /jquery.easing.1.3.js: -------------------------------------------------------------------------------- 1 | /* 2 | * jQuery Easing v1.3 - http://gsgd.co.uk/sandbox/jquery/easing/ 3 | * 4 | * Uses the built in easing capabilities added In jQuery 1.1 5 | * to offer multiple easing options 6 | * 7 | * TERMS OF USE - jQuery Easing 8 | * 9 | * Open source under the BSD License. 10 | * 11 | * Copyright © 2008 George McGinley Smith 12 | * All rights reserved. 13 | * 14 | * Redistribution and use in source and binary forms, with or without modification, 15 | * are permitted provided that the following conditions are met: 16 | * 17 | * Redistributions of source code must retain the above copyright notice, this list of 18 | * conditions and the following disclaimer. 19 | * Redistributions in binary form must reproduce the above copyright notice, this list 20 | * of conditions and the following disclaimer in the documentation and/or other materials 21 | * provided with the distribution. 22 | * 23 | * Neither the name of the author nor the names of contributors may be used to endorse 24 | * or promote products derived from this software without specific prior written permission. 25 | * 26 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY 27 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 28 | * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 29 | * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 30 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE 31 | * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 32 | * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 33 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 34 | * OF THE POSSIBILITY OF SUCH DAMAGE. 35 | * 36 | */ 37 | 38 | // t: current time, b: begInnIng value, c: change In value, d: duration 39 | jQuery.easing['jswing'] = jQuery.easing['swing']; 40 | 41 | jQuery.extend( jQuery.easing, 42 | { 43 | def: 'easeOutQuad', 44 | swing: function (x, t, b, c, d) { 45 | //alert(jQuery.easing.default); 46 | return jQuery.easing[jQuery.easing.def](x, t, b, c, d); 47 | }, 48 | easeInQuad: function (x, t, b, c, d) { 49 | return c*(t/=d)*t + b; 50 | }, 51 | easeOutQuad: function (x, t, b, c, d) { 52 | return -c *(t/=d)*(t-2) + b; 53 | }, 54 | easeInOutQuad: function (x, t, b, c, d) { 55 | if ((t/=d/2) < 1) return c/2*t*t + b; 56 | return -c/2 * ((--t)*(t-2) - 1) + b; 57 | }, 58 | easeInCubic: function (x, t, b, c, d) { 59 | return c*(t/=d)*t*t + b; 60 | }, 61 | easeOutCubic: function (x, t, b, c, d) { 62 | return c*((t=t/d-1)*t*t + 1) + b; 63 | }, 64 | easeInOutCubic: function (x, t, b, c, d) { 65 | if ((t/=d/2) < 1) return c/2*t*t*t + b; 66 | return c/2*((t-=2)*t*t + 2) + b; 67 | }, 68 | easeInQuart: function (x, t, b, c, d) { 69 | return c*(t/=d)*t*t*t + b; 70 | }, 71 | easeOutQuart: function (x, t, b, c, d) { 72 | return -c * ((t=t/d-1)*t*t*t - 1) + b; 73 | }, 74 | easeInOutQuart: function (x, t, b, c, d) { 75 | if ((t/=d/2) < 1) return c/2*t*t*t*t + b; 76 | return -c/2 * ((t-=2)*t*t*t - 2) + b; 77 | }, 78 | easeInQuint: function (x, t, b, c, d) { 79 | return c*(t/=d)*t*t*t*t + b; 80 | }, 81 | easeOutQuint: function (x, t, b, c, d) { 82 | return c*((t=t/d-1)*t*t*t*t + 1) + b; 83 | }, 84 | easeInOutQuint: function (x, t, b, c, d) { 85 | if ((t/=d/2) < 1) return c/2*t*t*t*t*t + b; 86 | return c/2*((t-=2)*t*t*t*t + 2) + b; 87 | }, 88 | easeInSine: function (x, t, b, c, d) { 89 | return -c * Math.cos(t/d * (Math.PI/2)) + c + b; 90 | }, 91 | easeOutSine: function (x, t, b, c, d) { 92 | return c * Math.sin(t/d * (Math.PI/2)) + b; 93 | }, 94 | easeInOutSine: function (x, t, b, c, d) { 95 | return -c/2 * (Math.cos(Math.PI*t/d) - 1) + b; 96 | }, 97 | easeInExpo: function (x, t, b, c, d) { 98 | return (t==0) ? b : c * Math.pow(2, 10 * (t/d - 1)) + b; 99 | }, 100 | easeOutExpo: function (x, t, b, c, d) { 101 | return (t==d) ? b+c : c * (-Math.pow(2, -10 * t/d) + 1) + b; 102 | }, 103 | easeInOutExpo: function (x, t, b, c, d) { 104 | if (t==0) return b; 105 | if (t==d) return b+c; 106 | if ((t/=d/2) < 1) return c/2 * Math.pow(2, 10 * (t - 1)) + b; 107 | return c/2 * (-Math.pow(2, -10 * --t) + 2) + b; 108 | }, 109 | easeInCirc: function (x, t, b, c, d) { 110 | return -c * (Math.sqrt(1 - (t/=d)*t) - 1) + b; 111 | }, 112 | easeOutCirc: function (x, t, b, c, d) { 113 | return c * Math.sqrt(1 - (t=t/d-1)*t) + b; 114 | }, 115 | easeInOutCirc: function (x, t, b, c, d) { 116 | if ((t/=d/2) < 1) return -c/2 * (Math.sqrt(1 - t*t) - 1) + b; 117 | return c/2 * (Math.sqrt(1 - (t-=2)*t) + 1) + b; 118 | }, 119 | easeInElastic: function (x, t, b, c, d) { 120 | var s=1.70158;var p=0;var a=c; 121 | if (t==0) return b; if ((t/=d)==1) return b+c; if (!p) p=d*.3; 122 | if (a < Math.abs(c)) { a=c; var s=p/4; } 123 | else var s = p/(2*Math.PI) * Math.asin (c/a); 124 | return -(a*Math.pow(2,10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )) + b; 125 | }, 126 | easeOutElastic: function (x, t, b, c, d) { 127 | var s=1.70158;var p=0;var a=c; 128 | if (t==0) return b; if ((t/=d)==1) return b+c; if (!p) p=d*.3; 129 | if (a < Math.abs(c)) { a=c; var s=p/4; } 130 | else var s = p/(2*Math.PI) * Math.asin (c/a); 131 | return a*Math.pow(2,-10*t) * Math.sin( (t*d-s)*(2*Math.PI)/p ) + c + b; 132 | }, 133 | easeInOutElastic: function (x, t, b, c, d) { 134 | var s=1.70158;var p=0;var a=c; 135 | if (t==0) return b; if ((t/=d/2)==2) return b+c; if (!p) p=d*(.3*1.5); 136 | if (a < Math.abs(c)) { a=c; var s=p/4; } 137 | else var s = p/(2*Math.PI) * Math.asin (c/a); 138 | if (t < 1) return -.5*(a*Math.pow(2,10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )) + b; 139 | return a*Math.pow(2,-10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )*.5 + c + b; 140 | }, 141 | easeInBack: function (x, t, b, c, d, s) { 142 | if (s == undefined) s = 1.70158; 143 | return c*(t/=d)*t*((s+1)*t - s) + b; 144 | }, 145 | easeOutBack: function (x, t, b, c, d, s) { 146 | if (s == undefined) s = 1.70158; 147 | return c*((t=t/d-1)*t*((s+1)*t + s) + 1) + b; 148 | }, 149 | easeInOutBack: function (x, t, b, c, d, s) { 150 | if (s == undefined) s = 1.70158; 151 | if ((t/=d/2) < 1) return c/2*(t*t*(((s*=(1.525))+1)*t - s)) + b; 152 | return c/2*((t-=2)*t*(((s*=(1.525))+1)*t + s) + 2) + b; 153 | }, 154 | easeInBounce: function (x, t, b, c, d) { 155 | return c - jQuery.easing.easeOutBounce (x, d-t, 0, c, d) + b; 156 | }, 157 | easeOutBounce: function (x, t, b, c, d) { 158 | if ((t/=d) < (1/2.75)) { 159 | return c*(7.5625*t*t) + b; 160 | } else if (t < (2/2.75)) { 161 | return c*(7.5625*(t-=(1.5/2.75))*t + .75) + b; 162 | } else if (t < (2.5/2.75)) { 163 | return c*(7.5625*(t-=(2.25/2.75))*t + .9375) + b; 164 | } else { 165 | return c*(7.5625*(t-=(2.625/2.75))*t + .984375) + b; 166 | } 167 | }, 168 | easeInOutBounce: function (x, t, b, c, d) { 169 | if (t < d/2) return jQuery.easing.easeInBounce (x, t*2, 0, c, d) * .5 + b; 170 | return jQuery.easing.easeOutBounce (x, t*2-d, 0, c, d) * .5 + c*.5 + b; 171 | } 172 | }); 173 | 174 | /* 175 | * 176 | * TERMS OF USE - EASING EQUATIONS 177 | * 178 | * Open source under the BSD License. 179 | * 180 | * Copyright © 2001 Robert Penner 181 | * All rights reserved. 182 | * 183 | * Redistribution and use in source and binary forms, with or without modification, 184 | * are permitted provided that the following conditions are met: 185 | * 186 | * Redistributions of source code must retain the above copyright notice, this list of 187 | * conditions and the following disclaimer. 188 | * Redistributions in binary form must reproduce the above copyright notice, this list 189 | * of conditions and the following disclaimer in the documentation and/or other materials 190 | * provided with the distribution. 191 | * 192 | * Neither the name of the author nor the names of contributors may be used to endorse 193 | * or promote products derived from this software without specific prior written permission. 194 | * 195 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY 196 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 197 | * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 198 | * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 199 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE 200 | * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 201 | * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 202 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 203 | * OF THE POSSIBILITY OF SUCH DAMAGE. 204 | * 205 | */ -------------------------------------------------------------------------------- /jquery.quicksand.js: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Quicksand 1.6.0 4 | 5 | Reorder and filter items with a nice shuffling animation. 6 | 7 | Copyright (c) 2010 Jacek Galanciak (razorjack.net) 8 | Big thanks for Piotr Petrus (riddle.pl) for deep code review and wonderful docs & demos. 9 | 10 | Dual licensed under the MIT and GPL version 2 licenses. 11 | https://raw.githubusercontent.com/razorjack/quicksand/master/MIT-LICENSE.txt 12 | https://raw.githubusercontent.com/razorjack/quicksand/master/GPL-LICENSE.txt 13 | 14 | Project site: https://razorjack.net/quicksand 15 | Github site: https://github.com/razorjack/quicksand 16 | 17 | */ 18 | 19 | // Uses CommonJS, AMD or browser globals to create a jQuery plugin. 20 | (function (factory) { 21 | if (typeof define === 'function' && define.amd) { 22 | // AMD. Register as an anonymous module. 23 | define(['jquery'], factory); 24 | } else if (typeof module === 'object' && module.exports) { 25 | // Node/CommonJS 26 | module.exports = function( root, jQuery ) { 27 | if ( jQuery === undefined ) { 28 | // require('jQuery') returns a factory that requires window to 29 | // build a jQuery instance, we normalize how we use modules 30 | // that require this pattern but the window provided is a noop 31 | // if it's defined (how jquery works) 32 | if ( typeof window !== 'undefined' ) { 33 | jQuery = require('jquery'); 34 | } 35 | else { 36 | jQuery = require('jquery')(root); 37 | } 38 | } 39 | factory(jQuery); 40 | return jQuery; 41 | }; 42 | } else { 43 | // Browser globals 44 | factory(jQuery); 45 | } 46 | }(function ($) { 47 | var cloneWithCanvases = function(jqueryObject) { 48 | var clonedJqueryObject = jqueryObject.clone(true, true); 49 | var canvases = jqueryObject.find('canvas'); 50 | if (canvases.length) { 51 | var clonedCanvases = clonedJqueryObject.find('canvas'); 52 | clonedCanvases.each(function(index) { 53 | var context = this.getContext('2d'); 54 | context.drawImage(canvases.get(index), 0, 0); 55 | }); 56 | } 57 | return clonedJqueryObject; 58 | }; 59 | 60 | $.fn.quicksand = function(collection, customOptions) { 61 | var options = { 62 | duration : 750, 63 | easing : 'swing', 64 | attribute : 'data-id', // attribute to recognize same items within source and dest 65 | adjustHeight : 'call', // 'dynamic' animates height during shuffling (slow), 'call' adjusts it 66 | // before or after the animation, false leaves height alone, 'auto' sets it to auto 67 | adjustWidth : false, // 'dynamic' animates width during shuffling (slow), 68 | // 'call' adjusts it before or after the animation, false leaves the width alone, 'auto' sets it to auto 69 | useScaling : false, // enable it if you're using scaling effect 70 | enhancement : function(c) {}, // Visual enhacement (eg. font replacement) function for cloned elements 71 | selector : '> *', 72 | atomic : false, 73 | dx : 0, 74 | dy : 0, 75 | maxWidth : 0, 76 | retainExisting : true // disable if you want the collection of items to be replaced completely by incoming items. 77 | }; 78 | 79 | $.extend(options, customOptions); 80 | 81 | if ((typeof ($.transform) == 'undefined')) { 82 | options.useScaling = false; 83 | } 84 | 85 | var callbackFunction; 86 | if (typeof (arguments[1]) == 'function') { 87 | callbackFunction = arguments[1]; 88 | } else if (typeof (arguments[2] == 'function')) { 89 | callbackFunction = arguments[2]; 90 | } 91 | 92 | var that = this; 93 | return this.each(function(i) { 94 | var val; 95 | var animationQueue = []; // used to store all the animation params before starting the animation; 96 | // solves initial animation slowdowns 97 | var $collection; 98 | if (typeof(options.attribute) == 'function') { 99 | $collection = $(collection); 100 | } else { 101 | $collection = cloneWithCanvases($(collection).filter('[' + options.attribute + ']')); // destination (target) collection 102 | } 103 | var $sourceParent = $(this); // source, the visible container of source collection 104 | var sourceHeight = $(this).css('height'); // used to keep height and document flow during the animation 105 | var sourceWidth = $(this).css('width'); // used to keep width and document flow during the animation 106 | var destHeight, destWidth; 107 | var adjustHeightOnCallback = false; 108 | var adjustWidthOnCallback = false; 109 | var offset = $($sourceParent).offset(); // offset of visible container, used in animation calculations 110 | var offsets = []; // coordinates of every source collection item 111 | var $source = $(this).find(options.selector); // source collection items 112 | var width = $($source).innerWidth(); // need for the responsive design 113 | 114 | // Replace the collection and quit if IE6 115 | if (navigator.userAgent.match(/msie [6]/i)) { 116 | $sourceParent.html('').append($collection); 117 | return; 118 | } 119 | 120 | // Gets called when any animation is finished 121 | var postCallbackPerformed = 0; // prevents the function from being called more than one time 122 | var postCallback = function() { 123 | $(this).css('margin', '').css('position', '').css('top', '').css('left', '').css('opacity', ''); 124 | if (!postCallbackPerformed) { 125 | postCallbackPerformed = 1; 126 | 127 | if (!options.atomic) { 128 | // hack: used to be: $sourceParent.html($dest.html()); 129 | // put target HTML into visible source container 130 | // but new webkit builds cause flickering when replacing the collections 131 | var $toDelete = $sourceParent.find(options.selector); 132 | if (!options.retainExisting) { 133 | $sourceParent.prepend($dest.find(options.selector)); 134 | $toDelete.remove(); 135 | } else { 136 | // Avoid replacing elements because we may have already altered items in significant 137 | // ways and it would be bad to have to do it again. (i.e. lazy load images) 138 | // But $dest holds the correct ordering. So we must re-sequence items in $sourceParent to match. 139 | var $keepElements = $([]); 140 | $dest.find(options.selector).each(function(i) { 141 | var $matchedElement = $([]); 142 | if (typeof (options.attribute) == 'function') { 143 | var val = options.attribute($(this)); 144 | $toDelete.each(function() { 145 | if (options.attribute(this) == val) { 146 | $matchedElement = $(this); 147 | return false; 148 | } 149 | }); 150 | } else { 151 | $matchedElement = $toDelete.filter( 152 | '[' + options.attribute + '="'+ 153 | $(this).attr(options.attribute) + '"]'); 154 | } 155 | if ($matchedElement.length > 0) { 156 | // There is a matching element in the $toDelete list and in $dest 157 | // list, so make sure it is in the right location within $sourceParent 158 | // and put it in the list of elements we need to not delete. 159 | $keepElements = $keepElements.add($matchedElement); 160 | if (i === 0) { 161 | $sourceParent.prepend($matchedElement); 162 | } else { 163 | $matchedElement.insertAfter($sourceParent.find(options.selector).get(i - 1)); 164 | } 165 | } 166 | }); 167 | // Remove whatever is remaining from the DOM 168 | $toDelete.not($keepElements).remove(); 169 | } 170 | 171 | if (adjustHeightOnCallback) { 172 | $sourceParent.css('height', destHeight); 173 | } 174 | if (adjustWidthOnCallback) { 175 | $sourceParent.css('width', sourceWidth); 176 | } 177 | } 178 | options.enhancement($sourceParent); // Perform custom visual enhancements on a newly replaced collection 179 | if (typeof callbackFunction == 'function') { 180 | callbackFunction.call(that); 181 | } 182 | } 183 | 184 | if ('auto' === options.adjustHeight) { 185 | $sourceParent.css('height', 'auto'); 186 | } 187 | 188 | if ('auto' === options.adjustWidth) { 189 | $sourceParent.css('width', 'auto'); 190 | } 191 | }; 192 | 193 | // Position: relative situations 194 | var $correctionParent = $sourceParent.offsetParent(); 195 | var correctionOffset = $correctionParent.offset(); 196 | if ($correctionParent.css('position') == 'relative') { 197 | if ($correctionParent.get(0).nodeName.toLowerCase() != 'body') { 198 | correctionOffset.top += (parseFloat($correctionParent.css('border-top-width')) || 0); 199 | correctionOffset.left += (parseFloat($correctionParent.css('border-left-width')) || 0); 200 | } 201 | } else { 202 | correctionOffset.top -= (parseFloat($correctionParent.css('border-top-width')) || 0); 203 | correctionOffset.left -= (parseFloat($correctionParent.css('border-left-width')) || 0); 204 | correctionOffset.top -= (parseFloat($correctionParent.css('margin-top')) || 0); 205 | correctionOffset.left -= (parseFloat($correctionParent.css('margin-left')) || 0); 206 | } 207 | 208 | // perform custom corrections from options (use when Quicksand fails to detect proper correction) 209 | if (isNaN(correctionOffset.left)) { 210 | correctionOffset.left = 0; 211 | } 212 | if (isNaN(correctionOffset.top)) { 213 | correctionOffset.top = 0; 214 | } 215 | 216 | correctionOffset.left -= options.dx; 217 | correctionOffset.top -= options.dy; 218 | 219 | // keeps nodes after source container, holding their position 220 | if (options.adjustHeight !== false) { 221 | $sourceParent.css('height', $(this).height()); 222 | } 223 | if (options.adjustWidth !== false) { 224 | $sourceParent.css('width', $(this).width()); 225 | } 226 | 227 | // get positions of source collections 228 | $source.each(function(i) { 229 | offsets[i] = $(this).offset(); 230 | }); 231 | 232 | // stops previous animations on source container 233 | $(this).stop(); 234 | var dx = 0; 235 | var dy = 0; 236 | $source.each(function(i) { 237 | $(this).stop(); // stop animation of collection items 238 | var rawObj = $(this).get(0); 239 | if (rawObj.style.position == 'absolute') { 240 | dx = -options.dx; 241 | dy = -options.dy; 242 | } else { 243 | dx = options.dx; 244 | dy = options.dy; 245 | } 246 | 247 | rawObj.style.position = 'absolute'; 248 | rawObj.style.margin = '0'; 249 | 250 | if (!options.adjustWidth) { 251 | rawObj.style.width = (width + 'px'); // sets the width to the current element 252 | // with even if it has been changed 253 | // by a responsive design 254 | } 255 | 256 | rawObj.style.top = (offsets[i].top- parseFloat(rawObj.style.marginTop) - correctionOffset.top + dy) + 'px'; 257 | rawObj.style.left = (offsets[i].left- parseFloat(rawObj.style.marginLeft) - correctionOffset.left + dx) + 'px'; 258 | 259 | if (options.maxWidth > 0 && offsets[i].left > options.maxWidth) { 260 | rawObj.style.display = 'none'; 261 | } 262 | }); 263 | 264 | // create temporary container with destination collection 265 | var $dest = cloneWithCanvases($($sourceParent)); 266 | var rawDest = $dest.get(0); 267 | rawDest.innerHTML = ''; 268 | rawDest.setAttribute('id', ''); 269 | rawDest.style.height = 'auto'; 270 | rawDest.style.width = $sourceParent.width() + 'px'; 271 | $dest.append($collection); 272 | // Inserts node into HTML. Note that the node is under visible source container in the exactly same position 273 | // The browser render all the items without showing them (opacity: 0.0) No offset calculations are needed, 274 | // the browser just extracts position from underlayered destination items and sets animation to destination positions. 275 | $dest.insertBefore($sourceParent); 276 | $dest.css('opacity', 0.0); 277 | rawDest.style.zIndex = -1; 278 | 279 | rawDest.style.margin = '0'; 280 | rawDest.style.position = 'absolute'; 281 | rawDest.style.top = offset.top - correctionOffset.top + 'px'; 282 | rawDest.style.left = offset.left - correctionOffset.left + 'px'; 283 | 284 | if (options.adjustHeight === 'dynamic') { 285 | // If destination container has different height than source container the height can be animated, 286 | // adjusting it to destination height 287 | $sourceParent.animate({ height : $dest.height() }, options.duration, options.easing); 288 | } else if (options.adjustHeight === 'call') { 289 | destHeight = $dest.height(); 290 | if (parseFloat(sourceHeight) < parseFloat(destHeight)) { 291 | // Adjust the height now so that the items don't move out of the container 292 | $sourceParent.css('height', destHeight); 293 | } else { 294 | // Adjust later, on callback 295 | adjustHeightOnCallback = true; 296 | } 297 | } 298 | 299 | if (options.adjustWidth === 'dynamic') { 300 | // If destination container has different width than source container the width can be animated, 301 | // adjusting it to destination width 302 | $sourceParent.animate({ width : $dest.width() }, options.duration, options.easing); 303 | } else if (options.adjustWidth === 'call') { 304 | destWidth = $dest.width(); 305 | if (parseFloat(sourceWidth) < parseFloat(destWidth)) { 306 | // Adjust the height now so that the items don't move out of the container 307 | $sourceParent.css('width', destWidth); 308 | } else { 309 | // Adjust later, on callback 310 | adjustWidthOnCallback = true; 311 | } 312 | } 313 | 314 | // Now it's time to do shuffling animation. First of all, we need to identify same elements within 315 | // source and destination collections 316 | $source.each(function(i) { 317 | var destElement = []; 318 | if (typeof (options.attribute) == 'function') { 319 | val = options.attribute($(this)); 320 | $collection.each(function() { 321 | if (options.attribute(this) == val) { 322 | destElement = $(this); 323 | return false; 324 | } 325 | }); 326 | } else { 327 | destElement = $collection.filter('[' + options.attribute + '="' + $(this).attr(options.attribute) + '"]'); 328 | } 329 | if (destElement.length) { 330 | // The item is both in source and destination collections. It it's under different position, let's move it 331 | if (!options.useScaling) { 332 | animationQueue.push({ 333 | element : $(this), dest : destElement, 334 | style : { 335 | top : $(this).offset().top, 336 | left : $(this).offset().left, 337 | opacity : "" 338 | }, 339 | animation : { 340 | top : destElement.offset().top - correctionOffset.top, 341 | left : destElement.offset().left - correctionOffset.left, 342 | opacity : 1.0 343 | } 344 | }); 345 | } else { 346 | animationQueue.push({ 347 | element : $(this), dest : destElement, 348 | style : { 349 | top : $(this).offset().top, 350 | left : $(this).offset().left, 351 | opacity : "" 352 | }, 353 | animation : { 354 | top : destElement.offset().top - correctionOffset.top, 355 | left : destElement.offset().left - correctionOffset.left, 356 | opacity : 1.0, 357 | transform : 'scale(1.0)' 358 | } 359 | }); 360 | } 361 | } else { 362 | // The item from source collection is not present in destination collections. Let's remove it 363 | if (!options.useScaling) { 364 | animationQueue.push({ 365 | element : $(this), 366 | style : { 367 | top : $(this).offset().top, 368 | left : $(this).offset().left, 369 | opacity : "" 370 | }, 371 | animation : { 372 | opacity : '0.0' 373 | } 374 | }); 375 | } else { 376 | animationQueue.push({ 377 | element : $(this), 378 | style : { 379 | top : $(this).offset().top, 380 | left : $(this).offset().left, 381 | opacity : "" 382 | }, 383 | animation : { 384 | opacity : '0.0', 385 | transform : 'scale(0.0)' 386 | } 387 | }); 388 | } 389 | } 390 | }); 391 | 392 | $collection.each(function(i) { 393 | // Grab all items from target collection not present in visible source collection 394 | var sourceElement = []; 395 | var destElement = []; 396 | if (typeof (options.attribute) == 'function') { 397 | val = options.attribute($(this)); 398 | $source.each(function() { 399 | if (options.attribute(this) == val) { 400 | sourceElement = $(this); 401 | return false; 402 | } 403 | }); 404 | 405 | $collection.each(function() { 406 | if (options.attribute(this) == val) { 407 | destElement = $(this); 408 | return false; 409 | } 410 | }); 411 | } else { 412 | sourceElement = $source.filter('[' + options.attribute + '="' + $(this).attr(options.attribute) + '"]'); 413 | destElement = $collection.filter('[' + options.attribute + '="' + $(this).attr(options.attribute) + '"]'); 414 | } 415 | 416 | var animationOptions; 417 | if (sourceElement.length === 0 && destElement.length > 0) { 418 | 419 | // No such element in source collection... 420 | if (!options.useScaling) { 421 | animationOptions = {opacity : '1.0'}; 422 | } else { 423 | animationOptions = {opacity : '1.0', transform : 'scale(1.0)'}; 424 | } 425 | 426 | // Let's create it 427 | var d = cloneWithCanvases(destElement); 428 | var rawDestElement = d.get(0); 429 | rawDestElement.style.position = 'absolute'; 430 | rawDestElement.style.margin = '0'; 431 | 432 | if (!options.adjustWidth) { 433 | // sets the width to the current element with even if it has been changed by a responsive design 434 | rawDestElement.style.width = width + 'px'; 435 | } 436 | 437 | rawDestElement.style.top = destElement.offset().top - correctionOffset.top + 'px'; 438 | rawDestElement.style.left = destElement.offset().left - correctionOffset.left + 'px'; 439 | 440 | d.css('opacity', 0.0); // IE 441 | 442 | if (options.useScaling) { 443 | d.css("transform", "scale(0.0)"); 444 | } 445 | d.appendTo($sourceParent); 446 | 447 | if (options.maxWidth === 0 || destElement.offset().left < options.maxWidth) { 448 | animationQueue.push({element : $(d), dest : destElement,animation : animationOptions}); 449 | } 450 | } 451 | }); 452 | 453 | $dest.remove(); 454 | if (!options.atomic) { 455 | options.enhancement($sourceParent); // Perform custom visual enhancements during the animation 456 | for (i = 0; i < animationQueue.length; i++) { 457 | animationQueue[i].element.animate(animationQueue[i].animation, options.duration, options.easing, postCallback); 458 | } 459 | } else { 460 | $toDelete = $sourceParent.find(options.selector); 461 | $sourceParent.prepend($dest.find(options.selector)); 462 | for (i = 0; i < animationQueue.length; i++) { 463 | if (animationQueue[i].dest && animationQueue[i].style) { 464 | var destElement = animationQueue[i].dest; 465 | var destOffset = destElement.offset(); 466 | 467 | destElement.css({ 468 | position : 'relative', 469 | top : (animationQueue[i].style.top - destOffset.top), 470 | left : (animationQueue[i].style.left - destOffset.left) 471 | }); 472 | 473 | destElement.animate({top : "0", left : "0"}, 474 | options.duration, 475 | options.easing, 476 | postCallback); 477 | } else { 478 | animationQueue[i].element.animate(animationQueue[i].animation, 479 | options.duration, 480 | options.easing, 481 | postCallback); 482 | } 483 | } 484 | $toDelete.remove(); 485 | } 486 | }); 487 | }; 488 | })); 489 | -------------------------------------------------------------------------------- /jquery.transform2d.js: -------------------------------------------------------------------------------- 1 | /* 2 | * transform: A jQuery cssHooks adding cross-browser 2d transform capabilities to $.fn.css() and $.fn.animate() 3 | * 4 | * limitations: 5 | * - requires jQuery 1.4.3+ 6 | * - Should you use the *translate* property, then your elements need to be absolutely positionned in a relatively positionned wrapper **or it will fail in IE678**. 7 | * - transformOrigin is not accessible 8 | * 9 | * latest version and complete README available on Github: 10 | * https://github.com/louisremi/jquery.transform.js 11 | * 12 | * Copyright 2011 @louis_remi 13 | * Licensed under the MIT license. 14 | * 15 | * This saved you an hour of work? 16 | * Send me music http://www.amazon.co.uk/wishlist/HNTU0468LQON 17 | * 18 | */ 19 | (function( $, window, document, Math, undefined ) { 20 | 21 | /* 22 | * Feature tests and global variables 23 | */ 24 | var div = document.createElement("div"), 25 | divStyle = div.style, 26 | suffix = "Transform", 27 | testProperties = [ 28 | "O" + suffix, 29 | "ms" + suffix, 30 | "Webkit" + suffix, 31 | "Moz" + suffix 32 | ], 33 | i = testProperties.length, 34 | supportProperty, 35 | supportMatrixFilter, 36 | supportFloat32Array = "Float32Array" in window, 37 | propertyHook, 38 | propertyGet, 39 | rMatrix = /Matrix([^)]*)/, 40 | rAffine = /^\s*matrix\(\s*1\s*,\s*0\s*,\s*0\s*,\s*1\s*(?:,\s*0(?:px)?\s*){2}\)\s*$/, 41 | _transform = "transform", 42 | _transformOrigin = "transformOrigin", 43 | _translate = "translate", 44 | _rotate = "rotate", 45 | _scale = "scale", 46 | _skew = "skew", 47 | _matrix = "matrix"; 48 | 49 | // test different vendor prefixes of these properties 50 | while ( i-- ) { 51 | if ( testProperties[i] in divStyle ) { 52 | $.support[_transform] = supportProperty = testProperties[i]; 53 | $.support[_transformOrigin] = supportProperty + "Origin"; 54 | continue; 55 | } 56 | } 57 | // IE678 alternative 58 | if ( !supportProperty ) { 59 | $.support.matrixFilter = supportMatrixFilter = divStyle.filter === ""; 60 | } 61 | 62 | // px isn't the default unit of these properties 63 | $.cssNumber[_transform] = $.cssNumber[_transformOrigin] = true; 64 | 65 | /* 66 | * fn.css() hooks 67 | */ 68 | if ( supportProperty && supportProperty != _transform ) { 69 | // Modern browsers can use jQuery.cssProps as a basic hook 70 | $.cssProps[_transform] = supportProperty; 71 | $.cssProps[_transformOrigin] = supportProperty + "Origin"; 72 | 73 | // Firefox needs a complete hook because it stuffs matrix with "px" 74 | if ( supportProperty == "Moz" + suffix ) { 75 | propertyHook = { 76 | get: function( elem, computed ) { 77 | return (computed ? 78 | // remove "px" from the computed matrix 79 | $.css( elem, supportProperty ).split("px").join(""): 80 | elem.style[supportProperty] 81 | ); 82 | }, 83 | set: function( elem, value ) { 84 | // add "px" to matrices 85 | elem.style[supportProperty] = /matrix\([^)p]*\)/.test(value) ? 86 | value.replace(/matrix((?:[^,]*,){4})([^,]*),([^)]*)/, _matrix+"$1$2px,$3px"): 87 | value; 88 | } 89 | }; 90 | /* Fix two jQuery bugs still present in 1.5.1 91 | * - rupper is incompatible with IE9, see http://jqbug.com/8346 92 | * - jQuery.css is not really jQuery.cssProps aware, see http://jqbug.com/8402 93 | */ 94 | } else if ( /^1\.[0-5](?:\.|$)/.test($.fn.jquery) ) { 95 | propertyHook = { 96 | get: function( elem, computed ) { 97 | return (computed ? 98 | $.css( elem, supportProperty.replace(/^ms/, "Ms") ): 99 | elem.style[supportProperty] 100 | ); 101 | } 102 | }; 103 | } 104 | /* TODO: leverage hardware acceleration of 3d transform in Webkit only 105 | else if ( supportProperty == "Webkit" + suffix && support3dTransform ) { 106 | propertyHook = { 107 | set: function( elem, value ) { 108 | elem.style[supportProperty] = 109 | value.replace(); 110 | } 111 | } 112 | }*/ 113 | 114 | } else if ( supportMatrixFilter ) { 115 | propertyHook = { 116 | get: function( elem, computed, asArray ) { 117 | var elemStyle = ( computed && elem.currentStyle ? elem.currentStyle : elem.style ), 118 | matrix, data; 119 | 120 | if ( elemStyle && rMatrix.test( elemStyle.filter ) ) { 121 | matrix = RegExp.$1.split(","); 122 | matrix = [ 123 | matrix[0].split("=")[1], 124 | matrix[2].split("=")[1], 125 | matrix[1].split("=")[1], 126 | matrix[3].split("=")[1] 127 | ]; 128 | } else { 129 | matrix = [1,0,0,1]; 130 | } 131 | 132 | if ( ! $.cssHooks[_transformOrigin] ) { 133 | matrix[4] = elemStyle ? parseInt(elemStyle.left, 10) || 0 : 0; 134 | matrix[5] = elemStyle ? parseInt(elemStyle.top, 10) || 0 : 0; 135 | 136 | } else { 137 | data = $._data( elem, "transformTranslate", undefined ); 138 | matrix[4] = data ? data[0] : 0; 139 | matrix[5] = data ? data[1] : 0; 140 | } 141 | 142 | return asArray ? matrix : _matrix+"(" + matrix + ")"; 143 | }, 144 | set: function( elem, value, animate ) { 145 | var elemStyle = elem.style, 146 | currentStyle, 147 | Matrix, 148 | filter, 149 | centerOrigin; 150 | 151 | if ( !animate ) { 152 | elemStyle.zoom = 1; 153 | } 154 | 155 | value = matrix(value); 156 | 157 | // rotate, scale and skew 158 | Matrix = [ 159 | "Matrix("+ 160 | "M11="+value[0], 161 | "M12="+value[2], 162 | "M21="+value[1], 163 | "M22="+value[3], 164 | "SizingMethod='auto expand'" 165 | ].join(); 166 | filter = ( currentStyle = elem.currentStyle ) && currentStyle.filter || elemStyle.filter || ""; 167 | 168 | elemStyle.filter = rMatrix.test(filter) ? 169 | filter.replace(rMatrix, Matrix) : 170 | filter + " progid:DXImageTransform.Microsoft." + Matrix + ")"; 171 | 172 | if ( ! $.cssHooks[_transformOrigin] ) { 173 | 174 | // center the transform origin, from pbakaus's Transformie http://github.com/pbakaus/transformie 175 | if ( (centerOrigin = $.transform.centerOrigin) ) { 176 | elemStyle[centerOrigin == "margin" ? "marginLeft" : "left"] = -(elem.offsetWidth/2) + (elem.clientWidth/2) + "px"; 177 | elemStyle[centerOrigin == "margin" ? "marginTop" : "top"] = -(elem.offsetHeight/2) + (elem.clientHeight/2) + "px"; 178 | } 179 | 180 | // translate 181 | // We assume that the elements are absolute positionned inside a relative positionned wrapper 182 | elemStyle.left = value[4] + "px"; 183 | elemStyle.top = value[5] + "px"; 184 | 185 | } else { 186 | $.cssHooks[_transformOrigin].set( elem, value ); 187 | } 188 | } 189 | }; 190 | } 191 | // populate jQuery.cssHooks with the appropriate hook if necessary 192 | if ( propertyHook ) { 193 | $.cssHooks[_transform] = propertyHook; 194 | } 195 | // we need a unique setter for the animation logic 196 | propertyGet = propertyHook && propertyHook.get || $.css; 197 | 198 | /* 199 | * fn.animate() hooks 200 | */ 201 | $.fx.step.transform = function( fx ) { 202 | var elem = fx.elem, 203 | start = fx.start, 204 | end = fx.end, 205 | pos = fx.pos, 206 | transform = "", 207 | precision = 1E5, 208 | i, startVal, endVal, unit; 209 | 210 | // fx.end and fx.start need to be converted to interpolation lists 211 | if ( !start || typeof start === "string" ) { 212 | 213 | // the following block can be commented out with jQuery 1.5.1+, see #7912 214 | if ( !start ) { 215 | start = propertyGet( elem, supportProperty ); 216 | } 217 | 218 | // force layout only once per animation 219 | if ( supportMatrixFilter ) { 220 | elem.style.zoom = 1; 221 | } 222 | 223 | // replace "+=" in relative animations (-= is meaningless with transforms) 224 | end = end.split("+=").join(start); 225 | 226 | // parse both transform to generate interpolation list of same length 227 | $.extend( fx, interpolationList( start, end ) ); 228 | start = fx.start; 229 | end = fx.end; 230 | } 231 | 232 | i = start.length; 233 | 234 | // interpolate functions of the list one by one 235 | while ( i-- ) { 236 | startVal = start[i]; 237 | endVal = end[i]; 238 | unit = +false; 239 | 240 | switch ( startVal[0] ) { 241 | 242 | case _translate: 243 | unit = "px"; 244 | case _scale: 245 | unit || ( unit = ""); 246 | 247 | transform = startVal[0] + "(" + 248 | Math.round( (startVal[1][0] + (endVal[1][0] - startVal[1][0]) * pos) * precision ) / precision + unit +","+ 249 | Math.round( (startVal[1][1] + (endVal[1][1] - startVal[1][1]) * pos) * precision ) / precision + unit + ")"+ 250 | transform; 251 | break; 252 | 253 | case _skew + "X": 254 | case _skew + "Y": 255 | case _rotate: 256 | transform = startVal[0] + "(" + 257 | Math.round( (startVal[1] + (endVal[1] - startVal[1]) * pos) * precision ) / precision +"rad)"+ 258 | transform; 259 | break; 260 | } 261 | } 262 | 263 | fx.origin && ( transform = fx.origin + transform ); 264 | 265 | propertyHook && propertyHook.set ? 266 | propertyHook.set( elem, transform, +true ): 267 | elem.style[supportProperty] = transform; 268 | }; 269 | 270 | /* 271 | * Utility functions 272 | */ 273 | 274 | // turns a transform string into its "matrix(A,B,C,D,X,Y)" form (as an array, though) 275 | function matrix( transform ) { 276 | transform = transform.split(")"); 277 | var 278 | trim = $.trim 279 | , i = -1 280 | // last element of the array is an empty string, get rid of it 281 | , l = transform.length -1 282 | , split, prop, val 283 | , prev = supportFloat32Array ? new Float32Array(6) : [] 284 | , curr = supportFloat32Array ? new Float32Array(6) : [] 285 | , rslt = supportFloat32Array ? new Float32Array(6) : [1,0,0,1,0,0] 286 | ; 287 | 288 | prev[0] = prev[3] = rslt[0] = rslt[3] = 1; 289 | prev[1] = prev[2] = prev[4] = prev[5] = 0; 290 | 291 | // Loop through the transform properties, parse and multiply them 292 | while ( ++i < l ) { 293 | split = transform[i].split("("); 294 | prop = trim(split[0]); 295 | val = split[1]; 296 | curr[0] = curr[3] = 1; 297 | curr[1] = curr[2] = curr[4] = curr[5] = 0; 298 | 299 | switch (prop) { 300 | case _translate+"X": 301 | curr[4] = parseInt(val, 10); 302 | break; 303 | 304 | case _translate+"Y": 305 | curr[5] = parseInt(val, 10); 306 | break; 307 | 308 | case _translate: 309 | val = val.split(","); 310 | curr[4] = parseInt(val[0], 10); 311 | curr[5] = parseInt(val[1] || 0, 10); 312 | break; 313 | 314 | case _rotate: 315 | val = toRadian(val); 316 | curr[0] = Math.cos(val); 317 | curr[1] = Math.sin(val); 318 | curr[2] = -Math.sin(val); 319 | curr[3] = Math.cos(val); 320 | break; 321 | 322 | case _scale+"X": 323 | curr[0] = +val; 324 | break; 325 | 326 | case _scale+"Y": 327 | curr[3] = val; 328 | break; 329 | 330 | case _scale: 331 | val = val.split(","); 332 | curr[0] = val[0]; 333 | curr[3] = val.length>1 ? val[1] : val[0]; 334 | break; 335 | 336 | case _skew+"X": 337 | curr[2] = Math.tan(toRadian(val)); 338 | break; 339 | 340 | case _skew+"Y": 341 | curr[1] = Math.tan(toRadian(val)); 342 | break; 343 | 344 | case _matrix: 345 | val = val.split(","); 346 | curr[0] = val[0]; 347 | curr[1] = val[1]; 348 | curr[2] = val[2]; 349 | curr[3] = val[3]; 350 | curr[4] = parseInt(val[4], 10); 351 | curr[5] = parseInt(val[5], 10); 352 | break; 353 | } 354 | 355 | // Matrix product (array in column-major order) 356 | rslt[0] = prev[0] * curr[0] + prev[2] * curr[1]; 357 | rslt[1] = prev[1] * curr[0] + prev[3] * curr[1]; 358 | rslt[2] = prev[0] * curr[2] + prev[2] * curr[3]; 359 | rslt[3] = prev[1] * curr[2] + prev[3] * curr[3]; 360 | rslt[4] = prev[0] * curr[4] + prev[2] * curr[5] + prev[4]; 361 | rslt[5] = prev[1] * curr[4] + prev[3] * curr[5] + prev[5]; 362 | 363 | prev = [rslt[0],rslt[1],rslt[2],rslt[3],rslt[4],rslt[5]]; 364 | } 365 | return rslt; 366 | } 367 | 368 | // turns a matrix into its rotate, scale and skew components 369 | // algorithm from http://hg.mozilla.org/mozilla-central/file/7cb3e9795d04/layout/style/nsStyleAnimation.cpp 370 | function unmatrix(matrix) { 371 | var 372 | scaleX 373 | , scaleY 374 | , skew 375 | , A = matrix[0] 376 | , B = matrix[1] 377 | , C = matrix[2] 378 | , D = matrix[3] 379 | ; 380 | 381 | // Make sure matrix is not singular 382 | if ( A * D - B * C ) { 383 | // step (3) 384 | scaleX = Math.sqrt( A * A + B * B ); 385 | A /= scaleX; 386 | B /= scaleX; 387 | // step (4) 388 | skew = A * C + B * D; 389 | C -= A * skew; 390 | D -= B * skew; 391 | // step (5) 392 | scaleY = Math.sqrt( C * C + D * D ); 393 | C /= scaleY; 394 | D /= scaleY; 395 | skew /= scaleY; 396 | // step (6) 397 | if ( A * D < B * C ) { 398 | A = -A; 399 | B = -B; 400 | skew = -skew; 401 | scaleX = -scaleX; 402 | } 403 | 404 | // matrix is singular and cannot be interpolated 405 | } else { 406 | // In this case the elem shouldn't be rendered, hence scale == 0 407 | scaleX = scaleY = skew = 0; 408 | } 409 | 410 | // The recomposition order is very important 411 | // see http://hg.mozilla.org/mozilla-central/file/7cb3e9795d04/layout/style/nsStyleAnimation.cpp#l971 412 | return [ 413 | [_translate, [+matrix[4], +matrix[5]]], 414 | [_rotate, Math.atan2(B, A)], 415 | [_skew + "X", Math.atan(skew)], 416 | [_scale, [scaleX, scaleY]] 417 | ]; 418 | } 419 | 420 | // build the list of transform functions to interpolate 421 | // use the algorithm described at http://dev.w3.org/csswg/css3-2d-transforms/#animation 422 | function interpolationList( start, end ) { 423 | var list = { 424 | start: [], 425 | end: [] 426 | }, 427 | i = -1, l, 428 | currStart, currEnd, currType; 429 | 430 | // get rid of affine transform matrix 431 | ( start == "none" || isAffine( start ) ) && ( start = "" ); 432 | ( end == "none" || isAffine( end ) ) && ( end = "" ); 433 | 434 | // if end starts with the current computed style, this is a relative animation 435 | // store computed style as the origin, remove it from start and end 436 | if ( start && end && !end.indexOf("matrix") && toArray( start ).join() == toArray( end.split(")")[0] ).join() ) { 437 | list.origin = start; 438 | start = ""; 439 | end = end.slice( end.indexOf(")") +1 ); 440 | } 441 | 442 | if ( !start && !end ) { return; } 443 | 444 | // start or end are affine, or list of transform functions are identical 445 | // => functions will be interpolated individually 446 | if ( !start || !end || functionList(start) == functionList(end) ) { 447 | 448 | start && ( start = start.split(")") ) && ( l = start.length ); 449 | end && ( end = end.split(")") ) && ( l = end.length ); 450 | 451 | while ( ++i < l-1 ) { 452 | start[i] && ( currStart = start[i].split("(") ); 453 | end[i] && ( currEnd = end[i].split("(") ); 454 | currType = $.trim( ( currStart || currEnd )[0] ); 455 | 456 | append( list.start, parseFunction( currType, currStart ? currStart[1] : 0 ) ); 457 | append( list.end, parseFunction( currType, currEnd ? currEnd[1] : 0 ) ); 458 | } 459 | 460 | // otherwise, functions will be composed to a single matrix 461 | } else { 462 | list.start = unmatrix(matrix(start)); 463 | list.end = unmatrix(matrix(end)) 464 | } 465 | 466 | return list; 467 | } 468 | 469 | function parseFunction( type, value ) { 470 | var 471 | // default value is 1 for scale, 0 otherwise 472 | defaultValue = +(!type.indexOf(_scale)), 473 | scaleX, 474 | // remove X/Y from scaleX/Y & translateX/Y, not from skew 475 | cat = type.replace( /e[XY]/, "e" ); 476 | 477 | switch ( type ) { 478 | case _translate+"Y": 479 | case _scale+"Y": 480 | 481 | value = [ 482 | defaultValue, 483 | value ? 484 | parseFloat( value ): 485 | defaultValue 486 | ]; 487 | break; 488 | 489 | case _translate+"X": 490 | case _translate: 491 | case _scale+"X": 492 | scaleX = 1; 493 | case _scale: 494 | 495 | value = value ? 496 | ( value = value.split(",") ) && [ 497 | parseFloat( value[0] ), 498 | parseFloat( value.length>1 ? value[1] : type == _scale ? scaleX || value[0] : defaultValue+"" ) 499 | ]: 500 | [defaultValue, defaultValue]; 501 | break; 502 | 503 | case _skew+"X": 504 | case _skew+"Y": 505 | case _rotate: 506 | value = value ? toRadian( value ) : 0; 507 | break; 508 | 509 | case _matrix: 510 | return unmatrix( value ? toArray(value) : [1,0,0,1,0,0] ); 511 | break; 512 | } 513 | 514 | return [[ cat, value ]]; 515 | } 516 | 517 | function isAffine( matrix ) { 518 | return rAffine.test(matrix); 519 | } 520 | 521 | function functionList( transform ) { 522 | return transform.replace(/(?:\([^)]*\))|\s/g, ""); 523 | } 524 | 525 | function append( arr1, arr2, value ) { 526 | while ( value = arr2.shift() ) { 527 | arr1.push( value ); 528 | } 529 | } 530 | 531 | // converts an angle string in any unit to a radian Float 532 | function toRadian(value) { 533 | return ~value.indexOf("deg") ? 534 | parseInt(value,10) * (Math.PI * 2 / 360): 535 | ~value.indexOf("grad") ? 536 | parseInt(value,10) * (Math.PI/200): 537 | parseFloat(value); 538 | } 539 | 540 | // Converts "matrix(A,B,C,D,X,Y)" to [A,B,C,D,X,Y] 541 | function toArray(matrix) { 542 | // remove the unit of X and Y for Firefox 543 | matrix = /([^,]*),([^,]*),([^,]*),([^,]*),([^,p]*)(?:px)?,([^)p]*)(?:px)?/.exec(matrix); 544 | return [matrix[1], matrix[2], matrix[3], matrix[4], matrix[5], matrix[6]]; 545 | } 546 | 547 | $.transform = { 548 | centerOrigin: "margin" 549 | }; 550 | 551 | })( jQuery, window, document, Math ); 552 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "jquery-quicksand", 3 | "version": "1.6.0", 4 | "description": "jQuery plugin. Reorder and filter items with a nice shuffling animation.", 5 | "homepage": "https://razorjack.net/quicksand", 6 | "main": "jquery.quicksand.js", 7 | "repository": { 8 | "type": "git", 9 | "url": "https://github.com/razorjack/quicksand.git" 10 | }, 11 | "author": { 12 | "name": "Jacek Galanciak", 13 | "url": "https://razorjack.net/" 14 | }, 15 | "keywords": [ 16 | "frontend", 17 | "animation", 18 | "filtering", 19 | "reordering" 20 | ], 21 | "bugs": "https://github.com/razorjack/quicksand/issues", 22 | "main": "jquery-quicksand.js", 23 | "license": "(MIT OR GPL-2.0)", 24 | "dependencies": { 25 | "jquery": ">=1.6.1" 26 | }, 27 | "files": ["jquery.quicksand.js", "README.md", "CHANGELOG.md", "MIT-LICENSE.txt", "GPL-LICENSE.txt"] 28 | } 29 | --------------------------------------------------------------------------------