├── .editorconfig ├── .gitattributes ├── .gitignore ├── .npmignore ├── .travis.yml ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── GUIDELINES.md ├── LICENSE.md ├── README.md ├── api ├── burst.md ├── easing │ ├── approximate.md │ ├── base-functions.md │ ├── bezier-curves.md │ └── path-easing.md ├── html.md ├── readme.md ├── shape-swirl.md ├── shape.md ├── stagger.md ├── syntax │ ├── property-maps.md │ ├── rand.md │ ├── stagger.md │ └── units.md └── tweens │ ├── timeline.md │ └── tween.md ├── bower.json ├── build ├── mo.js └── mo.min.js ├── css ├── assets │ ├── colors.css │ ├── colors.styl │ ├── imports.css │ ├── imports.styl │ ├── kit.css │ ├── kit.styl │ ├── mixins.css │ ├── mixins.styl │ └── nesting-tree-traversal.js ├── css-assets │ ├── blocks.styl │ ├── effects.styl │ ├── general.styl │ ├── icons.styl │ ├── layouts.styl │ └── normalize.styl ├── icons.svg ├── kit.html ├── kit.jade ├── main.css ├── main.styl └── partials │ └── icon.jade ├── gulpfile.js ├── index.html ├── js ├── burst.babel.js ├── delta │ ├── delta.babel.js │ └── deltas.babel.js ├── easing │ ├── approximate-map.babel.js │ ├── approximate.babel.js │ ├── bezier-easing.coffee │ ├── easing.coffee │ ├── mix.coffee │ └── path-easing.coffee ├── h.coffee ├── html.babel.js ├── module.babel.js ├── mojs.babel.js ├── motion-path.coffee ├── polyfills │ ├── performance.coffee │ └── raf.coffee ├── shape-swirl.babel.js ├── shape.babel.js ├── shapes │ ├── bit.babel.js │ ├── circle.coffee │ ├── cross.coffee │ ├── curve.babel.js │ ├── custom.babel.js │ ├── equal.coffee │ ├── line.coffee │ ├── polygon.coffee │ ├── rect.coffee │ ├── shapesMap.coffee │ └── zigzag.coffee ├── spriter.babel.js ├── stagger.babel.js ├── thenable.babel.js ├── tunable.babel.js ├── tween │ ├── timeline.babel.js │ ├── tween.babel.js │ ├── tweenable.babel.js │ └── tweener.babel.js └── vendor │ └── resize.coffee ├── karma.conf.js ├── logo.png ├── mockups ├── sample.babel.js └── spring.coffee ├── package.json ├── spec ├── burst.coffee ├── burst.js ├── delta │ ├── delta.coffee │ ├── delta.js │ ├── deltas.coffee │ └── deltas.js ├── easing │ ├── approximate.coffee │ ├── approximate.js │ ├── bezier-easing.coffee │ ├── bezier-easing.js │ ├── easing.coffee │ ├── easing.js │ ├── path-easing.coffee │ └── path-easing.js ├── h.coffee ├── h.js ├── html.coffee ├── html.js ├── mix.coffee ├── mix.js ├── module.coffee ├── module.js ├── mojs.coffee ├── mojs.js ├── motion-path.coffee ├── motion-path.js ├── shape-swirl.coffee ├── shape-swirl.js ├── shape.coffee ├── shape.js ├── shapes │ ├── bit.coffee │ ├── bit.coffee.html │ ├── bit.js │ ├── circle.coffee │ ├── circle.coffee.html │ ├── circle.js │ ├── cross.coffee │ ├── cross.coffee.html │ ├── cross.js │ ├── curve.coffee │ ├── curve.js │ ├── custom.coffee │ ├── custom.js │ ├── equal.coffee │ ├── equal.coffee.html │ ├── equal.js │ ├── line.coffee │ ├── line.coffee.html │ ├── line.js │ ├── polygon.coffee │ ├── polygon.coffee.html │ ├── polygon.js │ ├── rect.coffee │ ├── rect.coffee.html │ ├── rect.js │ ├── shapesMap.coffee │ ├── shapesMap.js │ ├── zigzag.coffee │ ├── zigzag.coffee.html │ └── zigzag.js ├── spriter.coffee ├── spriter.js ├── stagger.coffee ├── stagger.js ├── thenable.coffee ├── thenable.js ├── tunable.coffee ├── tunable.js └── tween │ ├── pool.coffee │ ├── pool.js │ ├── timeline.coffee │ ├── timeline.js │ ├── tween.coffee │ ├── tween.js │ ├── tweenable.coffee │ ├── tweenable.js │ ├── tweener.coffee │ ├── tweener.coffee.html │ └── tweener.js ├── todo.md ├── vendor ├── benchmark.js ├── lodash.min.js └── platform.js └── webpack.config.js /.editorconfig: -------------------------------------------------------------------------------- 1 | # top-most editorconfig file 2 | root = true 3 | 4 | # editor configuration 5 | [*] 6 | charset = utf-8 7 | end_of_line = lf 8 | indent_style = space 9 | indent_size = 2 10 | trim_trailing_whitespace = true 11 | insert_final_newline = true 12 | 13 | # preserves markdown line break 14 | [*.md] 15 | trim_trailing_whitespace = false 16 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files by default 2 | * text=auto 3 | 4 | # End of line as the UNIX convention (LF: Line Feed) 5 | * eol=lf 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | bower_components/ 2 | node_modules/ 3 | coverage/ 4 | vendor/ 5 | npm-debug.log 6 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | coverage/ 3 | mockups/ 4 | vendor/ 5 | spec/ 6 | css/ 7 | js/ 8 | .travis.yml 9 | index.html 10 | index.jade 11 | bower.json 12 | npm-debug.log 13 | gulpfile.js 14 | karma.conf.js 15 | webpack.config.js 16 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "0.10" 4 | 5 | after_script: 6 | - if [[ `node --version` == *v0.10* ]]; then cat ./coverage/lcov-report/lcov.info | ./node_modules/coveralls/bin/coveralls.js; fi 7 | 8 | env: 9 | global: 10 | - SAUCE_USERNAME='$SAUCE_DEV_NAME' 11 | - SAUCE_ACCESS_KEY='$SAUCE_DEV_KEY' 12 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. 6 | 7 | ## Our Standards 8 | 9 | Examples of behavior that contributes to creating a positive environment include: 10 | 11 | * Using welcoming and inclusive language 12 | * Being respectful of differing viewpoints and experiences 13 | * Gracefully accepting constructive criticism 14 | * Focusing on what is best for the community 15 | * Showing empathy towards other community members 16 | 17 | Examples of unacceptable behavior by participants include: 18 | 19 | * The use of sexualized language or imagery and unwelcome sexual attention or advances 20 | * Trolling, insulting/derogatory comments, and personal or political attacks 21 | * Public or private harassment 22 | * Publishing others' private information, such as a physical or electronic address, without explicit permission 23 | * Other conduct which could reasonably be considered inappropriate in a professional setting 24 | 25 | ## Our Responsibilities 26 | 27 | Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. 28 | 29 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. 30 | 31 | ## Scope 32 | 33 | This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. 34 | 35 | ## Enforcement 36 | 37 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at xavier.foucrier@gmail.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. 38 | 39 | Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. 40 | 41 | ## Attribution 42 | 43 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] 44 | 45 | [homepage]: http://contributor-covenant.org 46 | [version]: http://contributor-covenant.org/version/1/4/ 47 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to mojs 2 | 3 | First of all, thank for taking time to contribute! 4 | 5 | The following is a set of guidelines for contributing to mojs. These are mostly guidelines, not rules. Use your best judgment, and feel free to propose changes to this document in a pull request. 6 | 7 | 8 | #### Table Of Contents 9 | 10 | 1. [Code of conduct](#code-of-conduct) 11 | 2. [Reporting bugs](#reporting-bugs) 12 | 3. [Suggesting enhancements](#suggesting-enhancements) 13 | 14 | 15 | ## Code of conduct 16 | 17 | This project and everyone participating in it is governed by the [mojs of conduct](CODE_OF_CONDUCT.md). By participating, you are expected to uphold this code. Please report unacceptable behavior to [xavier.foucrier@gmail.com](mailto:xavier.foucrier@gmail.com). 18 | 19 | 20 | ## Reporting bugs 21 | 22 | This section guides you through submitting a bug report for mojs. Following these guidelines helps maintainers and the community understand your report, reproduce the behavior, and find related reports. 23 | 24 | Before creating bug reports, please check the [issues list](https://github.com/mojs-contrib/mojs/issues) as you might find out that you don't need to create one. When you are creating a bug report, please include as many details as possible. 25 | 26 | ### Before submitting a bug report 27 | 28 | * **Perform a [cursory search](https://github.com/mojs-contrib/mojs/issues)** to see if the problem has already been reported. If it has **and the issue is still open**, add a comment to the existing issue instead of opening a new one. 29 | 30 | ### How do I submit a (good) bug report? 31 | 32 | Bugs are tracked as [GitHub issues](https://guides.github.com/features/issues). After you've determined which repository your bug is related to, create an issue on that repository and provide the following information by filling in the template. 33 | 34 | * Use a **clear and descriptive title** for the issue to identify the problem 35 | * Describe the **exact steps** which reproduce the problem in as many details as possible 36 | * Explain which behavior **you expected to see** instead and why 37 | * Explain the problem and **include additional details** to help maintainers reproduce the problem 38 | * Include details about your **configuration and environment** 39 | * Include screenshots and animated GIFs if needed 40 | * Specify which version of mojs you're using 41 | 42 | 43 | ## Suggesting enhancements 44 | 45 | This section guides you through submitting an enhancement suggestion for mojs, including completely new features and minor improvements to existing functionality. Following these guidelines helps maintainers and the community understand your suggestion and find related suggestions. 46 | 47 | * Use a **clear and descriptive title** for the issue to identify the suggestion 48 | * Provide a **step-by-step description of the suggested enhancement** in as many details as possible 49 | * Describe the current behavior and explain **which behavior you expected to see** instead and why 50 | * Explain **why this enhancement would be useful** to most mojs users 51 | * List some other applications **where this enhancement exists** 52 | 53 | Don't hesitate to use pull requests to propose code changes. 54 | 55 | Thanks for reading and happy contributing! :tada: :+1: 56 | -------------------------------------------------------------------------------- /GUIDELINES.md: -------------------------------------------------------------------------------- 1 | # Project guidelines 2 | These are the official **mojs-contrib** guidelines for the **mojs** library. 3 | 4 | ## Goals 5 | - propose an alternative to the official **mojs** library that is now deprecated and unsupported 6 | - keep the library **up to date** with dependencies 7 | - make mojs sparkle again with a lot of **new features** :tada: 8 | 9 | ## Timeline 10 | - build a solid team of contributors 11 | - make the repository "clean" (typo, dead links, etc.) 12 | - make the documentation "clean" (typo, dead links, etc.) 13 | - build a gh-pages website (clone of mojs.io), see [mojs-website](https://github.com/mojs-contrib/mojs-website) 14 | - fix critical bugs in the library + fix dependencies to make it "clean" and up to date 15 | - build an NPM package attached to the [@mojs-contrib](https://github.com/mojs-contrib) organization, probably starting at version @0.288.2 16 | - build an **official v1.0.0** 17 | - add features, tests, fix issues, etc. 18 | - then finally... start building a v2 19 | 20 | ## Contribute 21 | - there is a small [contributors guide](CONTRIBUTING.md), please read it before contributing 22 | - discussions needs to be made on the appropriate [Slack channel](https://mojs-contrib.slack.com): please don't use issues or pull requests for that 23 | - push access are only give to **people that have suffisant community contributions** on Github 24 | - if you want to contribute and don't have a push access, you can fork the repo and propose changes through pull requests 25 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | # MIT License 2 | 3 | Copyright (c) 2019 mojs-contrib 4 | Copyright (c) 2014-2018 Oleg Solomka 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # mo · js – [![npm](https://img.shields.io/npm/v/mo-js.svg)](https://www.npmjs.com/package/mo-js) [![Build Status](https://travis-ci.org/mojs-contrib/mojs.svg?branch=master)](https://travis-ci.org/mojs-contrib/mojs) [![Coverage Status](https://coveralls.io/repos/legomushroom/mojs/badge.svg?branch=master)](https://coveralls.io/r/legomushroom/mojs?branch=master) [![Slack Channel](https://img.shields.io/badge/style-join-ff69b4.svg?label=slack%20workspace)](https://join.slack.com/t/mojs-contrib/shared_invite/enQtNTQwNDg2NTgyNDg3LWExZjQ1MGMzOThjYzg1Njk5MThlYzcxZTMyM2NlYmJmNGYxM2U3ZTAyMDUzMWVlZWU2ZjBkYmFkMDU3ZmNkNjE) 2 | The motion graphics toolbelt for the web. 3 | 4 | ![mo · js](logo.png "mo · js") 5 | 6 | ## What's new? 7 | The project has **moved to https://github.com/mojs**. 8 | Please consider following the project on the official repository. 9 | 10 | > Note that this organization will remain open for archives. 11 | 12 | ## mo · js contrib 13 | This is the official repository of `mojs` since the official owner has decided to [drop support](https://twitter.com/legomushroom/status/1056990310218133504) on the original project. Please **star the repo to show your interest** and read the [project guidelines](GUIDELINES.md) to know a bit more about what is planning! 14 | 15 | The team of contributors is still under construction and **we are still waiting for "pros" to join**: give a smile and say hello on the Slack **mojs-contrib** workspace. Note that this project is supported in the spare time of most contributors: we will do our best to make mojs sparkle again! :tada: 16 | 17 | > Join the discussion on Slack [mojs-contrib workspace](https://join.slack.com/t/mojs-contrib/shared_invite/enQtNTQwNDg2NTgyNDg3LWExZjQ1MGMzOThjYzg1Njk5MThlYzcxZTMyM2NlYmJmNGYxM2U3ZTAyMDUzMWVlZWU2ZjBkYmFkMDU3ZmNkNjE) 18 | 19 | ## Demos 20 | - [Motion Graphics for the Web](https://codepen.io/sol0mka/full/ogOYJj/) 21 | - [Bubble Layout](https://codepen.io/sol0mka/full/yNOage/) 22 | - [Sleepy Mole](https://codepen.io/sol0mka/full/OyzBXR) 23 | - [Animocons](https://tympanus.net/Development/Animocons/) 24 | - [Love or Hate Modal](https://codepen.io/sol0mka/full/812699ce32c9a7aeb70c9384b32a533a/) 25 | - [Mograph](https://codepen.io/sol0mka/full/39427561a8a0b15d7896480a7d96d3d1/) 26 | - [Word Reveal](https://codepen.io/sol0mka/full/c94452fb65dbf676b0ae8a12d4267473/) 27 | - [Jump and Squash](https://codepen.io/sol0mka/full/pEagoL/) 28 | - [Physical Balls](https://codepen.io/sol0mka/full/7315f4364360ec87a6655d33782702fe/) 29 | - [Dust Trail](https://codepen.io/sol0mka/full/633e6aa52d40691cca2f2cda91650bae/) 30 | - [Bubble Modal](https://codepen.io/sol0mka/full/3c49de2d7d0ca3e92bf5db5bf7a2687d/) 31 | - [Bubbles](https://codepen.io/sol0mka/full/2ef10ed42ff535182c31cd1dbb81e453/) 32 | - [Blast](https://codepen.io/sol0mka/full/699cfc8716a13e0e1c15105af2b6fb95/) (click to see) 33 | - [Simple Burst](https://codepen.io/sol0mka/full/6caf96461207a5caa9226fbd2631569d/) (click to see) 34 | - [Dusty Burst](https://codepen.io/sol0mka/full/03e9d8f2fbf886aa1505c61c81d782a0/) (click to see) 35 | - [Twitter Fav](https://codepen.io/sol0mka/full/wWdRLk/) (click to see) 36 | - [Twitter Fav (stars)](https://codepen.io/sol0mka/full/PzmAym/) (click to see) 37 | - [Twitter Fav Firework](https://codepen.io/sol0mka/full/xOAKKA/) (click to see) 38 | - [Simple Ripple](https://codepen.io/sol0mka/full/XKdWJg/) (click to see) 39 | 40 | ## Tutorials 41 | - Shape & Swirl (broken link) [API/shape](/api/shape.md) 42 | - Burst (broken link) [API/burst](/api/burst.md) 43 | - Path Easing (broken link) [API/easing/path-easing](/api/easing/path-easing.md) 44 | - [Video with MojsPlayer and MojsCurveEditor](https://vimeo.com/185587462) 45 | - [Icon Animations Powered by mo.js](https://tympanus.net/codrops/2016/02/23/icon-animations-powered-by-mo-js/) 46 | - [An Introduction to mo.js, by Sarah Drasner](https://css-tricks.com/introduction-mo-js/) 47 | - [Web Animations and Mo.js with Sarah Drasner](https://www.youtube.com/watch?v=yRxWa8lXasI) *[Video]* 48 | 49 | ## Documentation 50 | - [Reference](/api) 51 | 52 | ## Tools 53 | - [Player](https://github.com/mojs-contrib/mojs-player) 54 | - [Curve editor](https://github.com/mojs-contrib/mojs-curve-editor) 55 | - [Timeline editor](https://github.com/mojs-contrib/mojs-timeline-editor) 56 | 57 | ## Installation 58 | npm: `npm install mo-js` 59 | cdn: `` 60 | bower: `bower install mojs` 61 | 62 | ## Target browsers 63 | - Chrome 4+ 64 | - Firefox 4+ 65 | - Opera 11.5+ 66 | - Safari 4+ 67 | - IE 9+ 68 | 69 | ## Kudos 70 | Meet some of the outstanding guys that support `mojs` on [Patreon](https://patreon.com/user?u=3219311&utm_medium=social&utm_source=twitter&utm_campaign=creatorshare): 71 | 72 | - [Zak Frisch](https://github.com/zfrisch) 73 | - [Erhan Karadeniz](https://twitter.com/erhankaradeniz) 74 | - [Jorge Antunes](https://github.com/stoikerty) 75 | - [Daniel C. Henning](https://github.com/danielsdesk) 76 | - [Chris Dolphin](https://github.com/likethemammal) 77 | - [Volodymyr Kushnir](https://twitter.com/VovaKushnir) 78 | - [Wojtek Jodel]() 79 | - [Roman Kuba](https://github.com/codebryo) 80 | -------------------------------------------------------------------------------- /api/burst.md: -------------------------------------------------------------------------------- 1 | # Burst 2 | 3 | - [CodePen Example](https://codepen.io/sol0mka/pen/JKWKVR?editors=0010) 4 | - [ShapeSwirl API](./shape-swirl.md) 5 | - [Stagger Strings API](./syntax/stagger.md) 6 | - [Property Maps API](./syntax/property-maps.md) 7 | - [Tween API](./tweens/tween.md) 8 | - [Timeline API](./tweens/timeline.md) 9 | - [back](./readme.md) 10 | 11 | Full API reference: 12 | 13 | ```javascript 14 | const burst = new mojs.Burst({ 15 | 16 | /* BURST PROPERTIES */ 17 | 18 | // Parent of the module. {String, Object} [selector, HTMLElement] 19 | parent: document.body, 20 | 21 | // Class name. {String} 22 | className: '', 23 | 24 | // ∆ , Units :: Left position of the module. {Number, String} 25 | left: '50%', 26 | 27 | // ∆ , Units :: Top position of the module. {Number, String} 28 | top: '50%', 29 | 30 | // ∆ , Units :: X shift. {Number, String} 31 | x: 0, 32 | 33 | // ∆ , Units :: Y shift. {Number, String} 34 | y: 0, 35 | 36 | // ∆ :: Angle. {Number, String} 37 | angle: 0, 38 | 39 | // ∆ :: Scale of the module. {Number} 40 | scale: 1, 41 | 42 | // ∆ :: Explicit scaleX value (fallbacks to `scale`). {Number} 43 | scaleX: null, 44 | 45 | // ∆ :: Explicit scaleX value (fallbacks to `scale`). {Number} 46 | scaleY: null, 47 | 48 | // ∆ , Unit :: Origin for `x`, `y`, `scale`, `rotate` properties. {String} 49 | origin: '50% 50%', 50 | 51 | // ∆ :: Opacity. {Number} [ 0..1 ] 52 | opacity: 1, 53 | 54 | /* 55 | Radius of the radial shape that child particles form. Note that it has different meaning compared to shape-swirl. Burst `radius` defines radius of the children module 56 | */ 57 | radius: null, 58 | 59 | // Quantity of Burst particles. {Number} [ > 0 ] 60 | count: 5, 61 | 62 | // Degree of circlular shape that the particles form. {Number} [ > 0 ] 63 | degree: 360, 64 | 65 | // ∆ :: Radius of the Burst. {Number} 66 | radius: { 0: 50 }, 67 | 68 | // ∆ :: Radius X of the Burst (fallbacks to `radius`). {Number} 69 | radiusX: null, 70 | 71 | // ∆ :: Radius Y of the Burst (fallbacks to `radius`). {Number} 72 | radiusY: null, 73 | 74 | // If should hide module with `transforms` instead of `display`. {Boolean} 75 | isSoftHide: true, 76 | 77 | // If should trigger composite layer for the module. {Boolean} 78 | isForce3d: false, 79 | 80 | // If should be shown before animation starts. {Boolean} 81 | isShowStart: false, 82 | 83 | // If should stay shown after animation ends. {Boolean} 84 | isShowEnd: true, 85 | 86 | // If refresh state on subsequent plays. {Boolean} 87 | isRefreshState: true, 88 | 89 | /* 90 | Options for each children ShapeSwirl element. {Object} 91 | Supports `Stagger` strings for numeric values and `Property Maps` overall. 92 | see `Stagger Strings` and `Property Maps` section for more info. 93 | */ 94 | children: { 95 | /* (+) SHAPE SWIRL PROPERTIES AND CALLBACKS (excluding `x` and `y`) - see ShapeSwirl API */ 96 | } 97 | 98 | // Options for timeline that controls all child and main Shape Swirls. {Object} 99 | timeline: { 100 | /* (+) TIMELINE PROPERTIES AND CALLBACKS - see Tween API */ 101 | } 102 | 103 | }) 104 | 105 | /* 106 | Creates next state transition chain. 107 | @param options {Object} Next shape state. 108 | */ 109 | .then({ /* next state options */ }) 110 | 111 | /* 112 | Tunes start state with new options. 113 | @param options {Object} New start properties. 114 | */ 115 | .tune({ /* new start properties */ }) 116 | 117 | /* 118 | Regenerates all randoms in initial properties. 119 | */ 120 | .generate() 121 | 122 | /* 123 | Starts playback. 124 | @param shift {Number} Start progress shift in milliseconds. 125 | */ 126 | .play( shift = 0 ) 127 | /* 128 | Starts playback in backward direction. 129 | @param shift {Number} Start progress shift in milliseconds. 130 | */ 131 | .playBackward( shift = 0 ) 132 | /* 133 | Pauses playback. 134 | */ 135 | .pause() 136 | /* 137 | Restarts playback. 138 | @param shift {Number} Start progress shift in milliseconds. 139 | */ 140 | .replay( shift = 0 ) 141 | /* 142 | Restarts playback in backward direction. 143 | @param shift {Number} Start progress shift in milliseconds. 144 | */ 145 | .replayBackward( shift = 0 ) 146 | /* 147 | Resumes playback in direction it was prior to `pause`. 148 | @param shift {Number} Start progress shift in milliseconds. 149 | */ 150 | .resume( shift = 0 ) 151 | /* 152 | Sets progress of the tween. 153 | @param progress {Number} Progress to set [ 0..1 ]. 154 | */ 155 | .setProgress( progress ) 156 | /* 157 | Sets speed of the tween. 158 | @param speed {Number} Progress to set [ 0..∞ ]. 159 | */ 160 | setSpeed ( speed ) 161 | 162 | /* Stops and resets the tween. */ 163 | reset ( speed ) 164 | 165 | ``` 166 | 167 | - [CodePen Example](https://codepen.io/sol0mka/pen/JKWKVR?editors=0010) 168 | - [ShapeSwirl API](./shape-swirl.md) 169 | - [Stagger Strings API](./syntax/stagger.md) 170 | - [Property Maps API](./syntax/property-maps.md) 171 | - [Tween API](./tweens/tween.md) 172 | - [Timeline API](./tweens/timeline.md) 173 | - [back](./readme.md) 174 | -------------------------------------------------------------------------------- /api/easing/approximate.md: -------------------------------------------------------------------------------- 1 | # Approximate 2 | 3 | - [CodePen Example](https://codepen.io/sol0mka/pen/LZWRMm?editors=0010) 4 | - [back](/api/readme.md) 5 | 6 | The `approximate` function samples any given function with slow running time and returns new easing function with very fast one. The result still slower than `base easing functions` and may contain a tiny approximation error (by default less than `0.0001`). 7 | 8 | The syntax: 9 | 10 | ```javascript 11 | var fastEasing = mojs.easing.approximate( slowEasing, n = 4 ); 12 | // where `n` is optional quantity of samples as `10^n` (larger `n` - smaller error). 13 | ``` 14 | 15 | Another strategy for the `approximate` function is to feed it with precomputed `JSON` data to same `CPU` pressure from presampling the slow function: 16 | 17 | ```javascript 18 | var samples = require('./samples.json'); 19 | var fastEasing = mojs.easing.approximate( slowEasing, samples ); 20 | // where `samples` is `JSON` object that contains presampled data. 21 | ``` 22 | 23 | You can have the presampled data by calling `getSamples` function: 24 | 25 | ```javascript 26 | var fastEasing = mojs.easing.approximate( slowEasing ); 27 | var samples = fastEasing.getSamples(); 28 | ``` 29 | 30 | - [CodePen Example](https://codepen.io/sol0mka/pen/LZWRMm?editors=0010) 31 | - [back](/api/readme.md) 32 | -------------------------------------------------------------------------------- /api/easing/base-functions.md: -------------------------------------------------------------------------------- 1 | # Base Easing Functions 2 | 3 | - [CodePen Example](https://codepen.io/sol0mka/pen/qNraVV?editors=0010) 4 | - [back](/api/readme.md) 5 | 6 | The base easing functions could be expressed with strings that contain `easing name` and `direction` delimited by `.` or could be accessed directly on `mojs.easing` object: 7 | 8 | ```javascript 9 | // ... 10 | easing: 'cubic.in', 11 | // or 12 | easing: mojs.easing.cubic.in 13 | // ... 14 | ``` 15 | 16 | The full list of base functions: 17 | 18 | ```javascript 19 | 20 | 'linear.none' 21 | 22 | 'ease.in' 23 | 'ease.out' 24 | 'ease.inout' 25 | 26 | 'sin.in' 27 | 'sin.out' 28 | 'sin.inout' 29 | 30 | 'quad.in' 31 | 'quad.out' 32 | 'quad.inout' 33 | 34 | 'cubic.in' 35 | 'cubic.out' 36 | 'cubic.inout' 37 | 38 | 'quart.in' 39 | 'quart.out' 40 | 'quart.inout' 41 | 42 | 'quint.in' 43 | 'quint.out' 44 | 'quint.inout' 45 | 46 | 'expo.in' 47 | 'expo.out' 48 | 'expo.inout' 49 | 50 | 'circ.in' 51 | 'circ.out' 52 | 'circ.inout' 53 | 54 | 'back.in' 55 | 'back.out' 56 | 'back.inout' 57 | 58 | 'elastic.in' 59 | 'elastic.out' 60 | 'elastic.inout' 61 | 62 | 'bounce.in' 63 | 'bounce.out' 64 | 'bounce.inout' 65 | 66 | ``` 67 | 68 | - [CodePen Example](https://codepen.io/sol0mka/pen/qNraVV?editors=0010) 69 | - [back](/api/readme.md) 70 | -------------------------------------------------------------------------------- /api/easing/bezier-curves.md: -------------------------------------------------------------------------------- 1 | # Bezier Curves 2 | 3 | - [CodePen Example](https://codepen.io/sol0mka/pen/BzWLre?editors=0010) 4 | - [back](/api/readme.md) 5 | 6 | The bezier curves functions can be expressed with string containing `bezier()` function or with `mojs.easing.bezier` constructor that returns a function: 7 | 8 | ```javascript 9 | // ... 10 | easing: 'bezier()', 11 | // or 12 | easing: mojs.easing.path('M0,100 C50,100 50,67.578125 50,50 C50,32.421875 50,0 100,0') 13 | // ... 14 | ``` 15 | 16 | - [CodePen Example](https://codepen.io/sol0mka/pen/BzWLre?editors=0010) 17 | - [back](/api/readme.md) 18 | -------------------------------------------------------------------------------- /api/easing/path-easing.md: -------------------------------------------------------------------------------- 1 | # Path Easing 2 | 3 | - [CodePen Example](https://codepen.io/sol0mka/pen/KMWgQp?editors=0010) 4 | - [back](/api/readme.md) 5 | 6 | The path easing functions can be expressed with string containing `svg` path cordinates or with `easing.path` constructor that returns a function: 7 | 8 | ```javascript 9 | // ... 10 | easing: 'M0,100 C50,100 50,67.578125 50,50 C50,32.421875 50,0 100,0', 11 | // or 12 | easing: mojs.easing.path('M0,100 C50,100 50,67.578125 50,50 C50,32.421875 50,0 100,0') 13 | // ... 14 | ``` 15 | 16 | - [CodePen Example](https://codepen.io/sol0mka/pen/KMWgQp?editors=0010) 17 | - [back](/api/readme.md) 18 | -------------------------------------------------------------------------------- /api/readme.md: -------------------------------------------------------------------------------- 1 | # APIs 2 | 3 | ## Modules 4 | - [Html](./html.md) 5 | - [Shape](./shape.md) 6 | - [ShapeSwirl](./shape-swirl.md) 7 | - [Burst](./burst.md) 8 | - [stagger](./stagger.md) 9 | - MotionPath(soon) 10 | - Spriter(soon) 11 | - Radial Spring(soon) 12 | - Spring Wave(soon) 13 | - Goo Transfroms(soon) 14 | - Broom(soon) 15 | 16 | ## Tweens 17 | 18 | - [Tween](./tweens/tween.md) 19 | - [Timeline](./tweens/timeline.md) 20 | 21 | ## Easing 22 | - [Base Functions](./easing/base-functions.md) 23 | - [Bezier Curves](./easing/bezier-curves.md) 24 | - [Path Easing](./easing/path-easing.md) 25 | - Springs(soon) 26 | - [Approximate](./easing/approximate.md) 27 | - Transforms(soon) 28 | - Mixes(soon) 29 | 30 | ## Syntax 31 | - [Stagger Strings](./syntax/stagger.md) 32 | - [Rand Strings](./syntax/rand.md) 33 | - [Property Maps](./syntax/property-maps.md) 34 | - [Available Units](./syntax/units.md) 35 | 36 | ## License 37 | 38 | (The MIT License) 39 | 40 | Copyright (c) Oleg Solomka [@LegoMushroom](https://twitter.com/legomushroom) [legomushroom@gmail.com](mailto:legomushroom@gmail.com) 41 | 42 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the 'Software'), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 43 | 44 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 45 | 46 | THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 47 | -------------------------------------------------------------------------------- /api/shape-swirl.md: -------------------------------------------------------------------------------- 1 | # ShapeSwirl 2 | 3 | - [CodePen Example](https://codepen.io/sol0mka/pen/pbebwQ?editors=0010) 4 | - [Shape API](./shape.md) 5 | - [Tween API](./tweens/tween.md) 6 | - [back](./readme.md) 7 | 8 | Full API reference: 9 | 10 | ```javascript 11 | const shapeSwirl = new mojs.ShapeSwirl({ 12 | 13 | // ∆ :: Diviation size of sine. {Number} 14 | swirlSize: 10, 15 | 16 | // ∆ :: Frequency of sine. {Number} 17 | swirlFrequency: 3, 18 | 19 | // ∆ :: Sine length scale. {Number} [ 0..1 ] 20 | pathScale: 1, 21 | 22 | // ∆ :: Degree shift for sine path. {Number} 23 | degreeShift: 0, 24 | 25 | // [number: -1, 1] :: Directon of sine. {Number} [ -1, 1 ] 26 | direction: 1 27 | 28 | // If shape should follow sinusoidal path. {Boolean} 29 | isSwirl: true 30 | 31 | /* (+) SHAPE PROPERTIES AND CALLBACKS - see Shape API */ 32 | 33 | }) 34 | 35 | /* 36 | Creates next state transition chain. 37 | @param options {Object} Next shape state. 38 | */ 39 | .then({ /* next state options */ }) 40 | 41 | /* 42 | Tunes start state with new options. 43 | @param options {Object} New start properties. 44 | */ 45 | .tune({ /* new start properties */ }) 46 | 47 | /* 48 | Regenerates all randoms in initial properties. 49 | */ 50 | .generate() 51 | 52 | /* 53 | Starts playback. 54 | @param shift {Number} Start progress shift in milliseconds. 55 | */ 56 | .play( shift = 0 ) 57 | /* 58 | Starts playback in backward direction. 59 | @param shift {Number} Start progress shift in milliseconds. 60 | */ 61 | .playBackward( shift = 0 ) 62 | /* 63 | Pauses playback. 64 | */ 65 | .pause() 66 | /* 67 | Restarts playback. 68 | @param shift {Number} Start progress shift in milliseconds. 69 | */ 70 | .replay( shift = 0 ) 71 | /* 72 | Restarts playback in backward direction. 73 | @param shift {Number} Start progress shift in milliseconds. 74 | */ 75 | .replayBackward( shift = 0 ) 76 | /* 77 | Resumes playback in direction it was prior to `pause`. 78 | @param shift {Number} Start progress shift in milliseconds. 79 | */ 80 | .resume( shift = 0 ) 81 | /* 82 | Sets progress of the tween. 83 | @param progress {Number} Progress to set [ 0..1 ]. 84 | */ 85 | .setProgress( progress ) 86 | /* 87 | Sets speed of the tween. 88 | @param speed {Number} Progress to set [ 0..∞ ]. 89 | */ 90 | setSpeed ( speed ) 91 | 92 | /* Stops and resets the tween. */ 93 | reset ( speed ) 94 | 95 | ``` 96 | 97 | - [CodePen Example](https://codepen.io/sol0mka/pen/pbebwQ?editors=0010) 98 | - [Shape API](./shape.md) 99 | - [Tween API](./tweens/tween.md) 100 | - [back](./readme.md) 101 | -------------------------------------------------------------------------------- /api/syntax/property-maps.md: -------------------------------------------------------------------------------- 1 | # Property Map Syntax 2 | 3 | - [CodePen Example](https://codepen.io/sol0mka/pen/WxpGNm?editors=0010) 4 | - [back](/api/readme.md) 5 | 6 | `Property Map` array was designed to express sequential values. Often used with `Burst` and `Stagger` modules to generate values that repeat over children length. Basically it is just an array that maps its values to children based on child index with `mod` function. So if you have `property map` with `3 values` and `5 children`, then `4`th and `5`th items will recieve `0`th and `1`st values from the map respecively. Works with any values inside the array. 7 | 8 | 9 | Full API reference: 10 | 11 | ```javascript 12 | // ... 13 | property : [ 20, { 20 : 0 }, 'rand(0, 20)' ] 14 | // ... 15 | 16 | ``` 17 | 18 | - [CodePen Example](https://codepen.io/sol0mka/pen/WxpGNm?editors=0010) 19 | - [back](/api/readme.md) 20 | -------------------------------------------------------------------------------- /api/syntax/rand.md: -------------------------------------------------------------------------------- 1 | # Rand Strings Syntax 2 | 3 | - [CodePen Example](https://codepen.io/sol0mka/pen/QEpKwP?editors=0010) 4 | - [back](/api/readme.md) 5 | 6 | `Rand` string was designed to express random numeric values. Can be unit based (percents, pixels, rems etc.). 7 | 8 | 9 | Full API reference: 10 | 11 | ```javascript 12 | // ... 13 | property : 'rand(min, max)', 14 | // ... 15 | 16 | ``` 17 | 18 | - [CodePen Example](https://codepen.io/sol0mka/pen/QEpKwP?editors=0010) 19 | - [back](/api/readme.md) 20 | -------------------------------------------------------------------------------- /api/syntax/stagger.md: -------------------------------------------------------------------------------- 1 | # Stagger Strings Syntax 2 | 3 | - [CodePen Example](https://codepen.io/sol0mka/pen/wWJWVY?editors=0010) 4 | - [back](/api/readme.md) 5 | 6 | Stagger string was designed to express continious numeric values. Often used with `Burst` and `Stagger` modules to generate a value on children with some `step`. 7 | 8 | Takes 2 parameters: 9 | 10 | 1. `start` - start value for all children. 11 | 2. `step` - step of addition. It gets multiplied by child index and added to the `start`. 12 | 13 | Full API reference: 14 | 15 | ```javascript 16 | // ... 17 | property : 'stagger( start, step )' 18 | // ... 19 | 20 | ``` 21 | 22 | - [CodePen Example](https://codepen.io/sol0mka/pen/wWJWVY?editors=0010) 23 | - [back](/api/readme.md) 24 | -------------------------------------------------------------------------------- /api/syntax/units.md: -------------------------------------------------------------------------------- 1 | # Available Units 2 | 3 | - [CodePen Example](https://codepen.io/sol0mka/pen/JKWRYW?editors=0010) 4 | - [back](/api/readme.md) 5 | 6 | 7 | The list of available units: `px|%|rem|em|ex|cm|ch|mm|in|pt|pc|vh|vw|vmin`. 8 | 9 | If `start` and `end` units of `delta` are different - `mojs` will fallback to `end` units. 10 | 11 | Usage with properties: 12 | 13 | ```javascript 14 | // ... 15 | property : '20rem', 16 | property2 : { '10%' : '100%' }, 17 | // ... 18 | 19 | ``` 20 | 21 | - [CodePen Example](https://codepen.io/sol0mka/pen/JKWRYW?editors=0010) 22 | - [back](/api/readme.md) 23 | -------------------------------------------------------------------------------- /api/tweens/timeline.md: -------------------------------------------------------------------------------- 1 | # Timeline 2 | 3 | - [CodePen Example](https://codepen.io/sol0mka/pen/LZWZON?editors=0011) 4 | - [Tween API](./tween.md) 5 | - [back](/api/readme.md) 6 | 7 | `Timeline` inherits all properties, callbacks and public methods from `Tween`, the only difference between `Tween` and `Timeline` is that the later doesn't have the `duration` property - it gets calculated from durations/delays of children. Also `Timeline` add two public methods that can recieve children. Please check the Tween API for reference. 8 | 9 | Full API reference: 10 | 11 | ```javascript 12 | const timeline = new mojs.Timeline({ 13 | 14 | /* PROPERTIES */ 15 | 16 | /* (+) TWEEN PROPERTIES AND CALLBACKS - see Tween API */ 17 | 18 | /* 19 | Note: The timeline inherits all tween properties, callbacks and public methods excluding `duration` property. The `duration` property is computed automatically regarding children tweens and timelines. 20 | */ 21 | duration: null 22 | 23 | }) 24 | 25 | /* PUBLIC METHODS */ 26 | 27 | /* 28 | Adds children tweens/timelines to the timeline. 29 | @param children {Object, Array} Tweens/Timelines or array of such. 30 | */ 31 | .add( tween ) {} 32 | /* 33 | Appends children tweens/timelines to the timeline after the current children. 34 | @param children {Object, Array} Tweens/Timelines or array of such. 35 | */ 36 | .append( tween ) {} 37 | 38 | /* (+) TWEEN PUBLIC METHODS - see Tween API */ 39 | 40 | ``` 41 | 42 | - [CodePen Example](https://codepen.io/sol0mka/pen/LZWZON?editors=0011) 43 | - [Tween API](./tween.md) 44 | - [back](/api/readme.md) 45 | -------------------------------------------------------------------------------- /api/tweens/tween.md: -------------------------------------------------------------------------------- 1 | # Tween 2 | 3 | - [CodePen Example](https://codepen.io/sol0mka/pen/MepeEx?editors=0011) 4 | - [Timeline](./timeline.md) 5 | - [back](/api/readme.md) 6 | 7 | Full API reference: 8 | 9 | ```javascript 10 | const tween = new mojs.Tween({ 11 | 12 | /* PROPERTIES */ 13 | 14 | // Duration {Number} 15 | duration: 350, 16 | // Delay {Number} 17 | delay: 0, 18 | // If should repeat after animation finished {Number} *(1) 19 | repeat: 0, 20 | // Speed of the tween {Number}[0..∞] 21 | speed: 1, 22 | // If the progress should be flipped on repeat animation end {Boolean} 23 | isYoyo: false, 24 | // Easing function {String, Function}[ easing name, path coordinates, bezier string, easing function ] 25 | easing: 'sin.out', 26 | // Easing function for backward direction of the tween animation (fallbacks to `easing`) {String, Function}[ easing name, path coordinates, bezier string, easing function ] 27 | backwardEasing: null, 28 | 29 | /* CALLBACKS (in order of firing) */ 30 | 31 | /* 32 | Fires on every when progress needs an update. For instance when tween was finished an remains in `1` progress state, and you will play it again - it will stay in the `1` state until first sufficient update after delay. So the `onRefresh` callback serves you to `refresh` the `1` state with `0` update. 33 | 34 | @param isBefore {Boolean} If `true` - the refresh is before start time. 35 | */ 36 | onRefresh (p, isForward, isYoyo) {}, 37 | 38 | /* 39 | Fires on every update of the tween in any period (including delay periods). You probably want to use `onUpdate` method instead. 40 | @param p {Number} Normal (not eased) progress. 41 | @param isForward {Boolean} Direction of the progress. 42 | @param isYoyo {Boolean} If in `yoyo` period. 43 | */ 44 | onProgress (p, isForward, isYoyo) {}, 45 | /* 46 | Fires when tween's the entire progress reaches `0` point(doesn't fire in repeat periods). 47 | @param isForward {Boolean} If progress moves in forward direction. 48 | @param isYoyo {Boolean} If progress inside `yoyo` flip period. 49 | */ 50 | onStart (isForward, isYoyo) {}, 51 | /* 52 | Fires when tween's the progress reaches `0` point in normal or repeat period. 53 | @param isForward {Boolean} If progress moves in forward direction. 54 | @param isYoyo {Boolean} If progress inside `yoyo` flip period. 55 | */ 56 | onFirstUpdate (isForward, isYoyo) {}, 57 | /* 58 | Fires on first update of the tween in sufficiently active period (excluding delay periods). 59 | @param ep {Number} Eased progress. 60 | @param p {Number} Normal (not eased) progress. 61 | @param isForward {Boolean} Direction of the progress. 62 | @param isYoyo {Boolean} If in `yoyo` period. 63 | */ 64 | onUpdate (ep, p, isForward, isYoyo) {}, 65 | /* 66 | Fires when tween's the progress reaches `1` point in normal or repeat period. 67 | @param isForward {Boolean} If progress moves in forward direction. 68 | @param isYoyo {Boolean} If progress inside `yoyo` flip period. 69 | */ 70 | onRepeatComplete (isForward, isYoyo) {}, 71 | /* 72 | Fires when tween's the entire progress reaches `1` point(doesn't fire in repeat periods). 73 | @param isForward {Boolean} If progress moves in forward direction. 74 | @param isYoyo {Boolean} If progress inside `yoyo` flip period. 75 | */ 76 | onComplete (isForward, isYoyo) {}, 77 | /* Fires when the `.play` method called and tween isn't in play state yet. */ 78 | onPlaybackStart () {}, 79 | /* Fires when the `.pause` method called and tween isn't in pause state yet. */ 80 | onPlaybackPause () {}, 81 | /* Fires when the `.stop` method called and tween isn't in stop state yet. */ 82 | onPlaybackStop () {}, 83 | /* Fires when the tween end's animation (regardless progress) */ 84 | onPlaybackComplete () {}, 85 | }) 86 | /* PUBLIC METHODS */ 87 | 88 | /* 89 | Starts playback. 90 | @param shift {Number} Start progress shift in milliseconds. 91 | */ 92 | .play( shift = 0 ) 93 | /* 94 | Starts playback in backward direction. 95 | @param shift {Number} Start progress shift in milliseconds. 96 | */ 97 | .playBackward( shift = 0 ) 98 | 99 | /* 100 | Stops playback. 101 | @param progress {Number} Progress to set after stop [0..1]. *Default* is `0`. 102 | */ 103 | .stop( progress = 0 ) 104 | /* 105 | Pauses playback. 106 | */ 107 | .pause() 108 | /* 109 | Restarts playback. 110 | @param shift {Number} Start progress shift in milliseconds. 111 | */ 112 | .replay( shift = 0 ) 113 | /* 114 | Restarts playback in backward direction. 115 | @param shift {Number} Start progress shift in milliseconds. 116 | */ 117 | .replayBackward( shift = 0 ) 118 | /* 119 | Resumes playback in direction it was prior to `pause`. 120 | @param shift {Number} Start progress shift in milliseconds. 121 | */ 122 | .resume( shift = 0 ) 123 | /* 124 | Sets progress of the tween. 125 | @param progress {Number} Progress to set [ 0..1 ]. 126 | */ 127 | .setProgress( progress ) 128 | /* 129 | Sets speed of the tween. 130 | @param speed {Number} Progress to set [ 0..∞ ]. 131 | */ 132 | .setSpeed( speed ) 133 | 134 | /* Stops and resets the tween state. */ 135 | .reset() 136 | 137 | ``` 138 | 139 | - [CodePen Example](https://codepen.io/sol0mka/pen/MepeEx?editors=0011) 140 | - [Timeline](./timeline.md) 141 | - [back](/api/readme.md) 142 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mojs", 3 | "description": "motion graphics toolbelt for the web", 4 | "version": "0.288.2", 5 | "license": "MIT", 6 | "homepage": "https://github.com/legomushroom/mojs", 7 | "authors": [ 8 | "Oleg Solomka @legomushroom " 9 | ], 10 | "main": "build/mo.min.js", 11 | "keywords": [ 12 | "motion", 13 | "graphics", 14 | "animation", 15 | "effects" 16 | ], 17 | "ignore": [ 18 | "**/.*", 19 | "bower_components", 20 | "node_modules", 21 | "coverage", 22 | "docs", 23 | "spec", 24 | "css", 25 | "js" 26 | ], 27 | "moduleType": [ 28 | "amd", 29 | "globals", 30 | "node" 31 | ] 32 | } 33 | -------------------------------------------------------------------------------- /css/assets/colors.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mojs-contrib/mojs/856bf4c4cfdcabab3a78c18dfd419d429b78817e/css/assets/colors.css -------------------------------------------------------------------------------- /css/assets/colors.styl: -------------------------------------------------------------------------------- 1 | 2 | 3 | stepSize = 10 4 | c-grey1 = #232323 5 | 6 | c-grey2 = lighten(c-grey1, stepSize) 7 | c-grey2-hover = lighten(c-grey2, 5) 8 | 9 | c-grey3 = lighten(c-grey2, stepSize) 10 | c-grey3-hover = lighten(c-grey3, 5) 11 | 12 | c-grey4 = lighten(c-grey3, stepSize) 13 | c-grey4-hover = lighten(c-grey4, 5) 14 | 15 | c-grey5 = lighten(c-grey4, stepSize) 16 | c-grey5-hover = lighten(c-grey5, 5) 17 | 18 | c-grey6 = lighten(c-grey5, stepSize) 19 | c-grey6-hover = lighten(c-grey6, 5) 20 | 21 | c-grey7 = lighten(c-grey6, stepSize) 22 | c-grey7-hover = lighten(c-grey7, 5) 23 | 24 | c-grey8 = lighten(c-grey7, stepSize) 25 | c-grey8-hover = lighten(c-grey8, 5) 26 | 27 | c-grey9 = lighten(c-grey8, stepSize) 28 | c-grey9-hover = lighten(c-grey9, 5) 29 | 30 | c-grey10 = lighten(c-grey9, stepSize) 31 | c-grey10-hover = lighten(c-grey10, 5) 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /css/assets/imports.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mojs-contrib/mojs/856bf4c4cfdcabab3a78c18dfd419d429b78817e/css/assets/imports.css -------------------------------------------------------------------------------- /css/assets/imports.styl: -------------------------------------------------------------------------------- 1 | @import 'mixins' 2 | @import 'colors' -------------------------------------------------------------------------------- /css/assets/kit.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin-bottom: 160px; 3 | background: c-bg; 4 | } 5 | .hash-e { 6 | font-size: 16px; 7 | color: #717171; 8 | margin-bottom: 8px; 9 | margin-top: 16px; 10 | max-width: 100%; 11 | padding: 8px 0; 12 | white-space: nowrap; 13 | -webkit-text-overflow: ellipsis; 14 | -moz-text-overflow: ellipsis; 15 | -o-text-overflow: ellipsis; 16 | text-overflow: ellipsis; 17 | overflow: hidden; 18 | width: 64px; 19 | } 20 | .icon-sample-kl { 21 | float: left; 22 | margin: 16px; 23 | } 24 | .main-kl { 25 | max-width: 800px; 26 | margin: 0 auto; 27 | padding: 32px 0; 28 | } 29 | .section-kl + .section-kl { 30 | margin-top: 64px; 31 | } 32 | .header-ke { 33 | font-size: 26px; 34 | padding: 16px 0; 35 | color: #232323; 36 | } 37 | .color-sample-ke { 38 | width: 32px; 39 | height: 32px; 40 | margin: 8px; 41 | float: left; 42 | } 43 | -------------------------------------------------------------------------------- /css/assets/kit.styl: -------------------------------------------------------------------------------- 1 | // KIT 2 | @import '../assets/imports.styl' 3 | 4 | body 5 | margin-bottom 20*gs 6 | background c-bg 7 | 8 | .hash-e 9 | font-size 16px 10 | color c-grey4 11 | margin-bottom gs 12 | margin-top 2*gs 13 | max-width 100% 14 | padding gs 0 15 | textel() 16 | width 8*gs 17 | 18 | .icon-sample-kl 19 | float left 20 | margin 2*gs 21 | 22 | .main-kl 23 | max-width 100*gs 24 | margin 0 auto 25 | padding 4*gs 0 26 | 27 | .section-kl 28 | // background #ccc 29 | + .section-kl 30 | margin-top 8*gs 31 | 32 | .header-ke 33 | font-size 26px 34 | padding 2*gs 0 35 | color c-grey1 36 | 37 | .color-sample-ke 38 | rect 4*gs 39 | margin gs 40 | float left 41 | 42 | -------------------------------------------------------------------------------- /css/assets/mixins.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mojs-contrib/mojs/856bf4c4cfdcabab3a78c18dfd419d429b78817e/css/assets/mixins.css -------------------------------------------------------------------------------- /css/assets/mixins.styl: -------------------------------------------------------------------------------- 1 | gs = 8px 2 | br = 3px 3 | PX = (1/16)em 4 | 5 | retina(path, size= 100%, color= white) 6 | background color url(path+'.png') no-repeat center center 7 | background-size size 8 | @media only screen and (-webkit-min-device-pixel-ratio: 2), (min-resolution: 144dpi) 9 | background color url(path+'-@2x.png') no-repeat center center 10 | background-size size 11 | 12 | shadow() 13 | box-shadow (gs/2) (gs/2) 0 rgba(rgb(c-grey1), .25) 14 | 15 | random(a,b) 16 | return math(math(0, 'random')*(b - a + 1) + a, 'floor') 17 | 18 | norm(font,pixelSize) 19 | (1/(font/16))*pixelSize 20 | 21 | center(size) 22 | position absolute 23 | nw 50% 24 | margin-top -(size/2) 25 | margin-left -(size/2) 26 | 27 | t(position) 28 | top position 29 | 30 | b(position) 31 | bottom position 32 | 33 | l(position) 34 | left position 35 | 36 | r(position) 37 | right position 38 | 39 | nw(position=0) 40 | top position 41 | left position 42 | ne(position=0) 43 | top position 44 | right position 45 | sw(position=0) 46 | bottom position 47 | left position 48 | se(position=0) 49 | bottom position 50 | right position 51 | 52 | bbox() 53 | box-sizing border-box 54 | 55 | cbox() 56 | box-sizing content-box 57 | 58 | // rect(size) 59 | // width size 60 | // height size 61 | 62 | // min-rect(size) 63 | // min-width size 64 | // min-height size 65 | 66 | hl(color, size=1px) 67 | outline size solid color 68 | 69 | textel() 70 | white-space: nowrap 71 | -webkit-text-overflow: ellipsis 72 | -moz-text-overflow: ellipsis 73 | -o-text-overflow: ellipsis 74 | text-overflow: ellipsis 75 | overflow:hidden 76 | 77 | placeholder-mixin(args) 78 | &::-webkit-input-placeholder { /* WebKit browsers */ 79 | color: args.color 80 | } 81 | &:-moz-placeholder { /* Mozilla Firefox 4 to 18 */ 82 | color: args.color 83 | } 84 | &::-moz-placeholder { /* Mozilla Firefox 19+ */ 85 | color: args.colorg 86 | } 87 | &:-ms-input-placeholder { /* Internet Explorer 10+ */ 88 | color: args.color 89 | } 90 | 91 | II = !important 92 | 93 | -------------------------------------------------------------------------------- /css/assets/nesting-tree-traversal.js: -------------------------------------------------------------------------------- 1 | 2 | compileSelectors = function(arr, leaveHidden){ 3 | var self = this 4 | , selectors = [] 5 | , buf = [] 6 | , hiddenSelectorRegexp = /^\s*\/?\$/; 7 | 8 | function interpolateParent(selector, buf) { 9 | var str = selector.val.replace(/^\//g, '').trim(); 10 | if (buf.length) { 11 | for (var i = 0, len = buf.length; i < len; ++i) { 12 | if (~buf[i].indexOf('&') || '/' === buf[i].charAt(0)) { 13 | str = buf[i].replace(/&/g, str).replace(/^\//g, '').trim(); 14 | } else { 15 | str += ' ' + buf[i].trim(); 16 | } 17 | } 18 | } 19 | return str.trim(); 20 | } 21 | 22 | function compile(arr, i) { 23 | if (i) { 24 | arr[i].forEach(function(selector){ 25 | if (!leaveHidden && selector.val.match(hiddenSelectorRegexp)) return; 26 | if (selector.inherits) { 27 | buf.unshift(selector.val); 28 | compile(arr, i - 1); 29 | buf.shift(); 30 | } else { 31 | selectors.push(interpolateParent(selector, buf)); 32 | } 33 | }); 34 | } else { 35 | arr[0].forEach(function(selector){ 36 | if (!leaveHidden && selector.val.match(hiddenSelectorRegexp)) return; 37 | var str = interpolateParent(selector, buf); 38 | if (~str.indexOf('&')) str = str.replace(/&/g, '').trim(); 39 | if (!str.length) return; 40 | selectors.push((self.indent || '') + str.trimRight()); 41 | }); 42 | } 43 | } 44 | 45 | compile(arr, arr.length - 1); 46 | 47 | // Return the list with unique selectors only 48 | return selectors.filter(function(value, index, self){ 49 | return self.indexOf(value) === index; 50 | }); 51 | }; 52 | 53 | 54 | 55 | var plugin = function(){ 56 | return function(style){ 57 | style.define('root', function() { 58 | var stack = this.selectorStack; 59 | return stack.length ? utils.compileSelectors(stack.slice(0,1)).join(',') : '&'; 60 | }); 61 | 62 | style.define('up', function(i) { 63 | var stack = this.selectorStack, 64 | len = stack.length, i; 65 | if (!stack.length) return ''; 66 | i = parseInt(i, 10); 67 | if (!i) (i = 1); 68 | if (i < 0) (i = -i); 69 | i = (i >= len) ? len - 1 : i; 70 | stack = stack.slice(0,len-i); 71 | return utils.compileSelectors(stack).join(','); 72 | }); 73 | 74 | style.define('end', function() { 75 | var len = this.selectorStack.length, 76 | selector = null; 77 | if (!len) return ''; 78 | selector = utils.compileSelectors(this.selectorStack).join(','); 79 | selector = selector.toString().split(' '); 80 | return selector[selector.length-1]; 81 | }); 82 | }; 83 | }; 84 | module.exports = plugin; 85 | 86 | -------------------------------------------------------------------------------- /css/css-assets/blocks.styl: -------------------------------------------------------------------------------- 1 | // BLOCKS 2 | @import '../assets/imports.styl' 3 | -------------------------------------------------------------------------------- /css/css-assets/effects.styl: -------------------------------------------------------------------------------- 1 | // EFFECTS 2 | @import '../assets/imports.styl' 3 | 4 | -------------------------------------------------------------------------------- /css/css-assets/general.styl: -------------------------------------------------------------------------------- 1 | // GENERAL 2 | @import '../assets/imports.styl' 3 | 4 | * 5 | bbox() 6 | 7 | 8 | html 9 | font-size 16px 10 | 11 | body, html 12 | min-rect 100% 13 | margin 0 14 | padding 0 15 | 16 | body 17 | rect 100% 18 | overflow hidden 19 | 20 | @css{ 21 | ::selection{ 22 | background-color: #f59995; 23 | color: white; 24 | text-shadow: none; 25 | } 26 | 27 | ::-moz-selection{ 28 | background-color: #f59995; 29 | color: white; 30 | text-shadow: none; 31 | } 32 | 33 | } 34 | 35 | 36 | // FONT 37 | .bold-g, .b-g 38 | font-weight bold 39 | // FONT 40 | 41 | .op-0-g 42 | opacity 0 43 | .op-5-g 44 | opacity .5 45 | 46 | a, .link-e 47 | text-decoration none 48 | position relative 49 | color inherit 50 | 51 | 52 | .float-left-g, .f-l-g 53 | float left 54 | .float-right-g, .f-r-g 55 | float right 56 | 57 | 58 | .h-g 59 | display none 60 | .h-g-i 61 | display none II 62 | 63 | 64 | // CLEARFIX 65 | .cf:after, 66 | .cf:before { 67 | content: ''; 68 | display: table; 69 | } 70 | .cf:after { 71 | clear: both; 72 | } 73 | .cf { 74 | zoom: 1; 75 | } 76 | 77 | 78 | metrics() 79 | maxSize = 20 80 | short = w h 81 | long = width height 82 | for property, j in long 83 | for i in 1..maxSize 84 | .{property}-x{i}-gm, .{short[j]}-x{i}-gm 85 | {property} i*gs 86 | 87 | maxSize = 20 88 | short = m p 89 | long = margin padding 90 | for property, j in long 91 | for i in 1..maxSize 92 | shortSide = -l -r -t -b '' 93 | longSide = -left -right -top -bottom '' 94 | for prop, x in longSide 95 | 96 | .{property}{prop}-x{i}-gm, .{short[j]}{shortSide[x]}-x{i}-gm 97 | {property}{prop} i*gs 98 | 99 | 100 | colors() 101 | colors = c-bg c-red c-orange1 c-orange2 c-grey1 c-grey2 c-grey3 c-grey4 c-grey5 c-grey6 c-grey7 c-grey8 c-grey9 c-grey10 102 | colorsNames = 'c-bg' 'c-red' 'c-orange1' 'c-orange2' 'c-grey1' 'c-grey2' 'c-grey3' 'c-grey4' 'c-grey5' 'c-grey6' 'c-grey7' 'c-grey8' 'c-grey9' 'c-grey10' 103 | for color, i in colors 104 | .{colorsNames[i]}-bg-g 105 | background-color color 106 | 107 | metrics() 108 | colors() 109 | 110 | 111 | -------------------------------------------------------------------------------- /css/css-assets/icons.styl: -------------------------------------------------------------------------------- 1 | // ICONS 2 | @import '../assets/imports.styl' 3 | 4 | .icon 5 | rect 4*gs 6 | fill c-grey5 7 | display block 8 | position relative 9 | 10 | &:after 11 | content: '' 12 | rect 100% 13 | position absolute 14 | nw 0 15 | 16 | svg 17 | float left 18 | rect 100% 19 | 20 | &.is-hoverable 21 | &:hover 22 | fill c-grey4 23 | cursor pointer 24 | -------------------------------------------------------------------------------- /css/css-assets/layouts.styl: -------------------------------------------------------------------------------- 1 | // LAYOUTS 2 | @import '../assets/imports.styl' 3 | -------------------------------------------------------------------------------- /css/css-assets/normalize.styl: -------------------------------------------------------------------------------- 1 | /* http://meyerweb.com/eric/tools/css/reset/ 2 | v2.0 | 20110126 3 | License: none (public domain) 4 | */ 5 | 6 | html, body, div, span, applet, object, iframe, 7 | h1, h2, h3, h4, h5, h6, p, blockquote, pre, 8 | a, abbr, acronym, address, big, cite, code, 9 | del, dfn, em, img, ins, kbd, q, s, samp, 10 | small, strike, strong, sub, sup, tt, var, 11 | b, u, i, center, 12 | dl, dt, dd, ol, ul, li, 13 | fieldset, form, label, legend, 14 | table, caption, tbody, tfoot, thead, tr, th, td, 15 | article, aside, canvas, details, embed, 16 | figure, figcaption, footer, header, hgroup, 17 | menu, nav, output, ruby, section, summary, 18 | time, mark, audio, video { 19 | margin: 0; 20 | padding: 0; 21 | border-width: 0; 22 | font-size: 100%; 23 | font: inherit; 24 | vertical-align: baseline; 25 | } 26 | /* HTML5 display-role reset for older browsers */ 27 | article, aside, details, figcaption, figure, 28 | footer, header, hgroup, menu, nav, section { 29 | display: block; 30 | } 31 | body { 32 | line-height: 1; 33 | font-family:monospace,Helvetica,Arial,sans-serif; 34 | } 35 | ol, ul { 36 | list-style: none; 37 | } 38 | blockquote, q { 39 | quotes: none; 40 | } 41 | blockquote:before, blockquote:after, 42 | q:before, q:after { 43 | content: ''; 44 | content: none; 45 | } 46 | table { 47 | border-collapse: collapse; 48 | border-spacing: 0; 49 | } -------------------------------------------------------------------------------- /css/icons.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /css/kit.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Social stats UI kit 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 |
16 |
Colors
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
Icons
38 |
39 |
40 |
7a570084fcf17dfd19f27128300c8648
41 |
42 |
43 |
44 |
45 |
Elements
46 |
b69a1e4a33a526ed9af2af493cfb4cb1
47 |
48 |
49 | 50 | -------------------------------------------------------------------------------- /css/kit.jade: -------------------------------------------------------------------------------- 1 | !!!5 2 | html 3 | head 4 | meta(charset='UTF-8') 5 | title Social stats UI kit 6 | link(rel="stylesheet", type="text/css", href="main.css") 7 | link(rel="stylesheet", type="text/css", href="assets/kit.css") 8 | link(rel="stylesheet", href="css-assets/font/stylesheet.css", type="text/css", charset="utf-8") 9 | body 10 | include icons.svg 11 | .main-kl 12 | .section-kl 13 | .header-ke Colors 14 | .cf 15 | .color-sample-ke.c-grey1-bg-g 16 | .color-sample-ke.c-grey2-bg-g 17 | .color-sample-ke.c-grey3-bg-g 18 | .color-sample-ke.c-grey4-bg-g 19 | .color-sample-ke.c-grey5-bg-g 20 | .color-sample-ke.c-grey6-bg-g 21 | .color-sample-ke.c-grey7-bg-g 22 | .color-sample-ke.c-grey8-bg-g 23 | .color-sample-ke.c-grey9-bg-g 24 | .color-sample-ke.c-grey10-bg-g 25 | 26 | .cf 27 | .color-sample-ke.c-red-bg-g 28 | .color-sample-ke.c-orange1-bg-g 29 | .color-sample-ke.c-orange2-bg-g 30 | .color-sample-ke.c-bg-bg-g 31 | 32 | - var data = {} 33 | 34 | .section-kl 35 | .header-ke Icons 36 | .cf 37 | .icon-sample-kl 38 | .hash-e 7a570084fcf17dfd19f27128300c8648 39 | //- - var data = { path: 'old-pen-icon' } 40 | //- include partials/icon 41 | 42 | .section-kl 43 | .header-ke Elements 44 | .hash-e b69a1e4a33a526ed9af2af493cfb4cb1 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /css/main.css: -------------------------------------------------------------------------------- 1 | .el { 2 | width: 1.25em; 3 | height: 7.5em; 4 | background: #aaa; 5 | position: absolute; 6 | left: 0; 7 | } 8 | html, 9 | body { 10 | height: 1000px; 11 | } 12 | .sprite { 13 | position: absolute; 14 | left: 100px; 15 | top: 50px; 16 | } 17 | .sprite__frame { 18 | position: absolute; 19 | top: 0; 20 | left: 0; 21 | -webkit-transform: translateZ(0); 22 | -ms-transform: translateZ(0); 23 | transform: translateZ(0); 24 | display: block; 25 | width: 100px; 26 | height: 100px; 27 | } 28 | .sprite__ellipse { 29 | stroke: #ff1493; 30 | fill: none; 31 | } 32 | .svg { 33 | position: absolute; 34 | top: 400px; 35 | left: 0; 36 | } 37 | .svg-path { 38 | position: absolute; 39 | top: 160px; 40 | } 41 | html, 42 | body { 43 | padding: 0; 44 | margin: 0; 45 | background: #333; 46 | } 47 | .page { 48 | background: #ff1493; 49 | width: 100%; 50 | height: 1888px; 51 | position: absolute; 52 | overflow: hidden; 53 | } 54 | .page__inner { 55 | overflow: hidden; 56 | position: absolute; 57 | width: 4px; 58 | height: 4px; 59 | left: 1000px; 60 | top: 40px; 61 | background: #ffa500; 62 | -webkit-backface-visibility: hidden; 63 | -ms-backface-visibility: hidden; 64 | backface-visibility: hidden; 65 | -webkit-perspective: 1280px; 66 | -ms-perspective: 1280px; 67 | perspective: 1280px; 68 | } 69 | .page__inner.is-normal { 70 | width: 100%; 71 | height: auto; 72 | -webkit-transition: none; 73 | transition: none; 74 | overflow: visible; 75 | position: static; 76 | } 77 | .page__inner.is-normal .page__inner__inner { 78 | width: 100%; 79 | -webkit-transition: none; 80 | transition: none; 81 | -webkit-transform: none; 82 | -ms-transform: none; 83 | transform: none; 84 | position: static; 85 | } 86 | .page__inner.is-scaled { 87 | -webkit-transform: scale(1280); 88 | -ms-transform: scale(1280); 89 | transform: scale(1280); 90 | -webkit-transition: -webkit-transform 7s cubic-bezier(0.19, 1, 0.22, 1); 91 | transition: transform 7s cubic-bezier(0.19, 1, 0.22, 1); 92 | } 93 | .page__inner.is-scaled .page__inner__inner { 94 | -webkit-transform: translateZ(-1638400px); 95 | -ms-transform: translateZ(-1638400px); 96 | transform: translateZ(-1638400px); 97 | -webkit-transition: -webkit-transform 7s cubic-bezier(0.19, 1, 0.22, 1); 98 | transition: transform 7s cubic-bezier(0.19, 1, 0.22, 1); 99 | } 100 | .page__inner__inner { 101 | width: 1280px; 102 | left: -1000px; 103 | top: -40px; 104 | position: absolute; 105 | -webkit-backface-visibility: hidden; 106 | -ms-backface-visibility: hidden; 107 | backface-visibility: hidden; 108 | background: #ffa500; 109 | } 110 | .elem { 111 | width: 50px; 112 | height: 50px; 113 | margin-left: 200px; 114 | background: #ff1493; 115 | border-radius: 50%; 116 | } 117 | -------------------------------------------------------------------------------- /css/main.styl: -------------------------------------------------------------------------------- 1 | @import 'assets/imports.styl' 2 | // @import 'css-assets/normalize.styl' 3 | 4 | // @import 'css-assets/general' 5 | // @import 'css-assets/blocks' 6 | // @import 'css-assets/layouts' 7 | // @import 'css-assets/effects' 8 | // @import 'css-assets/icons' 9 | 10 | // use('assets/nesting-tree-traversal.js') 11 | 12 | .el 13 | width 20*PX 14 | height 120*PX 15 | background #aaa 16 | position absolute 17 | left 0 18 | // transform-origin center bottom 19 | 20 | 21 | html, body 22 | height 1000px 23 | 24 | // .el 25 | // width 800px 26 | // height 800px 27 | // // background #111 28 | // background magenta 29 | // transform translateZ(0) 30 | 31 | .sprite 32 | position absolute 33 | left 100px 34 | top 50px 35 | &__frame 36 | position absolute 37 | top 0 38 | left 0 39 | transform translateZ(0) 40 | display block 41 | width 100px 42 | height 100px 43 | &__ellipse 44 | stroke deeppink 45 | fill none 46 | 47 | // .el 48 | // width 50px 49 | // height 50px 50 | // border-radius 50% 51 | // background deeppink 52 | // position absolute 53 | // top 20% 54 | // left 10% 55 | // margin-left 500px 56 | // nw 50% 57 | 58 | // svg 59 | // hl deeppink 60 | 61 | .svg 62 | position absolute 63 | top 50*gs 64 | left 0 65 | 66 | .svg-path 67 | position absolute 68 | top 20*gs 69 | 70 | html, body 71 | padding 0 72 | margin 0 73 | background #333 74 | gap = 100px 75 | originX = 1000px 76 | originY = 40px 77 | windowSize = 1280 78 | .page 79 | background deeppink 80 | width 100% 81 | height 1888px 82 | position absolute 83 | overflow hidden 84 | startSize = 4px 85 | // z-index 0 86 | &__inner 87 | overflow hidden 88 | position absolute 89 | width startSize 90 | height startSize 91 | left originX 92 | top originY 93 | background orange 94 | backface-visibility hidden 95 | perspective (windowSize)px 96 | &.is-normal 97 | width 100% 98 | height auto 99 | transition none 100 | overflow visible 101 | position static 102 | .page__inner__inner 103 | width 100% 104 | transition none 105 | transform none 106 | position static 107 | &.is-scaled 108 | transform scale(1*windowSize) 109 | transition transform 7s cubic-bezier(0.19, 1, 0.22, 1) 110 | .page__inner__inner 111 | transform translateZ(-(windowSize*windowSize)px) 112 | transition transform 7s cubic-bezier(0.19, 1, 0.22, 1) 113 | &__inner 114 | width (windowSize)px 115 | left - originX 116 | top - originY 117 | position absolute 118 | backface-visibility hidden 119 | background orange 120 | // .content 121 | // position absolute 122 | // nw 0 123 | // se 0 124 | // padding-left 4*gs 125 | // // min-width 1280px 126 | // // min-height 900px 127 | // background purple 128 | // // margin-left -640px 129 | // // margin-top -450px 130 | // position absolute 131 | 132 | // // chrome: 133 | // position absolute 134 | // overflow hidden 135 | // rect 140px 136 | 137 | // // safari: 138 | // overflow hidden 139 | // rect 140px 140 | 141 | // .div-wrapper 142 | // position absolute 143 | // overflow hidden 144 | // rect 140px 145 | // .div 146 | // position absolute 147 | // svg 148 | // hl hotpink 149 | // body 150 | // height 600px 151 | 152 | // iframe 153 | // display none !important 154 | // canvas 155 | // hl orange 156 | 157 | // .container 158 | // width 800px 159 | // height 800px 160 | // border 1px solid hotpink 161 | // position relative 162 | 163 | // .el 164 | // // border 1px solid deeppink 165 | // background hotpink 166 | // border-radius 50% 167 | // size = 5*gs 168 | // rect size 169 | // margin-left -(size/2) 170 | // position absolute 171 | // nw 0 172 | // // backface-visibility hidden 173 | // // transform translateZ(0) 174 | 175 | 176 | .elem { 177 | width: 50px; 178 | height: 50px; 179 | margin-left: 200px; 180 | background: deeppink; 181 | border-radius: 50%; 182 | // -webkit-mask-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAA5JREFUeNpiYGBgAAgwAAAEAAGbA+oJAAAAAElFTkSuQmCC'); 183 | // -webkit-mask-image: -webkit-radial-gradient(circle, white 100%, black 100%); 184 | // -webkit-filter: url(#filter-1); 185 | // -moz-filter: url(#filter-1); 186 | // filter: url(#filter-1); 187 | } -------------------------------------------------------------------------------- /css/partials/icon.jade: -------------------------------------------------------------------------------- 1 | div(class="icon #{ data.className || '' }", id="#{ data.idAttribute || '' }", title="#{ data.title || '' }") 2 | svg(viewBox="#{data.viewBox || '0 0 32 32'}") 3 | use(xlink:href="##{data.path || '' }") -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | var gulp = require('gulp'); 2 | var fs = require('fs'); 3 | var stylus = require('gulp-stylus'); 4 | var autoprefixer = require('gulp-autoprefixer'); 5 | var notify = require('gulp-notify'); 6 | var livereload = require('gulp-livereload'); 7 | var coffee = require('gulp-coffee'); 8 | var changed = require('gulp-changed'); 9 | // var jade = require('gulp-jade'); 10 | var watch = require('gulp-jade'); 11 | var coffeelint = require('gulp-coffeelint'); 12 | var plumber = require('gulp-plumber'); 13 | var concat = require('gulp-concat'); 14 | var csslint = require('gulp-csslint'); 15 | var browserify = require('gulp-browserify'); 16 | var rename = require('gulp-rename'); 17 | var uglify = require('gulp-uglify'); 18 | var sequence = require('run-sequence'); 19 | // var coffeeify = require('gulp-coffeeify'); 20 | var insert = require('gulp-insert'); 21 | var jeditor = require("gulp-json-editor"); 22 | var shell = require("gulp-shell"); 23 | var grock = require("grock"); 24 | // var babel = require("gulp-babel"); 25 | 26 | var devFolder = '', distFolder = '', currentVersion = 0; 27 | var distMoFile = devFolder + 'build/mo.js'; 28 | 29 | var paths = { 30 | src: { 31 | js: devFolder + 'js/**/*.coffee', 32 | babel: devFolder + 'js/**/*.babel.js', 33 | index: devFolder + 'index.jade', 34 | css: devFolder + 'css/**/*.styl', 35 | tests: distFolder + 'spec/**/*.coffee' 36 | }, 37 | dist:{ 38 | js: distFolder + 'js/', 39 | index: distFolder, 40 | css: distFolder + 'css/', 41 | tests: distFolder + 'spec/' 42 | } 43 | } 44 | 45 | gulp.task('coffee:tests', function(e){ 46 | return gulp.src(paths.src.tests) 47 | .pipe(plumber()) 48 | .pipe(changed(paths.dist.tests, { extension: '.js'})) 49 | .pipe(coffeelint()) 50 | .pipe(coffeelint.reporter()) 51 | .pipe(coffee()) 52 | .pipe(gulp.dest(paths.dist.tests)) 53 | }); 54 | 55 | gulp.task('stylus', function(){ 56 | return gulp.src(devFolder + 'css/main.styl') 57 | .pipe(plumber()) 58 | .pipe(stylus()) 59 | .pipe(autoprefixer('last 4 version')) 60 | .pipe(gulp.dest(paths.dist.css)) 61 | .pipe(livereload()) 62 | }); 63 | 64 | var credits = '' 65 | 66 | gulp.task('lib', function(e){ 67 | return gulp.src(paths.src.js) 68 | .pipe(plumber()) 69 | .pipe(coffee()) 70 | .pipe(gulp.dest('lib/')) 71 | }); 72 | 73 | gulp.task('babel-lib', function(e){ 74 | return gulp.src(paths.src.babel) 75 | .pipe(plumber()) 76 | .pipe(babel()) 77 | .pipe(rename(function (path) { 78 | return path.basename = path.basename.replace('.babel', ''); 79 | }) 80 | ).pipe(gulp.dest('lib/')) 81 | }); 82 | 83 | gulp.task('minify-mo', function() { 84 | return gulp.src(distMoFile) 85 | .pipe(plumber()) 86 | .pipe(uglify()) 87 | .pipe(insert.transform(function(contents) { 88 | return credits + contents; 89 | })) 90 | .pipe(rename('mo.min.js')) 91 | .pipe(gulp.dest('./build')) 92 | }); 93 | 94 | gulp.task('update-version', function() { 95 | sequence('get-current-version', 'update-bower-version', 'update-main-file-version'); 96 | }); 97 | 98 | gulp.task('get-current-version', function(e){ 99 | return gulp.src('package.json') 100 | .pipe(plumber()) 101 | .pipe(jeditor(function (json) { 102 | currentVersion = json.version; 103 | credits = '/*! \n\t:: mo · js :: motion graphics toolbelt for the web\n\tOleg Solomka @LegoMushroom 2015 MIT\n\t' + currentVersion + ' \n*/\n\n' 104 | return json; 105 | })) 106 | }); 107 | 108 | gulp.task('update-bower-version', function(e){ 109 | return gulp.src('bower.json') 110 | .pipe(plumber()) 111 | .pipe(jeditor(function (json) { 112 | json.version = currentVersion; 113 | return json; 114 | })) 115 | .pipe(gulp.dest('')) 116 | }); 117 | 118 | gulp.task('update-main-file-version', function(e){ 119 | return gulp.src('js/mojs.babel.js') 120 | .pipe(plumber()) 121 | .pipe(insert.transform(function(contents) { 122 | var newString = 'revision: \''+currentVersion+'\''; 123 | return contents 124 | .replace(/revision\:\s+?(\'|\")\d+\.\d+\.+\d+(\'|\")/i, newString); 125 | })) 126 | .pipe(gulp.dest('js/')) 127 | }); 128 | 129 | gulp.task('default', function(){ 130 | var server = livereload(); 131 | gulp.run('get-current-version'); 132 | gulp.watch(paths.src.tests,['coffee:tests']); 133 | gulp.watch(paths.src.css, ['stylus']); 134 | // gulp.watch(paths.src.js, ['coffeeify', 'coffee-lint', 'docs', 'lib']); 135 | // gulp.watch(paths.src.js, [ 'lib', 'babel-lib' ]); 136 | // gulp.watch(paths.src.babel, [ 'lib', 'babel-lib' ]); 137 | gulp.watch(distMoFile, [ 'minify-mo' ]); 138 | // gulp.watch(paths.src.index,['index:jade']); 139 | gulp.watch('package.json', ['update-version']); 140 | }); 141 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | mo · js 6 | 7 | 8 | 9 | 10 | 23 | 24 |
25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /js/delta/delta.babel.js: -------------------------------------------------------------------------------- 1 | const h = require('../h'); 2 | import Tween from '../tween/tween'; 3 | 4 | class Delta { 5 | 6 | constructor ( o = {} ) { 7 | this._o = o; 8 | this._createTween( o.tweenOptions ); 9 | // initial properties render 10 | !this._o.isChained && this.refresh( true ); 11 | } 12 | /* 13 | Method to call `_refresh` method on `tween`. 14 | Use switch between `0` and `1` progress for delta value. 15 | @public 16 | @param {Boolean} If refresh before start time or after. 17 | @returns this. 18 | */ 19 | refresh (isBefore) { 20 | this._previousValues = []; 21 | 22 | var deltas = this._o.deltas; 23 | for (var i = 0; i < deltas.length; i++) { 24 | var name = deltas[i].name; 25 | this._previousValues.push({ 26 | name, value: this._o.props[name] 27 | }) 28 | } 29 | 30 | this.tween._refresh( isBefore ); 31 | return this; 32 | } 33 | /* 34 | Method to restore all saved properties from `_previousValues` array. 35 | @public 36 | @returns this. 37 | */ 38 | restore () { 39 | var prev = this._previousValues; 40 | for (var i = 0; i < prev.length; i++) { 41 | const record = prev[i]; 42 | this._o.props[record.name] = record.value; 43 | } 44 | return this; 45 | } 46 | /* 47 | Method to create tween of the delta. 48 | @private 49 | @param {Object} Options object. 50 | */ 51 | _createTween ( o = {} ) { 52 | var it = this; 53 | o.callbackOverrides = { 54 | onUpdate (ep, p) { it._calcCurrentProps( ep, p ); }, 55 | } 56 | 57 | // if not chained - add the onRefresh callback 58 | // to refresh the tween when needed 59 | if ( !this._o.isChained ) { 60 | o.callbackOverrides.onRefresh = function (isBefore, ep, p) { 61 | it._calcCurrentProps( ep, p ); 62 | } 63 | } 64 | 65 | o.callbacksContext = this._o.callbacksContext; 66 | this.tween = new Tween( o ); 67 | } 68 | /* 69 | Method to calculate current progress of the deltas. 70 | @private 71 | @param {Number} Eased progress to calculate - [0..1]. 72 | @param {Number} Progress to calculate - [0..1]. 73 | */ 74 | _calcCurrentProps ( easedProgress, p ) { 75 | var deltas = this._o.deltas; 76 | for (var i = 0; i < deltas.length; i++) { 77 | var type = deltas[i].type; 78 | this[`_calcCurrent_${type}`]( deltas[i], easedProgress, p ); 79 | } 80 | } 81 | /* 82 | Method to calc the current color delta value. 83 | @param {Object} Delta 84 | @param {Number} Eased progress [0..1]. 85 | @param {Number} Plain progress [0..1]. 86 | */ 87 | _calcCurrent_color (delta, ep, p) { 88 | var r, g, b, a, 89 | start = delta.start, 90 | d = delta.delta; 91 | if ( !delta.curve ) { 92 | r = parseInt(start.r + ep * d.r, 10); 93 | g = parseInt(start.g + ep * d.g, 10); 94 | b = parseInt(start.b + ep * d.b, 10); 95 | a = parseFloat(start.a + ep * d.a); 96 | } else { 97 | var cp = delta.curve(p); 98 | r = parseInt(cp * (start.r + p*d.r), 10); 99 | g = parseInt(cp * (start.g + p*d.g), 10); 100 | b = parseInt(cp * (start.b + p*d.b), 10); 101 | a = parseFloat(cp * (start.a + p*d.a)); 102 | } 103 | this._o.props[delta.name] = `rgba(${r},${g},${b},${a})`; 104 | } 105 | /* 106 | Method to calc the current number delta value. 107 | @param {Object} Delta 108 | @param {Number} Eased progress [0..1]. 109 | @param {Number} Plain progress [0..1]. 110 | */ 111 | _calcCurrent_number (delta, ep, p) { 112 | this._o.props[delta.name] = (!delta.curve) 113 | ? delta.start + ep * delta.delta 114 | : delta.curve(p) * ( delta.start + p * delta.delta ); 115 | } 116 | /* 117 | Method to calc the current number with units delta value. 118 | @param {Object} Delta 119 | @param {Number} Eased progress [0..1]. 120 | @param {Number} Plain progress [0..1]. 121 | */ 122 | _calcCurrent_unit (delta, ep, p) { 123 | var currentValue = (!delta.curve) 124 | ? delta.start.value + ep*delta.delta 125 | : delta.curve(p) * ( delta.start.value + p * delta.delta ); 126 | 127 | this._o.props[delta.name] = `${currentValue}${delta.end.unit}`; 128 | } 129 | /* 130 | Method to calc the current array delta value. 131 | @param {Object} Delta 132 | @param {Number} Eased progress [0..1]. 133 | @param {Number} Plain progress [0..1]. 134 | */ 135 | _calcCurrent_array (delta, ep, p) { 136 | // var arr, 137 | var name = delta.name, 138 | props = this._o.props, 139 | string = ''; 140 | 141 | // to prevent GC bothering with arrays garbage 142 | // if ( h.isArray( props[name] ) ) { 143 | // arr = props[name]; 144 | // arr.length = 0; 145 | // } else { arr = []; } 146 | 147 | // just optimization to prevent curve 148 | // calculations on every array item 149 | var proc = (delta.curve) ? delta.curve(p) : null; 150 | 151 | for ( var i = 0; i < delta.delta.length; i++ ) { 152 | var item = delta.delta[i], 153 | dash = (!delta.curve) 154 | ? delta.start[i].value + ep * item.value 155 | : proc * (delta.start[i].value + p * item.value); 156 | 157 | string += `${dash}${item.unit} `; 158 | // arr.push({ 159 | // string: `${dash}${item.unit}`, 160 | // value: dash, 161 | // unit: item.unit, 162 | // }); 163 | } 164 | props[name] = string; 165 | } 166 | } 167 | 168 | export default Delta; -------------------------------------------------------------------------------- /js/easing/approximate-map.babel.js: -------------------------------------------------------------------------------- 1 | import h from '../h'; 2 | 3 | /* 4 | Method to bootstrap approximation function. 5 | @private 6 | @param {Object} Samples Object. 7 | @returns {Function} Approximate function. 8 | */ 9 | const _proximate = (samples) => { 10 | var n = samples.base, 11 | samplesAmount = Math.pow( 10, n ), 12 | samplesStep = 1/samplesAmount; 13 | 14 | function RoundNumber (input, numberDecimals) 15 | { 16 | numberDecimals = +numberDecimals || 0; // +var magic! 17 | 18 | var multiplyer = Math.pow(10.0, numberDecimals); 19 | 20 | return Math.round(input * multiplyer) / multiplyer; 21 | } 22 | 23 | var cached = function cached (p) { 24 | var newKey = RoundNumber(p, n), 25 | sample = samples.get(newKey); 26 | 27 | if ( Math.abs(p - newKey) < samplesStep ) { return sample; } 28 | 29 | if ( p > newKey ) { 30 | var nextIndex = newKey + samplesStep; 31 | var nextValue = samples.get(nextIndex); 32 | } else { 33 | var nextIndex = newKey - samplesStep; 34 | var nextValue = samples.get(nextIndex); 35 | } 36 | 37 | var dLength = nextIndex - newKey; 38 | var dValue = nextValue - sample; 39 | if ( dValue < samplesStep ) { 40 | return sample; 41 | } 42 | 43 | var progressScale = (p - newKey)/dLength; 44 | var coef = ( nextValue > sample ) ? -1 : 1; 45 | var scaledDifference = coef*progressScale*dValue; 46 | 47 | return sample + scaledDifference; 48 | } 49 | 50 | cached.getSamples = () => { return samples; } 51 | 52 | return cached; 53 | } 54 | /* 55 | Method to take samples of the function and call the _proximate 56 | method with the dunction and samples. Or if samples passed - pipe 57 | them to the _proximate method without sampling. 58 | @private 59 | @param {Function} Function to sample. 60 | @param {Number, Object, String} Precision or precomputed samples. 61 | */ 62 | const _sample = (fn, n = 4) => { 63 | 64 | const nType = typeof n; 65 | 66 | var samples = new Map; 67 | if (nType === 'number') { 68 | var p = 0, 69 | samplesCount = Math.pow( 10, n ), 70 | step = 1/samplesCount; 71 | 72 | samples.set(0, fn(0)); 73 | for (var i = 0; i < samplesCount-1; i++) { 74 | p += step; 75 | 76 | var index = parseFloat(p.toFixed(n)); 77 | samples.set(index, p); 78 | } 79 | samples.set(1, fn(1)); 80 | 81 | samples.base = n; 82 | } 83 | else if (nType === 'object') { samples = new Map(n.entries()); } 84 | else if (nType === 'string' ) { samples = new Map((JSON.parse(n)).entries()); } 85 | 86 | return Approximate._sample._proximate( samples ); 87 | } 88 | 89 | const Approximate = { _sample, _proximate }; 90 | Approximate._sample._proximate = Approximate._proximate; 91 | 92 | export default Approximate._sample; 93 | -------------------------------------------------------------------------------- /js/easing/approximate.babel.js: -------------------------------------------------------------------------------- 1 | import h from '../h'; 2 | 3 | /* 4 | Method to bootstrap approximation function. 5 | @private 6 | @param {Object} Samples Object. 7 | @returns {Function} Approximate function. 8 | */ 9 | const _proximate = (samples) => { 10 | var n = samples.base, 11 | samplesAmount = Math.pow( 10, n ), 12 | samplesStep = 1/samplesAmount; 13 | 14 | function RoundNumber (input, numberDecimals) 15 | { 16 | numberDecimals = +numberDecimals || 0; // +var magic! 17 | 18 | var multiplyer = Math.pow(10.0, numberDecimals); 19 | 20 | return Math.round(input * multiplyer) / multiplyer; 21 | } 22 | 23 | var cached = function cached (p) { 24 | var newKey = RoundNumber(p, n), 25 | sample = samples[ newKey.toString() ]; 26 | 27 | if ( Math.abs(p - newKey) < samplesStep ) { return sample; } 28 | 29 | if ( p > newKey ) { 30 | var nextIndex = newKey + samplesStep; 31 | var nextValue = samples[ nextIndex ]; 32 | } else { 33 | var nextIndex = newKey - samplesStep; 34 | var nextValue = samples[ nextIndex ]; 35 | } 36 | 37 | var dLength = nextIndex - newKey; 38 | var dValue = nextValue - sample; 39 | if ( dValue < samplesStep ) { 40 | return sample; 41 | } 42 | 43 | var progressScale = (p - newKey)/dLength; 44 | var coef = ( nextValue > sample ) ? -1 : 1; 45 | var scaledDifference = coef*progressScale*dValue; 46 | 47 | return sample + scaledDifference; 48 | } 49 | 50 | cached.getSamples = () => { return samples; } 51 | 52 | return cached; 53 | } 54 | /* 55 | Method to take samples of the function and call the _proximate 56 | method with the dunction and samples. Or if samples passed - pipe 57 | them to the _proximate method without sampling. 58 | @private 59 | @param {Function} Function to sample. 60 | @param {Number, Object, String} Precision or precomputed samples. 61 | */ 62 | const _sample = (fn, n = 4) => { 63 | 64 | const nType = typeof n; 65 | 66 | var samples = {}; 67 | if (nType === 'number') { 68 | var p = 0, 69 | samplesCount = Math.pow( 10, n ), 70 | step = 1/samplesCount; 71 | 72 | samples[ 0 ] = fn(0); 73 | for (var i = 0; i < samplesCount-1; i++) { 74 | p += step; 75 | 76 | var index = parseFloat(p.toFixed(n)); 77 | samples[ index ] = fn( p ); 78 | } 79 | samples[ 1 ] = fn(1); 80 | 81 | samples.base = n; 82 | } 83 | else if (nType === 'object') { samples = n; } 84 | else if (nType === 'string' ) { samples = JSON.parse(n); } 85 | 86 | return Approximate._sample._proximate( samples ); 87 | } 88 | 89 | const Approximate = { _sample, _proximate }; 90 | Approximate._sample._proximate = Approximate._proximate; 91 | 92 | export default Approximate._sample; -------------------------------------------------------------------------------- /js/easing/bezier-easing.coffee: -------------------------------------------------------------------------------- 1 | h = require '../h' 2 | 3 | ###* 4 | * Copyright (c) 2014 Gaëtan Renaudeau http://goo.gl/El3k7u 5 | * Adopted from https://github.com/gre/bezier-easing 6 | ### 7 | # TODO: remove 3 ### istanbul ignore next ### statements 8 | # and cover the gaps 9 | 10 | class BezierEasing 11 | constructor:(o)-> @vars(); return @generate 12 | vars:-> @generate = h.bind @generate, @ 13 | generate:(mX1, mY1, mX2, mY2)-> 14 | # params parsing 15 | if arguments.length < 4 16 | return @error 'Bezier function expects 4 arguments' 17 | for i in [0...4] 18 | arg = arguments[i] 19 | if (typeof arg isnt "number" or isNaN(arg) or !isFinite(arg)) 20 | return @error 'Bezier function expects 4 arguments' 21 | if (mX1 < 0 or mX1 > 1 or mX2 < 0 or mX2 > 1) 22 | return @error 'Bezier x values should be > 0 and < 1' 23 | # These values are established by empiricism with 24 | # tests (tradeoff: performance VS precision) 25 | NEWTON_ITERATIONS = 4 26 | NEWTON_MIN_SLOPE = 0.001 27 | SUBDIVISION_PRECISION = 0.0000001 28 | SUBDIVISION_MAX_ITERATIONS = 10 29 | kSplineTableSize = 11 30 | kSampleStepSize = 1.0 / (kSplineTableSize - 1.0) 31 | float32ArraySupported = 'Float32Array' in global 32 | 33 | A = (aA1, aA2) -> 1.0 - 3.0 * aA2 + 3.0 * aA1 34 | B = (aA1, aA2) -> 3.0 * aA2 - 6.0 * aA1 35 | C = (aA1) -> 3.0 * aA1 36 | 37 | # Returns x(t) given t, x1, and x2, or y(t) given t, y1, and y2. 38 | calcBezier = (aT, aA1, aA2) -> 39 | ((A(aA1, aA2) * aT + B(aA1, aA2)) * aT + C(aA1)) * aT 40 | 41 | # Returns dx/dt given t, x1, and x2, or dy/dt given t, y1, and y2. 42 | getSlope = (aT, aA1, aA2) -> 43 | 3.0 * A(aA1, aA2) * aT * aT + 2.0 * B(aA1, aA2) * aT + C(aA1) 44 | 45 | newtonRaphsonIterate = (aX, aGuessT) -> 46 | i = 0 47 | while i < NEWTON_ITERATIONS 48 | currentSlope = getSlope(aGuessT, mX1, mX2) 49 | ### istanbul ignore if ### 50 | return aGuessT if currentSlope is 0.0 51 | currentX = calcBezier(aGuessT, mX1, mX2) - aX 52 | aGuessT -= currentX / currentSlope 53 | ++i 54 | aGuessT 55 | 56 | calcSampleValues = -> 57 | i = 0 58 | while i < kSplineTableSize 59 | mSampleValues[i] = calcBezier(i * kSampleStepSize, mX1, mX2) 60 | ++i 61 | return 62 | ### istanbul ignore next ### 63 | binarySubdivide = (aX, aA, aB) -> 64 | currentX = undefined; currentT = undefined; i = 0 65 | loop 66 | currentT = aA + (aB - aA) / 2.0 67 | currentX = calcBezier(currentT, mX1, mX2) - aX 68 | if currentX > 0.0 then aB = currentT 69 | else aA = currentT 70 | isBig = Math.abs(currentX) > SUBDIVISION_PRECISION 71 | unless isBig and ++i < SUBDIVISION_MAX_ITERATIONS 72 | break 73 | currentT 74 | 75 | getTForX = (aX) -> 76 | intervalStart = 0.0 77 | currentSample = 1 78 | lastSample = kSplineTableSize - 1 79 | while currentSample != lastSample and mSampleValues[currentSample] <= aX 80 | intervalStart += kSampleStepSize 81 | ++currentSample 82 | --currentSample 83 | # Interpolate to provide an initial guess for t 84 | delta = (mSampleValues[currentSample + 1] - mSampleValues[currentSample]) 85 | dist = (aX - mSampleValues[currentSample]) / delta 86 | guessForT = intervalStart + dist * kSampleStepSize 87 | initialSlope = getSlope(guessForT, mX1, mX2) 88 | 89 | if initialSlope >= NEWTON_MIN_SLOPE 90 | newtonRaphsonIterate aX, guessForT 91 | else 92 | ### istanbul ignore next ### 93 | if initialSlope == 0.0 then guessForT 94 | else binarySubdivide aX, intervalStart, intervalStart + kSampleStepSize 95 | 96 | precompute = -> 97 | _precomputed = true 98 | calcSampleValues() if mX1 != mY1 or mX2 != mY2 99 | 100 | mSampleValues = if !float32ArraySupported then new Array(kSplineTableSize) 101 | else new Float32Array(kSplineTableSize) 102 | _precomputed = false 103 | 104 | f = (aX) -> 105 | if !_precomputed then precompute() 106 | if mX1 == mY1 and mX2 == mY2 then return aX 107 | # linear 108 | # Because JavaScript number are imprecise, 109 | # we should guarantee the extremes are right. 110 | return 0 if aX == 0 111 | return 1 if aX == 1 112 | calcBezier getTForX(aX), mY1, mY2 113 | 114 | str = "bezier(" + [mX1, mY1, mX2, mY2] + ")" 115 | f.toStr = -> str 116 | f 117 | 118 | error:(msg)-> h.error msg 119 | 120 | bezierEasing = new BezierEasing 121 | 122 | module.exports = bezierEasing 123 | 124 | 125 | 126 | 127 | -------------------------------------------------------------------------------- /js/easing/mix.coffee: -------------------------------------------------------------------------------- 1 | easing = null 2 | 3 | # Method to check if need to parse easing expression. 4 | # 5 | # @param {Object} Mix array item 6 | # @return {Function, Number} Parsed easing or static easing number. 7 | parseIfEasing = (item)-> 8 | if typeof item.value is 'number' then item.value 9 | else easing.parseEasing item.value 10 | # --- 11 | 12 | # Method to sort an array form smallest to largest. 13 | # 14 | # @param {Any} Array item. 15 | # @param {Any} Array item. 16 | # @return {Number} Comparation indicator. 17 | # @side-effect Check if value on **array item** should be 18 | # parsed, and parses it if so. 19 | sort = (a, b)-> 20 | a.value = parseIfEasing(a); b.value = parseIfEasing(b) 21 | 22 | returnValue = 0 23 | a.to < b.to and (returnValue = -1) 24 | a.to > b.to and (returnValue = 1) 25 | returnValue 26 | 27 | # --- 28 | 29 | # Method to get the nearest to item to the progress. 30 | # 31 | # @param {Array} Array to search in. 32 | # @param {Number} Progress to search for. 33 | # @return {Number} Nearest item index. 34 | getNearest = (array, progress)-> 35 | index = 0 36 | (index = i; break if value.to > progress) for value, i in array 37 | index 38 | # --- 39 | 40 | # Method to get the nearest to item to the progress. 41 | # 42 | # @param {Array} Array to search in. 43 | # @param {Number} Progress to search for. 44 | # @return {Number} Nearest item index. 45 | mix = (args...)-> 46 | # if there are more than 1 mix values - sort the array 47 | if args.length > 1 then args = args.sort(sort) 48 | # if there is just one value - parse it's easing expression 49 | else args[0].value = parseIfEasing args[0] 50 | 51 | (progress)-> 52 | index = getNearest(args, progress) 53 | if index isnt -1 54 | value = args[index].value 55 | # return 1 if not defined 56 | return 1 if index is args.length-1 and progress > args[index].to 57 | # evaluate the function if it was passed or return the value itself 58 | return if typeof value is 'function' then value(progress) else value 59 | # --- 60 | 61 | # Method initialize the mix function. 62 | # It was made since requiring "easing" module cuases 63 | # cycle dependensies issue but we need the module. 64 | # So we pass it to the create method and it assigns it to 65 | # already declared easing variable. 66 | # 67 | # @param {Object} Easing module. 68 | # @return {Function} Mix function. 69 | create = (e)-> easing = e; mix 70 | 71 | module.exports = create -------------------------------------------------------------------------------- /js/mojs.babel.js: -------------------------------------------------------------------------------- 1 | import h from './h'; 2 | import shapesMap from './shapes/shapesMap'; 3 | import Shape from './shape'; 4 | import ShapeSwirl from './shape-swirl'; 5 | import Burst from './burst'; 6 | import Html from './html'; 7 | import stagger from './stagger'; 8 | import Spriter from './spriter'; 9 | import MotionPath from './motion-path'; 10 | import Tween from './tween/tween'; 11 | import Timeline from './tween/timeline'; 12 | import Tweener from './tween/tweener'; 13 | import Tweenable from './tween/tweenable'; 14 | import Thenable from './thenable'; 15 | import Tunable from './tunable'; 16 | import Delta from './delta/delta'; 17 | import Deltas from './delta/deltas'; 18 | import Module from './module'; 19 | import tweener from './tween/tweener'; 20 | import easing from './easing/easing'; 21 | 22 | var mojs = { 23 | revision: '0.288.2', isDebug: true, helpers: h, 24 | Shape, ShapeSwirl, Burst, Html, stagger, Spriter, MotionPath, 25 | Tween, Timeline, Tweenable, Thenable, Tunable, Module, 26 | tweener, easing, shapesMap, _pool: { Delta, Deltas } 27 | } 28 | 29 | // functions alias 30 | mojs.h = mojs.helpers; 31 | mojs.delta = mojs.h.delta; 32 | // custom shape add function and class 33 | mojs.addShape = mojs.shapesMap.addShape; 34 | mojs.CustomShape = mojs.shapesMap.custom; 35 | // module alias 36 | mojs.Transit = mojs.Shape; 37 | mojs.Swirl = mojs.ShapeSwirl; 38 | 39 | // TODO: 40 | /* 41 | H/V in paths 42 | 43 | rand for direction 44 | burst children angle after tune 45 | burst pathScale after tune 46 | swirl then issue 47 | 'rand' angle flick with `then` 48 | not able to `play()` in `onComplete` callback 49 | --- 50 | module names 51 | swirls in then chains for x/y 52 | parse rand(stagger(20, 10), 20) values 53 | percentage for radius 54 | */ 55 | 56 | // istanbul ignore next 57 | if ( (typeof define === "function") && define.amd ) { 58 | define("mojs", [], function () { return mojs; }); 59 | } 60 | // istanbul ignore next 61 | if ( (typeof module === "object") && (typeof module.exports === "object") ) { 62 | module.exports = mojs; 63 | } 64 | 65 | export default mojs; 66 | 67 | (typeof window !== 'undefined') && (window.mojs = mojs); 68 | -------------------------------------------------------------------------------- /js/polyfills/performance.coffee: -------------------------------------------------------------------------------- 1 | ### istanbul ignore next ### 2 | # performance.now polyfill 3 | ((root)-> 4 | if !root.performance? then root.performance = {} 5 | # IE 8 6 | Date.now = Date.now or -> (new Date).getTime() 7 | if !root.performance.now? 8 | offset = if root.performance?.timing?.navigationStart 9 | performance.timing.navigationStart 10 | else Date.now() 11 | root.performance.now = -> Date.now() - offset 12 | )(window) -------------------------------------------------------------------------------- /js/polyfills/raf.coffee: -------------------------------------------------------------------------------- 1 | ### istanbul ignore next ### 2 | # Adapted from https://gist.github.com/paulirish/1579671 which derived from 3 | # http://paulirish.com/2011/requestanimationframe-for-smart-animating/ 4 | # http://my.opera.com/emoller/blog/2011/12/20/requestanimationframe-for-smart-er-animating 5 | # requestAnimationFrame polyfill by Erik Möller. 6 | # Fixes from Paul Irish, Tino Zijdel, Andrew Mao, Klemen Slavič, Darius Bacon 7 | # MIT license 8 | 9 | do -> 10 | 'use strict' 11 | vendors = [ 12 | 'webkit' 13 | 'moz' 14 | ] 15 | i = 0 16 | w = window 17 | while i < vendors.length and !w.requestAnimationFrame 18 | vp = vendors[i] 19 | w.requestAnimationFrame = w[vp + 'RequestAnimationFrame'] 20 | cancel = w[vp + 'CancelAnimationFrame'] 21 | w.cancelAnimationFrame = cancel or w[vp + 'CancelRequestAnimationFrame'] 22 | ++i 23 | isOldBrowser = !w.requestAnimationFrame or !w.cancelAnimationFrame 24 | if /iP(ad|hone|od).*OS 6/.test(w.navigator.userAgent) or isOldBrowser 25 | lastTime = 0 26 | 27 | w.requestAnimationFrame = (callback) -> 28 | now = Date.now() 29 | nextTime = Math.max(lastTime + 16, now) 30 | setTimeout (-> 31 | callback lastTime = nextTime 32 | return 33 | ), nextTime - now 34 | 35 | w.cancelAnimationFrame = clearTimeout 36 | return -------------------------------------------------------------------------------- /js/shapes/circle.coffee: -------------------------------------------------------------------------------- 1 | # ignore coffescript sudo code 2 | ### istanbul ignore next ### 3 | 4 | Bit = require('./bit').default or require('./bit'); 5 | 6 | class Circle extends Bit 7 | _declareDefaults: -> 8 | super 9 | @_defaults.shape = 'ellipse' 10 | 11 | _draw:-> 12 | rx = if @_props.radiusX? then @_props.radiusX else @_props.radius 13 | ry = if @_props.radiusY? then @_props.radiusY else @_props.radius 14 | @_setAttrIfChanged 'rx', rx 15 | @_setAttrIfChanged 'ry', ry 16 | @_setAttrIfChanged 'cx', @_props.width/2 17 | @_setAttrIfChanged 'cy', @_props.height/2 18 | # @_setAttrIfChanged 'cx', @_props.width/2 19 | # @_setAttrIfChanged 'cy', @_props.height/2 20 | # @setAttrsIfChanged rx: rx, ry: ry, cx: @_props.x, cy: @_props.y 21 | super 22 | _getLength:-> 23 | radiusX = if @_props.radiusX? then @_props.radiusX else @_props.radius 24 | radiusY = if @_props.radiusY? then @_props.radiusY else @_props.radius 25 | # Math.pow is needed for safari's 6.0.5 odd bug 26 | # pow = Math.pow; 27 | 2*Math.PI*Math.sqrt((radiusX*radiusX + radiusY*radiusY)/2) 28 | 29 | module.exports = Circle 30 | -------------------------------------------------------------------------------- /js/shapes/cross.coffee: -------------------------------------------------------------------------------- 1 | # ignore coffescript sudo code 2 | ### istanbul ignore next ### 3 | 4 | Bit = require('./bit').default or require('./bit');; 5 | 6 | class Cross extends Bit 7 | # shape: 'path' 8 | _declareDefaults: -> 9 | super 10 | @_defaults.tag = 'path' 11 | _draw:-> 12 | super 13 | p = @_props 14 | radiusX = if @_props.radiusX? then @_props.radiusX else @_props.radius 15 | radiusY = if @_props.radiusY? then @_props.radiusY else @_props.radius 16 | 17 | isRadiusX = radiusX is @_prevRadiusX 18 | isRadiusY = radiusY is @_prevRadiusY 19 | # skip if nothing changed 20 | return if ( isRadiusX and isRadiusY ) 21 | 22 | x = @_props.width/2; y = @_props.height/2 23 | x1 = x-radiusX; x2 = x+radiusX 24 | line1 = "M#{x1},#{y} L#{x2},#{y}" 25 | y1 = y-radiusY; y2 = y+radiusY 26 | line2 = "M#{x},#{y1} L#{x},#{y2}" 27 | d = "#{line1} #{line2}" 28 | @el.setAttribute 'd', d 29 | 30 | # save the properties 31 | @_prevRadiusX = radiusX 32 | @_prevRadiusY = radiusY 33 | 34 | _getLength:-> 35 | radiusX = if @_props.radiusX? then @_props.radiusX else @_props.radius 36 | radiusY = if @_props.radiusY? then @_props.radiusY else @_props.radius 37 | 2*(radiusX+radiusY) 38 | 39 | module.exports = Cross 40 | -------------------------------------------------------------------------------- /js/shapes/curve.babel.js: -------------------------------------------------------------------------------- 1 | // istanbul ignore next 2 | import Bit from './bit'; 3 | 4 | class Curve extends Bit { 5 | /* 6 | Method to declare module's defaults. 7 | @private 8 | @overrides @ Bit 9 | */ 10 | _declareDefaults () { 11 | super._declareDefaults(); 12 | this._defaults.tag = 'path'; 13 | } 14 | /* 15 | Method to draw the module. 16 | @private 17 | @overrides @ Bit 18 | */ 19 | _draw () { 20 | super._draw(); 21 | var p = this._props; 22 | 23 | var radiusX = (p.radiusX != null) ? p.radiusX : p.radius; 24 | var radiusY = (p.radiusY != null) ? p.radiusY : p.radius; 25 | 26 | var isRadiusX = radiusX === this._prevRadiusX; 27 | var isRadiusY = radiusY === this._prevRadiusY; 28 | var isPoints = p.points === this._prevPoints; 29 | // skip if nothing changed 30 | if ( isRadiusX && isRadiusY && isPoints ) { return; } 31 | 32 | var x = p.width/2; 33 | var y = p.height/2; 34 | var x1 = x - radiusX; 35 | var x2 = x + radiusX; 36 | 37 | var d = `M${x1} ${y} Q ${x} ${ y - 2*radiusY } ${x2} ${y}`; 38 | 39 | // set the `d` attribute and save it to `_prevD` 40 | this.el.setAttribute('d', d); 41 | // save the properties 42 | this._prevPoints = p.points; 43 | this._prevRadiusX = radiusX; 44 | this._prevRadiusY = radiusY; 45 | } 46 | 47 | _getLength () { 48 | var p = this._props; 49 | 50 | var radiusX = (p.radiusX != null) ? p.radiusX : p.radius; 51 | var radiusY = (p.radiusY != null) ? p.radiusY : p.radius; 52 | 53 | var dRadius = radiusX + radiusY; 54 | var sqrt = Math.sqrt((3*radiusX + radiusY)*(radiusX + 3*radiusY)); 55 | 56 | return .5 * Math.PI * ( 3*dRadius - sqrt ); 57 | } 58 | } 59 | 60 | export default Curve; 61 | -------------------------------------------------------------------------------- /js/shapes/custom.babel.js: -------------------------------------------------------------------------------- 1 | import Bit from './bit'; 2 | 3 | class Custom extends Bit { 4 | /* 5 | Method to declare module's defaults. 6 | @private 7 | @overrides @ Bit 8 | */ 9 | _declareDefaults () { 10 | super._declareDefaults(); 11 | 12 | this._defaults.tag = 'path'; 13 | this._defaults.parent = null; 14 | 15 | // remove `stroke-width` from `_drawMap` 16 | // because we need to recal strokeWidth size regarding scale 17 | for (var i = 0; i < this._drawMap.length; i++) { 18 | if ( this._drawMap[i] === 'stroke-width' ) { 19 | this._drawMap.splice( i, 1 ); 20 | } 21 | } 22 | } 23 | /* 24 | Method to get shape to set on module's path. 25 | @public 26 | @returns {String} Empty string. 27 | */ 28 | getShape () { return ''; } 29 | /* 30 | Method to get shape perimeter length. 31 | @public 32 | @returns {Number} Default length string. 33 | */ 34 | getLength () { return 100; } 35 | /* 36 | Method to draw the shape. 37 | Called on every frame. 38 | @private 39 | @overrides @ Bit 40 | */ 41 | _draw () { 42 | var p = this._props, 43 | state = this._state, 44 | radiusXChange = state[ 'radiusX' ] !== p.radiusX, 45 | radiusYChange = state[ 'radiusY' ] !== p.radiusY, 46 | radiusChange = state[ 'radius' ] !== p.radius; 47 | 48 | // update transform only if one of radiuses changed 49 | if ( radiusXChange || radiusYChange || radiusChange ) { 50 | this.el.setAttribute( 'transform', this._getScale() ); 51 | state[ 'radiusX' ] = p.radiusX; 52 | state[ 'radiusY' ] = p.radiusY; 53 | state[ 'radius' ] = p.radius; 54 | } 55 | 56 | this._setAttrIfChanged( 'stroke-width', p['stroke-width']/p.maxScale ); 57 | 58 | super._draw(); 59 | } 60 | /* 61 | Method for initial render of the shape. 62 | @private 63 | @overrides @ Bit 64 | */ 65 | _render () { 66 | if ( this._isRendered ) { return; } 67 | this._isRendered = true; 68 | 69 | this._length = this.getLength(); 70 | 71 | var p = this._props; 72 | p.parent.innerHTML = `${this.getShape()}`; 73 | 74 | this._canvas = p.parent.querySelector('#js-mojs-shape-canvas'); 75 | this.el = p.parent.querySelector('#js-mojs-shape-el'); 76 | this._setCanvasSize(); 77 | } 78 | /* 79 | Method to get scales for the shape. 80 | @private 81 | @mutates @props 82 | */ 83 | _getScale () { 84 | var p = this._props, 85 | radiusX = ( p.radiusX ) ? p.radiusX : p.radius, 86 | radiusY = ( p.radiusY ) ? p.radiusY : p.radius; 87 | 88 | p.scaleX = (2*radiusX)/100; 89 | p.scaleY = (2*radiusY)/100; 90 | p.maxScale = Math.max( p.scaleX, p.scaleY ); 91 | 92 | p.shiftX = (p.width/2 - 50*p.scaleX); 93 | p.shiftY = (p.height/2 - 50*p.scaleY); 94 | 95 | var translate = `translate(${p.shiftX}, ${p.shiftY})`; 96 | return `${translate} scale(${ p.scaleX }, ${p.scaleY})`; 97 | } 98 | /* 99 | Method to length of the shape. 100 | @private 101 | @returns {Number} Length of the shape. 102 | */ 103 | _getLength () { return this._length; } 104 | } 105 | 106 | export default Custom; -------------------------------------------------------------------------------- /js/shapes/equal.coffee: -------------------------------------------------------------------------------- 1 | # ignore coffescript sudo code 2 | ### istanbul ignore next ### 3 | 4 | Bit = require('./bit').default or require('./bit'); 5 | 6 | class Equal extends Bit 7 | # shape: 'path' 8 | # ratio: 1.43 9 | _declareDefaults:-> 10 | super 11 | this._defaults.tag = 'path' 12 | this._defaults.points = 2 13 | _draw:-> 14 | super 15 | p = @_props 16 | return if !@_props.points 17 | 18 | radiusX = if @_props.radiusX? then @_props.radiusX else @_props.radius 19 | radiusY = if @_props.radiusY? then @_props.radiusY else @_props.radius 20 | 21 | isRadiusX = radiusX is @_prevRadiusX 22 | isRadiusY = radiusY is @_prevRadiusY 23 | isPoints = p.points is @_prevPoints 24 | # skip if nothing changed 25 | return if ( isRadiusX and isRadiusY and isPoints ) 26 | 27 | x = @_props.width/2; y = @_props.height/2 28 | x1 = x-radiusX; x2 = x+radiusX 29 | d = ''; yStep = 2*radiusY/(@_props.points-1) 30 | yStart = y-radiusY 31 | for i in [0...@_props.points] 32 | y = "#{i*yStep + yStart}" 33 | d += "M#{x1}, #{y} L#{x2}, #{y} " 34 | 35 | @el.setAttribute 'd', d 36 | 37 | # save the properties 38 | @_prevPoints = p.points 39 | @_prevRadiusX = radiusX 40 | @_prevRadiusY = radiusY 41 | 42 | _getLength:-> 43 | 2*if @_props.radiusX? then @_props.radiusX else @_props.radius 44 | 45 | module.exports = Equal 46 | -------------------------------------------------------------------------------- /js/shapes/line.coffee: -------------------------------------------------------------------------------- 1 | # ignore coffescript sudo code 2 | ### istanbul ignore next ### 3 | 4 | Bit = require('./bit').default or require('./bit'); 5 | 6 | class Line extends Bit 7 | _declareDefaults:-> 8 | super 9 | this._defaults.tag = 'line' 10 | _draw:-> 11 | radiusX = if @_props.radiusX? then @_props.radiusX else @_props.radius 12 | x = @_props.width/2 13 | y = @_props.height/2 14 | @_setAttrIfChanged 'x1', x - radiusX 15 | @_setAttrIfChanged 'x2', x + radiusX 16 | @_setAttrIfChanged 'y1', y 17 | @_setAttrIfChanged 'y2', y 18 | super 19 | 20 | module.exports = Line 21 | -------------------------------------------------------------------------------- /js/shapes/polygon.coffee: -------------------------------------------------------------------------------- 1 | # ignore coffescript sudo code 2 | ### istanbul ignore next ### 3 | 4 | Bit = require('./bit').default or require('./bit'); 5 | h = require '../h' 6 | 7 | class Polygon extends Bit 8 | ### 9 | Method to declare defaults. 10 | @overrides @ Bit 11 | ### 12 | _declareDefaults:-> 13 | super 14 | this._defaults.tag = 'path' 15 | this._defaults.points = 3 16 | ### 17 | Method to draw the shape. 18 | @overrides @ Bit 19 | ### 20 | _draw:-> 21 | p = @_props 22 | 23 | radiusX = if @_props.radiusX? then @_props.radiusX else @_props.radius 24 | radiusY = if @_props.radiusY? then @_props.radiusY else @_props.radius 25 | 26 | isRadiusX = radiusX is @_prevRadiusX 27 | isRadiusY = radiusY is @_prevRadiusY 28 | isPoints = p.points is @_prevPoints 29 | # skip if nothing changed 30 | if ( !( isRadiusX and isRadiusY and isPoints) ) 31 | 32 | step = 360/(@_props.points) 33 | # reuse radial points buffer 34 | if !@_radialPoints? then @_radialPoints = [] 35 | else @_radialPoints.length = 0 36 | 37 | for i in [0...@_props.points] 38 | @_radialPoints.push h.getRadialPoint 39 | radius: @_props.radius 40 | radiusX: @_props.radiusX 41 | radiusY: @_props.radiusY 42 | angle: (i*step) 43 | center: x: p.width/2, y: p.height/2 44 | 45 | d = '' 46 | for point, i in @_radialPoints 47 | char = if i is 0 then 'M' else 'L' 48 | d += "#{char}#{point.x.toFixed(4)},#{point.y.toFixed(4)} " 49 | 50 | # save the properties 51 | @_prevPoints = p.points 52 | @_prevRadiusX = radiusX 53 | @_prevRadiusY = radiusY 54 | 55 | @el.setAttribute 'd', (d += 'z') 56 | super 57 | 58 | ### 59 | Method to get length of the shape. 60 | @overrides @ Bit 61 | ### 62 | _getLength:-> @_getPointsPerimiter( @_radialPoints ); 63 | 64 | module.exports = Polygon 65 | -------------------------------------------------------------------------------- /js/shapes/rect.coffee: -------------------------------------------------------------------------------- 1 | # ignore coffescript sudo code 2 | ### istanbul ignore next ### 3 | 4 | Bit = require('./bit').default or require('./bit'); 5 | 6 | class Rect extends Bit 7 | # shape: 'rect' 8 | # ratio: 1.43 9 | _declareDefaults:-> 10 | super 11 | this._defaults.tag = 'rect' 12 | this._defaults.rx = 0 13 | this._defaults.ry = 0 14 | # this._defaults.ratio = 1.43 15 | _draw:-> 16 | super 17 | p = @_props 18 | radiusX = if p.radiusX? then p.radiusX else p.radius 19 | radiusY = if p.radiusY? then p.radiusY else p.radius 20 | @_setAttrIfChanged 'width', 2*radiusX 21 | @_setAttrIfChanged 'height', 2*radiusY 22 | @_setAttrIfChanged 'x', (p.width/2) - radiusX 23 | @_setAttrIfChanged 'y', (p.height/2) - radiusY 24 | @_setAttrIfChanged 'rx', p.rx 25 | @_setAttrIfChanged 'ry', p.ry 26 | 27 | _getLength:-> 28 | radiusX = if @_props.radiusX? then @_props.radiusX else @_props.radius 29 | radiusY = if @_props.radiusY? then @_props.radiusY else @_props.radius 30 | 2*(2*radiusX + 2*radiusY) 31 | 32 | module.exports = Rect 33 | -------------------------------------------------------------------------------- /js/shapes/shapesMap.coffee: -------------------------------------------------------------------------------- 1 | 2 | Bit = require('./bit').default or require('./bit'); 3 | Custom = require('./custom').default or require('./custom'); 4 | Circle = require './circle' 5 | Line = require './line' 6 | Zigzag = require './zigzag' 7 | Rect = require './rect' 8 | Polygon = require './polygon' 9 | Cross = require './cross' 10 | Curve = require('./curve').default or require('./curve') 11 | Equal = require './equal' 12 | h = require '../h' 13 | 14 | class BitsMap 15 | constructor: ()-> this.addShape = h.bind this.addShape, @ 16 | 17 | bit: Bit 18 | custom: Custom 19 | circle: Circle 20 | line: Line 21 | zigzag: Zigzag 22 | rect: Rect 23 | polygon: Polygon 24 | cross: Cross 25 | equal: Equal 26 | curve: Curve 27 | getShape:(name)-> @[name] or h.error "no \"#{name}\" shape available yet, 28 | please choose from this list:", [ 'circle', 'line', 'zigzag', 'rect', 'polygon', 'cross', 'equal', 'curve' ] 29 | 30 | ### 31 | Method to add shape to the map. 32 | @public 33 | @param {String} Name of the shape module. 34 | @param {Object} Shape module class. 35 | ### 36 | addShape:(name, Module)-> @[name] = Module 37 | 38 | module.exports = new BitsMap 39 | -------------------------------------------------------------------------------- /js/shapes/zigzag.coffee: -------------------------------------------------------------------------------- 1 | # ignore coffescript sudo code 2 | ### istanbul ignore next ### 3 | 4 | Bit = require('./bit').default or require('./bit'); 5 | 6 | class Zigzag extends Bit 7 | _declareDefaults:-> 8 | super 9 | @_defaults.tag = 'path' 10 | @_defaults.points = 3 11 | # @_defaults.ratio = 1.43; 12 | _draw:-> 13 | super 14 | p = this._props 15 | return if !@_props.points 16 | 17 | radiusX = if @_props.radiusX? then @_props.radiusX else @_props.radius 18 | radiusY = if @_props.radiusY? then @_props.radiusY else @_props.radius 19 | 20 | isRadiusX = radiusX is @_prevRadiusX 21 | isRadiusY = radiusY is @_prevRadiusY 22 | isPoints = p.points is @_prevPoints 23 | # skip if nothing changed 24 | return if ( isRadiusX and isRadiusY and isPoints ) 25 | 26 | x = p.width/2 27 | y = p.height/2 28 | 29 | currentX = x-radiusX 30 | currentY = y 31 | stepX = (2*radiusX) / (p.points-1) 32 | yFlip = -1 33 | 34 | delta = Math.sqrt(stepX*stepX + radiusY*radiusY) 35 | length = -delta 36 | 37 | points = "M#{currentX}, #{y} " 38 | for i in [0...p.points] 39 | points += "L#{currentX}, #{currentY} " 40 | currentX += stepX 41 | length += delta 42 | 43 | currentY = if yFlip is -1 then y-radiusY else y 44 | yFlip = -yFlip 45 | 46 | @_length = length 47 | @el.setAttribute 'd', points 48 | 49 | # save the properties 50 | @_prevPoints = p.points 51 | @_prevRadiusX = radiusX 52 | @_prevRadiusY = radiusY 53 | 54 | _getLength:-> @_length 55 | 56 | module.exports = Zigzag 57 | -------------------------------------------------------------------------------- /js/stagger.babel.js: -------------------------------------------------------------------------------- 1 | import h from './h'; 2 | import Timeline from './tween/timeline'; 3 | import Tunable from './tunable'; 4 | 5 | class Stagger extends Tunable { 6 | constructor (options, Module) { 7 | super(); 8 | return this._init(options, Module); 9 | } 10 | /* 11 | Method to create then chain on child modules. 12 | @param {Object} Then options. 13 | @return {Object} this. 14 | */ 15 | then (o) { 16 | if (o == null) { return this; } 17 | for (var i = 0; i < this._modules.length; i++ ) { 18 | // get child module's option and pass to the child `then` 19 | this._modules[i].then( this._getOptionByIndex(i, o) ); 20 | } 21 | this.timeline._recalcTotalDuration(); 22 | return this; 23 | } 24 | /* 25 | Method to tune child modules. 26 | @param {Object} Tune options. 27 | @return {Object} this. 28 | */ 29 | tune (o) { 30 | if (o == null) { return this; } 31 | for (var i = 0; i < this._modules.length; i++ ) { 32 | // get child module's option and pass to the child `then` 33 | this._modules[i].tune( this._getOptionByIndex(i, o) ); 34 | } 35 | this.timeline._recalcTotalDuration(); 36 | return this; 37 | } 38 | /* 39 | Method to generate child modules. 40 | @return {Object} this. 41 | */ 42 | generate () { 43 | for (var i = 0; i < this._modules.length; i++ ) { 44 | // get child module's option and pass to the child `then` 45 | this._modules[i].generate(); 46 | } 47 | this.timeline._recalcTotalDuration(); 48 | return this; 49 | } 50 | /* 51 | Method to get an option by modulo and name. 52 | @param {String} Name of the property to get. 53 | @param {Number} Index for the modulo calculation. 54 | @param {Object} Options hash to look in. 55 | @return {Any} Property. 56 | */ 57 | _getOptionByMod (name, i, store) { 58 | var props = store[name]; 59 | // if not dom list then clone it to array 60 | if (props + '' === '[object NodeList]' || props + '' === '[object HTMLCollection]') 61 | props = Array.prototype.slice.call(props, 0); 62 | // get the value in array or return the value itself 63 | var value = h.isArray(props) ? props[i % props.length] : props; 64 | // check if value has the stagger expression, if so parse it 65 | return h.parseIfStagger(value, i); 66 | } 67 | /* 68 | Method to get option by modulo of index. 69 | @param {Number} Index for modulo calculations. 70 | @param {Object} Options hash to look in. 71 | */ 72 | _getOptionByIndex (i, store) { 73 | var options = {}; 74 | Object.keys(store).forEach(key => 75 | options[key] = this._getOptionByMod(key, i, store)); 76 | return options; 77 | } 78 | /* 79 | Method to get total child modules quantity. 80 | @param {String} Name of quantifier in options hash. 81 | @param {Object} Options hash object. 82 | @return {Number} Number of child object that should be defined. 83 | */ 84 | _getChildQuantity (name, store) { 85 | // if number was set 86 | if (typeof name === 'number') { return name; } 87 | 88 | var quantifier = store[name]; 89 | if (h.isArray(quantifier)) { return quantifier.length; } 90 | else if (quantifier + '' === '[object NodeList]') { 91 | return quantifier.length; 92 | } else if (quantifier + '' === '[object HTMLCollection]') { 93 | return Array.prototype.slice.call(quantifier, 0).length; 94 | } else if (quantifier instanceof HTMLElement) { 95 | return 1; 96 | } else if (typeof quantifier == 'string') { 97 | return 1; 98 | } 99 | } 100 | /* 101 | Method to make stagger form options 102 | @param {Object} Options. 103 | @param {Object} Child class. 104 | */ 105 | _init (options, Module) { 106 | var count = this._getChildQuantity(options.quantifier || 'el', options); 107 | this._createTimeline(options); this._modules = []; 108 | for (var i = 0; i < count; i++) { 109 | // get child module's option 110 | var option = this._getOptionByIndex(i, options); 111 | option.isRunLess = true; 112 | // set index of the module 113 | option.index = i; 114 | // create child module 115 | var module = new Module(option); this._modules.push(module); 116 | // add child module's timeline to the self timeline 117 | this.timeline.add(module); 118 | } 119 | return this; 120 | } 121 | /* 122 | Method to create timeline. 123 | @param {Object} Timeline options. 124 | */ 125 | _createTimeline (options = {}) { 126 | this.timeline = new Timeline(options.timeline); 127 | } 128 | 129 | /* @overrides @ Tweenable */ 130 | _makeTween () {} 131 | _makeTimeline () {} 132 | } 133 | 134 | module.exports = function(Module) { 135 | return options => new Stagger(options, Module); 136 | }; -------------------------------------------------------------------------------- /js/tween/tweenable.babel.js: -------------------------------------------------------------------------------- 1 | import Tween from './tween'; 2 | import Timeline from './timeline'; 3 | import Module from '../module'; 4 | 5 | /* 6 | Class to define a module ancestor 7 | with timeline and tween options and functionality. 8 | All runable modules should inherit from this class. 9 | 10 | @class Tweenable 11 | */ 12 | class Tweenable extends Module { 13 | /* 14 | `play` method for the timeline. 15 | @public 16 | @param {Number} Time shift. 17 | @returns this. 18 | */ 19 | play () { 20 | this.timeline.play.apply( this.timeline, arguments ); 21 | return this; 22 | } 23 | /* 24 | `playBackward` method for the timeline. 25 | @public 26 | @param {Number} Time shift. 27 | @returns this. 28 | */ 29 | playBackward () { 30 | this.timeline.playBackward.apply( this.timeline, arguments ); 31 | return this; 32 | } 33 | /* 34 | `pause` method for the timeline. 35 | @public 36 | @returns this. 37 | */ 38 | pause () { 39 | this.timeline.pause.apply( this.timeline, arguments ); 40 | return this; 41 | } 42 | /* 43 | `stop` method for the timeline. 44 | @public 45 | @param {Number} [0...1] Progress to set on stop. 46 | *Default* is `0` if `play` 47 | and `1` if `playBAckward`. 48 | @returns this. 49 | */ 50 | stop () { 51 | this.timeline.stop.apply( this.timeline, arguments ); 52 | return this; 53 | } 54 | /* 55 | `reset` method for the timeline. 56 | @public 57 | @returns this. 58 | */ 59 | reset () { 60 | this.timeline.reset.apply( this.timeline, arguments ); 61 | return this; 62 | } 63 | /* 64 | `replay` method for the timeline. 65 | @public 66 | @returns this. 67 | */ 68 | replay () { 69 | this.timeline.replay.apply( this.timeline, arguments ); 70 | return this; 71 | } 72 | /* 73 | `replay` method for the timeline. 74 | @public 75 | @returns this. 76 | */ 77 | replayBackward () { 78 | this.timeline.replayBackward.apply( this.timeline, arguments ); 79 | return this; 80 | } 81 | /* 82 | `resume` method for the timeline. 83 | @public 84 | @param {Number} Time shift. 85 | @returns this. 86 | */ 87 | resume (shift = 0) { 88 | this.timeline.resume.apply( this.timeline, arguments ); 89 | return this; 90 | } 91 | /* 92 | `setProgress` method for the timeline. 93 | @public 94 | @param {Number} [0...1] Progress value. 95 | @returns this. 96 | */ 97 | setProgress () { 98 | this.timeline.setProgress.apply( this.timeline, arguments ); 99 | return this; 100 | } 101 | /* 102 | setSpeed method for the timeline. 103 | @param {Number} Speed value. 104 | @returns this. 105 | */ 106 | setSpeed ( speed ) { 107 | this.timeline.setSpeed.apply( this.timeline, arguments ); 108 | return this; 109 | } 110 | 111 | // ^ PUBLIC METHOD(S) ^ 112 | // v PRIVATE METHOD(S) v 113 | 114 | constructor ( o = {} ) { 115 | // super of Module 116 | super( o ); 117 | // pipe function for _o object 118 | // before creating tween/timeline 119 | this._transformTweenOptions(); 120 | // make tween only if isTweenLess option is not set 121 | !this._o.isTweenLess && this._makeTween(); 122 | // make timeline only if isTimelineLess option is not set 123 | !this._o.isTimelineLess && this._makeTimeline(); 124 | } 125 | /* 126 | Placeholder method that should be overrided 127 | and will be called before tween/timeline creation. 128 | @private 129 | */ 130 | _transformTweenOptions () {} 131 | /* 132 | Method to create tween. 133 | @private 134 | */ 135 | _makeTween () { 136 | // pass callbacks context 137 | this._o.callbacksContext = this._o.callbacksContext || this; 138 | this.tween = new Tween( this._o ); 139 | // make timeline property point to tween one is "no timeline" mode 140 | ( this._o.isTimelineLess ) && ( this.timeline = this.tween ); 141 | } 142 | /* 143 | Method to create timeline. 144 | @private 145 | @param {Object} Timeline's options. 146 | An object which contains "timeline" property with 147 | timeline options. 148 | */ 149 | _makeTimeline () { 150 | // pass callbacks context 151 | this._o.timeline = this._o.timeline || {}; 152 | this._o.timeline.callbacksContext = this._o.callbacksContext || this; 153 | this.timeline = new Timeline( this._o.timeline ); 154 | // set the flag to indicate that real timeline is present 155 | this._isTimeline = true; 156 | // if tween exist - add it to the timeline there is some 157 | // modules like burst and stagger that have no tween 158 | this.tween && this.timeline.add( this.tween ); 159 | } 160 | } 161 | 162 | export default Tweenable; 163 | -------------------------------------------------------------------------------- /js/tween/tweener.babel.js: -------------------------------------------------------------------------------- 1 | import '../polyfills/raf'; 2 | import '../polyfills/performance'; 3 | import h from '../h'; 4 | 5 | class Tweener { 6 | constructor() { 7 | this._vars(); 8 | this._listenVisibilityChange(); 9 | return this; 10 | } 11 | 12 | _vars () { 13 | this.tweens = []; 14 | this._savedTweens = []; 15 | this._loop = this._loop.bind(this); 16 | this._onVisibilityChange = this._onVisibilityChange.bind(this); 17 | } 18 | /* 19 | Main animation loop. Should have only one concurrent loop. 20 | @private 21 | @returns this 22 | */ 23 | _loop() { 24 | if (!this._isRunning) { return false; } 25 | this._update(window.performance.now()); 26 | if (!this.tweens.length) { return this._isRunning = false; } 27 | requestAnimationFrame(this._loop); 28 | return this; 29 | } 30 | /* 31 | Method to start animation loop. 32 | @private 33 | */ 34 | _startLoop() { 35 | if (this._isRunning) { return; }; this._isRunning = true 36 | requestAnimationFrame(this._loop); 37 | } 38 | /* 39 | Method to stop animation loop. 40 | @private 41 | */ 42 | _stopLoop() { this._isRunning = false; } 43 | /* 44 | Method to update every tween/timeline on animation frame. 45 | @private 46 | */ 47 | _update(time) { 48 | var i = this.tweens.length; 49 | while(i--) { 50 | // cache the current tween 51 | var tween = this.tweens[i]; 52 | if ( tween && tween._update(time) === true ) { 53 | this.remove( tween ); 54 | tween._onTweenerFinish(); 55 | tween._prevTime = undefined; 56 | } 57 | } 58 | } 59 | /* 60 | Method to add a Tween/Timeline to loop pool. 61 | @param {Object} Tween/Timeline to add. 62 | */ 63 | add(tween) { 64 | // return if tween is already running 65 | if ( tween._isRunning ) { return; } 66 | tween._isRunning = true; 67 | this.tweens.push(tween); 68 | this._startLoop(); 69 | } 70 | /* 71 | Method stop updating all the child tweens/timelines. 72 | @private 73 | */ 74 | removeAll() { this.tweens.length = 0; } 75 | /* 76 | Method to remove specific tween/timeline form updating. 77 | @private 78 | */ 79 | remove(tween) { 80 | var index = (typeof tween === 'number') 81 | ? tween 82 | : this.tweens.indexOf(tween); 83 | 84 | if (index !== -1) { 85 | tween = this.tweens[index]; 86 | if ( tween ) { 87 | tween._isRunning = false; 88 | this.tweens.splice(index, 1); 89 | tween._onTweenerRemove(); 90 | } 91 | } 92 | } 93 | 94 | /* 95 | Method to initialize event listeners to visibility change events. 96 | @private 97 | */ 98 | _listenVisibilityChange () { 99 | if (typeof document.hidden !== "undefined") { 100 | this._visibilityHidden = "hidden"; 101 | this._visibilityChange = "visibilitychange"; 102 | } else if (typeof document.mozHidden !== "undefined") { 103 | this._visibilityHidden = "mozHidden"; 104 | this._visibilityChange = "mozvisibilitychange"; 105 | } else if (typeof document.msHidden !== "undefined") { 106 | this._visibilityHidden = "msHidden"; 107 | this._visibilityChange = "msvisibilitychange"; 108 | } else if (typeof document.webkitHidden !== "undefined") { 109 | this._visibilityHidden = "webkitHidden"; 110 | this._visibilityChange = "webkitvisibilitychange"; 111 | } 112 | 113 | document.addEventListener(this._visibilityChange, 114 | this._onVisibilityChange, false); 115 | } 116 | /* 117 | Method that will fire on visibility change. 118 | */ 119 | _onVisibilityChange () { 120 | if (document[this._visibilityHidden]) { this._savePlayingTweens() } 121 | else { this._restorePlayingTweens(); } 122 | } 123 | /* 124 | Method to save all playing tweens. 125 | @private 126 | */ 127 | _savePlayingTweens () { 128 | this._savedTweens = this.tweens.slice(0); 129 | for (let i = 0; i < this._savedTweens.length; i++ ) { 130 | this._savedTweens[i].pause(); 131 | } 132 | } 133 | /* 134 | Method to restore all playing tweens. 135 | @private 136 | */ 137 | _restorePlayingTweens () { 138 | for (let i = 0; i < this._savedTweens.length; i++ ) { 139 | this._savedTweens[i].resume(); 140 | } 141 | } 142 | } 143 | 144 | var t = new Tweener 145 | export default t; 146 | -------------------------------------------------------------------------------- /logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mojs-contrib/mojs/856bf4c4cfdcabab3a78c18dfd419d429b78817e/logo.png -------------------------------------------------------------------------------- /mockups/sample.babel.js: -------------------------------------------------------------------------------- 1 | 2 | var cache = function cache ( fun, samples, n ) { 3 | 4 | var samplesAmount = Math.pow( 10, n ); 5 | var samplesStep = 1/samplesAmount; 6 | 7 | function RoundNumber (input, numberDecimals) 8 | { 9 | numberDecimals = +numberDecimals || 0; // +var magic! 10 | 11 | var multiplyer = Math.pow(10.0, numberDecimals); 12 | 13 | return Math.round(input * multiplyer) / multiplyer; 14 | } 15 | 16 | window.RoundNumber = RoundNumber; 17 | 18 | var cached = function cached (p) { 19 | 20 | // return parseFloat(RoundNumber(obj.a, n)); 21 | 22 | var newKey = RoundNumber(p, n); 23 | var sample = samples[ newKey.toString() ]; 24 | 25 | if ( Math.abs(p - newKey) < samplesStep ) { return sample; } 26 | 27 | if ( p > newKey ) { 28 | var nextIndex = newKey + samplesStep; 29 | var nextValue = samples[ nextIndex ]; 30 | } else { 31 | var nextIndex = newKey - samplesStep; 32 | var nextValue = samples[ nextIndex ]; 33 | } 34 | 35 | var dLength = nextIndex - newKey; 36 | var dValue = nextValue - sample; 37 | if ( dValue < samplesStep ) { 38 | return sample; 39 | } 40 | 41 | var progressScale = (p - newKey)/dLength; 42 | var coef = ( nextValue > sample ) ? -1 : 1; 43 | var scaledDifference = coef*progressScale*dValue; 44 | 45 | return sample + scaledDifference; 46 | } 47 | 48 | cached.getSamples = () => { return samples; } 49 | 50 | return cached; 51 | } 52 | 53 | var preSample = function preSample ( fun, n = 4 ) { 54 | var samples = {}, 55 | p = 0; 56 | 57 | var samplesCount = Math.pow( 10, n ); 58 | var step = 1/samplesCount; 59 | 60 | samples[ 0 ] = fun(0); 61 | for (var i = 0; i < samplesCount-1; i++) { 62 | p += step; 63 | 64 | var index = parseFloat(p.toFixed(n)); 65 | samples[ index ] = fun( p ); 66 | } 67 | samples[ 1 ] = fun(1); 68 | 69 | return cache( fun, samples, n ); 70 | } 71 | 72 | 73 | var curvePath = mojs.easing.path( 'M0,100 C21.3776817,95.8051376 50,77.3262711 50,-700 C50,80.1708527 76.6222458,93.9449005 100,100' ); 74 | 75 | const id = function (p) { 76 | return p; 77 | } 78 | 79 | const scale = function (curve, n) { 80 | return (p) => { return n*curve(p); } 81 | } 82 | 83 | const increase = function (curve, n) { 84 | return (p) => { return n + curve(p); } 85 | } 86 | 87 | const scaleCurve = id(id(id(id(increase( scale( curvePath, .65 ), 1 ))))); 88 | 89 | var preScaleCurve = preSample( scaleCurve ); 90 | 91 | var simple = mojs.easing.cubic.out; 92 | 93 | var p = 0; 94 | var suite = new Benchmark.Suite; 95 | // add tests 96 | suite 97 | .add('normal', function() { 98 | p += Math.random()/100; 99 | p = (p > 1) ? 0 : p; 100 | scaleCurve(p); 101 | }) 102 | .add('cached', function() { 103 | p += Math.random()/100; 104 | p = (p > 1) ? 0 : p; 105 | preScaleCurve(p); 106 | }) 107 | .add('simple', function() { 108 | p += Math.random()/100; 109 | p = (p > 1) ? 0 : p; 110 | simple(p); 111 | }) 112 | .on('start', function () { 113 | console.log('setup'); 114 | }) 115 | .on('error', function (e) { 116 | console.log(e); 117 | }) 118 | // add listeners 119 | .on('cycle', function(event) { 120 | console.log(String(event.target)); 121 | }) 122 | .on('complete', function() { 123 | console.log('Fastest is ' + this.filter('fastest').map('name')); 124 | // console.log(JSON.stringify( preScaleCurve.getSamples() )); 125 | }) 126 | // run async 127 | .run({ 'async': true }); -------------------------------------------------------------------------------- /mockups/spring.coffee: -------------------------------------------------------------------------------- 1 | spring = (options={}) -> 2 | # applyDefaults(options, arguments.callee.defaults) 3 | 4 | frequency = Math.max(1, 10 / 20) 5 | friction = Math.pow(20, 20 / 100) 6 | s = 0 / 1000 7 | decal = Math.max(0, s) 8 | 9 | # In case of anticipation 10 | A1 = (t) -> 11 | M = 0.8 12 | 13 | x0 = (s / (1 - s)) 14 | x1 = 0 15 | 16 | b = (x0 - (M * x1)) / (x0 - x1) 17 | a = (M - b) / x0 18 | 19 | (a * t * options.anticipationStrength / 100) + b 20 | 21 | # Normal curve 22 | A2 = (t) -> 23 | Math.pow(friction / 10,-t) * (1 - t) 24 | 25 | (t) -> 26 | t = Math.max(t, 0) 27 | t = Math.min(t, 1) 28 | frictionT = (t / (1 - s)) - (s / (1 - s)) 29 | 30 | if t < s 31 | yS = (s / (1 - s)) - (s / (1 - s)) 32 | y0 = (0 / (1 - s)) - (s / (1 - s)) 33 | b = Math.acos(1 / A1(yS)) 34 | a = (Math.acos(1 / A1(y0)) - b) / (frequency * (-s)) 35 | A = A1 36 | else 37 | A = A2 38 | b = 0 39 | a = 1 40 | 41 | At = A(frictionT) 42 | 43 | angle = frequency * (t - s) * a + b 44 | 1 - (At * Math.cos(angle)) 45 | 46 | a = spring() 47 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mo-js", 3 | "description": "motion graphics toolbelt for the web", 4 | "version": "0.288.2", 5 | "license": "MIT", 6 | "private": false, 7 | "scripts": { 8 | "build": "", 9 | "start": "webpack", 10 | "test": "karma start --single-run" 11 | }, 12 | "main": "build/mo.js", 13 | "keywords": [ 14 | "motion", 15 | "effects", 16 | "animation", 17 | "motion graphics" 18 | ], 19 | "author": { 20 | "name": "LegoMushroom", 21 | "email": "legomushroom@gmail.com", 22 | "twitter": "@legomushroom", 23 | "url": "https://twitter.com/legomushroom" 24 | }, 25 | "repository": { 26 | "type": "git", 27 | "url": "https://github.com/legomushroom/mojs.git" 28 | }, 29 | "bugs": { 30 | "url": "https://github.com/legomushroom/mojs/issues" 31 | }, 32 | "homepage": "https://github.com/legomushroom/mojs", 33 | "dependencies": { 34 | "babel-runtime": "^6.5.0" 35 | }, 36 | "devDependencies": { 37 | "6to5-loader": "^3.0.0", 38 | "6to5-runtime": "^3.6.5", 39 | "babel-core": "^6.5.2", 40 | "babel-loader": "^6.2.2", 41 | "babel-plugin-transform-es2015-classes": "^6.6.5", 42 | "babel-plugin-transform-runtime": "^6.5.2", 43 | "babel-preset-es2015": "^6.5.0", 44 | "babel-preset-es2015-loose": "^7.0.0", 45 | "babel-preset-react": "^6.5.0", 46 | "babel-preset-stage-0": "^6.5.0", 47 | "babel-preset-stage-2": "^6.11.0", 48 | "brfs": "^1.2.0", 49 | "browserify": "^6.2.0", 50 | "browserify-istanbul": "^0.1.2", 51 | "browserify-shim": "~3.8.0", 52 | "chalk": "^0.5.1", 53 | "coffee-loader": "^0.7.2", 54 | "coffeeify": "latest", 55 | "coveralls": "^2.11.2", 56 | "grock": "git+https://github.com/legomushroom/grock.git", 57 | "gulp": "latest", 58 | "gulp-autoprefixer": "0.0.6", 59 | "gulp-browserify": "latest", 60 | "gulp-changed": "~0.2.0", 61 | "gulp-coffee": "~1.4.1", 62 | "gulp-coffeeify": "^0.1.8", 63 | "gulp-coffeelint": "*", 64 | "gulp-concat": "*", 65 | "gulp-csslint": "*", 66 | "gulp-insert": "^0.4.0", 67 | "gulp-jade": "~0.4.2", 68 | "gulp-json-editor": "^2.2.1", 69 | "gulp-karma": "*", 70 | "gulp-livereload": "~1.2.0", 71 | "gulp-minify-css": "~0.3.0", 72 | "gulp-notify": "~0.5.1", 73 | "gulp-plumber": "*", 74 | "gulp-rename": "latest", 75 | "gulp-shell": "^0.4.0", 76 | "gulp-stylus": "latest", 77 | "gulp-uglify": "latest", 78 | "gulp-watch": "*", 79 | "karma": "^0.12.24", 80 | "karma-babel-preprocessor": "^6.0.1", 81 | "karma-browserify": "^2.0.0", 82 | "karma-chrome-launcher": "^0.1.4", 83 | "karma-clear-screen-reporter": "0.0.1", 84 | "karma-cli": "0.0.4", 85 | "karma-coverage": "0.2.6", 86 | "karma-jasmine": "^0.2.0", 87 | "karma-phantomjs-launcher": "^0.1.4", 88 | "karma-sauce-launcher": "^0.2.10", 89 | "mojs-player": "^0.40.0", 90 | "nodemon": "*", 91 | "phantomjs-polyfill": "0.0.2", 92 | "pygments": "^0.2.0", 93 | "run-sequence": "^1.0.2", 94 | "webpack": "^1.14.0" 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /spec/easing/approximate.coffee: -------------------------------------------------------------------------------- 1 | 2 | approximate = mojs.easing.approximate 3 | 4 | describe 'approximate method ->', -> 5 | describe '_sample method', -> 6 | it 'should call _proximate method with fn, samples', -> 7 | spyOn approximate, '_proximate' 8 | 9 | fn = (k)-> 2*k 10 | n = 4 11 | samplesCount = Math.pow( 10, n ) 12 | approximate fn, n 13 | 14 | samples = approximate._proximate.calls.first().args[0] 15 | keys = Object.keys(samples) 16 | expect(keys.length).toBe samplesCount + 2 17 | 18 | expect(samples[0]).toBe fn(0) 19 | p = 0 20 | step = 1/samplesCount 21 | for i in [0...samplesCount-1] 22 | p += step 23 | index = parseFloat(p.toFixed(n)); 24 | expect(samples[ index ]).toBe fn(p) 25 | 26 | expect(samples[1]).toBe fn(1) 27 | 28 | it 'should preserve presicion', -> 29 | spyOn approximate, '_proximate' 30 | 31 | fn = (k)-> 2*k 32 | n = 4 33 | samplesCount = Math.pow( 10, n ) 34 | approximate fn, n 35 | 36 | samples = approximate._proximate.calls.first().args[0] 37 | keys = Object.keys(samples) 38 | 39 | expect(parseFloat(keys[101]) - parseFloat(keys[100])) 40 | .toBeCloseTo 0, n-1 41 | 42 | it 'should set base to the samples', -> 43 | spyOn approximate, '_proximate' 44 | 45 | fn = (k)-> 2*k 46 | n = 4 47 | samplesCount = Math.pow( 10, n ) 48 | approximate fn, n 49 | 50 | samples = approximate._proximate.calls.first().args[0] 51 | expect(samples.base).toBe n 52 | 53 | it 'should return a function', -> 54 | fn = (k)-> 2*k 55 | n = 4 56 | result = approximate fn, n 57 | 58 | expect(typeof result).toBe 'function' 59 | 60 | p = 0; size = 10000; step = 1/size 61 | for i in [0..size] 62 | expect(result(p)).toBeCloseTo fn(p) 63 | p += Math.random() 64 | if p > 1 then p = 0 65 | 66 | it 'should return samples', -> 67 | spyOn(approximate, '_proximate').and.callThrough() 68 | fn = (k)-> 2*k 69 | n = 4 70 | result = approximate(fn, n) 71 | samples = approximate._proximate.calls.first().args[0] 72 | 73 | expect(result.getSamples()).toBe samples 74 | 75 | it 'should be able to load with samples', -> 76 | spyOn(approximate, '_proximate').and.callThrough() 77 | 78 | loadSamples = {} 79 | fn = (k)-> 2*k 80 | n = 4 81 | result = approximate(fn, loadSamples) 82 | 83 | expect(approximate._proximate).toHaveBeenCalledWith loadSamples 84 | samples = approximate._proximate.calls.first().args[0] 85 | expect(result.getSamples()).toBe loadSamples 86 | 87 | it 'should be able to load with stringified samples', -> 88 | spyOn(approximate, '_proximate').and.callThrough() 89 | 90 | loadSamples = { a: 2, c: 3, d: 10 } 91 | fn = (k)-> 2*k 92 | n = 4 93 | result = approximate(fn, JSON.stringify(loadSamples) ) 94 | 95 | expect(approximate._proximate).toHaveBeenCalledWith loadSamples 96 | samples = approximate._proximate.calls.first().args[0] 97 | expect(result.getSamples()).toEqual loadSamples 98 | 99 | 100 | -------------------------------------------------------------------------------- /spec/easing/approximate.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | var approximate; 3 | 4 | approximate = mojs.easing.approximate; 5 | 6 | describe('approximate method ->', function() { 7 | return describe('_sample method', function() { 8 | it('should call _proximate method with fn, samples', function() { 9 | var fn, i, index, keys, n, p, samples, samplesCount, step, _i, _ref; 10 | spyOn(approximate, '_proximate'); 11 | fn = function(k) { 12 | return 2 * k; 13 | }; 14 | n = 4; 15 | samplesCount = Math.pow(10, n); 16 | approximate(fn, n); 17 | samples = approximate._proximate.calls.first().args[0]; 18 | keys = Object.keys(samples); 19 | expect(keys.length).toBe(samplesCount + 2); 20 | expect(samples[0]).toBe(fn(0)); 21 | p = 0; 22 | step = 1 / samplesCount; 23 | for (i = _i = 0, _ref = samplesCount - 1; 0 <= _ref ? _i < _ref : _i > _ref; i = 0 <= _ref ? ++_i : --_i) { 24 | p += step; 25 | index = parseFloat(p.toFixed(n)); 26 | expect(samples[index]).toBe(fn(p)); 27 | } 28 | return expect(samples[1]).toBe(fn(1)); 29 | }); 30 | it('should preserve presicion', function() { 31 | var fn, keys, n, samples, samplesCount; 32 | spyOn(approximate, '_proximate'); 33 | fn = function(k) { 34 | return 2 * k; 35 | }; 36 | n = 4; 37 | samplesCount = Math.pow(10, n); 38 | approximate(fn, n); 39 | samples = approximate._proximate.calls.first().args[0]; 40 | keys = Object.keys(samples); 41 | return expect(parseFloat(keys[101]) - parseFloat(keys[100])).toBeCloseTo(0, n - 1); 42 | }); 43 | it('should set base to the samples', function() { 44 | var fn, n, samples, samplesCount; 45 | spyOn(approximate, '_proximate'); 46 | fn = function(k) { 47 | return 2 * k; 48 | }; 49 | n = 4; 50 | samplesCount = Math.pow(10, n); 51 | approximate(fn, n); 52 | samples = approximate._proximate.calls.first().args[0]; 53 | return expect(samples.base).toBe(n); 54 | }); 55 | it('should return a function', function() { 56 | var fn, i, n, p, result, size, step, _i, _results; 57 | fn = function(k) { 58 | return 2 * k; 59 | }; 60 | n = 4; 61 | result = approximate(fn, n); 62 | expect(typeof result).toBe('function'); 63 | p = 0; 64 | size = 10000; 65 | step = 1 / size; 66 | _results = []; 67 | for (i = _i = 0; 0 <= size ? _i <= size : _i >= size; i = 0 <= size ? ++_i : --_i) { 68 | expect(result(p)).toBeCloseTo(fn(p)); 69 | p += Math.random(); 70 | if (p > 1) { 71 | _results.push(p = 0); 72 | } else { 73 | _results.push(void 0); 74 | } 75 | } 76 | return _results; 77 | }); 78 | it('should return samples', function() { 79 | var fn, n, result, samples; 80 | spyOn(approximate, '_proximate').and.callThrough(); 81 | fn = function(k) { 82 | return 2 * k; 83 | }; 84 | n = 4; 85 | result = approximate(fn, n); 86 | samples = approximate._proximate.calls.first().args[0]; 87 | return expect(result.getSamples()).toBe(samples); 88 | }); 89 | it('should be able to load with samples', function() { 90 | var fn, loadSamples, n, result, samples; 91 | spyOn(approximate, '_proximate').and.callThrough(); 92 | loadSamples = {}; 93 | fn = function(k) { 94 | return 2 * k; 95 | }; 96 | n = 4; 97 | result = approximate(fn, loadSamples); 98 | expect(approximate._proximate).toHaveBeenCalledWith(loadSamples); 99 | samples = approximate._proximate.calls.first().args[0]; 100 | return expect(result.getSamples()).toBe(loadSamples); 101 | }); 102 | return it('should be able to load with stringified samples', function() { 103 | var fn, loadSamples, n, result, samples; 104 | spyOn(approximate, '_proximate').and.callThrough(); 105 | loadSamples = { 106 | a: 2, 107 | c: 3, 108 | d: 10 109 | }; 110 | fn = function(k) { 111 | return 2 * k; 112 | }; 113 | n = 4; 114 | result = approximate(fn, JSON.stringify(loadSamples)); 115 | expect(approximate._proximate).toHaveBeenCalledWith(loadSamples); 116 | samples = approximate._proximate.calls.first().args[0]; 117 | return expect(result.getSamples()).toEqual(loadSamples); 118 | }); 119 | }); 120 | }); 121 | 122 | }).call(this); 123 | -------------------------------------------------------------------------------- /spec/easing/bezier-easing.coffee: -------------------------------------------------------------------------------- 1 | bezier = mojs.easing.bezier 2 | 3 | describe 'bezier easing ->', -> 4 | it 'should be a function', -> 5 | expect(typeof bezier).toBe 'function' 6 | it 'should return a function', -> 7 | expect(typeof bezier(0, 0, 1, 1)).toBe 'function' 8 | describe 'linear curves ->', -> 9 | it 'shoud be linear', -> 10 | bezier1 = bezier(0,0,1,1); bezier2 = bezier 1,1,0,0 11 | samples = 100 12 | for i in [0..samples] 13 | x = i / samples 14 | expect(bezier1(x)).toBe bezier2(x) 15 | expect(bezier1(x)).toBe x 16 | expect(bezier1(x)).toBeDefined() 17 | describe 'common props ->', -> 18 | it 'should be the right value at extremes', -> 19 | for i in [0...1000] 20 | a = Math.random(); b = 2*Math.random()-0.5 21 | c = Math.random(); d = 2*Math.random()-0.5 22 | easing = bezier(a,b,c,d) 23 | expect(easing(0)).toBe 0 24 | expect(easing(1)).toBe 1 25 | it 'should approach the projected value of its x=y projected curve', -> 26 | samples = 1000 27 | for i in [0...samples] 28 | x = i / samples 29 | a = Math.random(); b = Math.random() 30 | c = Math.random(); d = Math.random() 31 | easing = bezier(a,b,c,d) 32 | projected = bezier(b,a,d,c) 33 | composed = (x)-> projected easing x 34 | expect(x).toBeCloseTo composed(x), 1 35 | describe 'two same instances ->', -> 36 | it 'should be strictly equal', -> 37 | samples = 100 38 | for i in [0..samples] 39 | a = Math.random(); b = 2*Math.random()-0.5 40 | c = Math.random(); d = 2*Math.random()-0.5 41 | x = i/samples 42 | expect(bezier(a,b,c,d)(x)).toBe bezier(a,b,c,d)(x) 43 | 44 | describe 'symetric curves ->', -> 45 | it 'should have a central value y~=0.5 at x=0.5', -> 46 | samples = 100 47 | for i in [0..samples] 48 | a = Math.random(); b = 2*Math.random()-0.5 49 | c = 1-a; d = 1-b; easing = bezier(a,b,c,d) 50 | expect(easing(.5)).toBeCloseTo .5 51 | 52 | it 'should be symetrical', -> 53 | samples = 100 54 | for i in [0..samples] 55 | a = Math.random(); b = 2*Math.random()-0.5 56 | c = 1-a; d = 1-b; easing = bezier(a,b,c,d) 57 | sym = (x)-> 1 - easing(1-x) 58 | x = i/samples 59 | expect(easing(x)).toBeCloseTo sym(x) 60 | 61 | describe 'toStr method ->', -> 62 | it 'should return params, the function was called with', -> 63 | expect(bezier(0,1,0,1).toStr()).toBe 'bezier(0,1,0,1)' 64 | 65 | 66 | describe 'arguments parsing ->', -> 67 | it 'should error if no arguments', -> 68 | spyOn mojs.h, 'error' 69 | expect(bezier()).toBe undefined 70 | expect(mojs.h.error).toHaveBeenCalled() 71 | it 'should error if 1 argument', -> 72 | spyOn mojs.h, 'error' 73 | expect(bezier(1)).toBe undefined 74 | expect(mojs.h.error).toHaveBeenCalled() 75 | it 'should error if 2 arguments', -> 76 | spyOn mojs.h, 'error' 77 | expect(bezier(2, 1)).toBe undefined 78 | expect(mojs.h.error).toHaveBeenCalled() 79 | it 'should error if 3 arguments', -> 80 | spyOn mojs.h, 'error' 81 | expect(bezier(2, 1, 3)).toBe undefined 82 | expect(mojs.h.error).toHaveBeenCalled() 83 | it 'should error if 3 arguments', -> 84 | spyOn mojs.h, 'error' 85 | expect(bezier(2, 1, 3)).toBe undefined 86 | expect(mojs.h.error).toHaveBeenCalled() 87 | it 'should error if string argument', -> 88 | spyOn mojs.h, 'error' 89 | expect(bezier(2, 1, 3, 'a')).toBe undefined 90 | expect(mojs.h.error).toHaveBeenCalled() 91 | it 'should error if NaN argument', -> 92 | spyOn mojs.h, 'error' 93 | expect(bezier(2, 1, 3, NaN)).toBe undefined 94 | expect(mojs.h.error).toHaveBeenCalled() 95 | it 'should error if Infinity argument', -> 96 | spyOn mojs.h, 'error' 97 | expect(bezier(2, 1, 3, Infinity)).toBe undefined 98 | expect(mojs.h.error).toHaveBeenCalled() 99 | it 'should error if Infinity argument', -> 100 | spyOn mojs.h, 'error' 101 | expect(bezier(2, 1, 3, Infinity)).toBe undefined 102 | expect(mojs.h.error).toHaveBeenCalled() 103 | it 'should error if x < 0', -> 104 | spyOn mojs.h, 'error' 105 | expect(bezier(0.5, 0.5, -5, 0.5)).toBe undefined 106 | expect(mojs.h.error).toHaveBeenCalled() 107 | it 'should error if x < 0 #2', -> 108 | spyOn mojs.h, 'error' 109 | expect(bezier(-2, 0.5, 1, 0.5)).toBe undefined 110 | expect(mojs.h.error).toHaveBeenCalled() 111 | it 'should error if x < 0 #3', -> 112 | spyOn mojs.h, 'error' 113 | expect(bezier(-Math.random()-0.000001, 0.5, 0.5, 0.5)).toBe undefined 114 | expect(mojs.h.error).toHaveBeenCalled() 115 | it 'should error if x > 1', -> 116 | spyOn mojs.h, 'error' 117 | expect(bezier(0.5, 0.5, 5, 0.5)).toBe undefined 118 | expect(mojs.h.error).toHaveBeenCalled() 119 | it 'should error if x > 1 #2', -> 120 | spyOn mojs.h, 'error' 121 | expect(bezier(2, 0.5, 1, 0.5)).toBe undefined 122 | expect(mojs.h.error).toHaveBeenCalled() 123 | it 'should error if x > 1 #3', -> 124 | spyOn mojs.h, 'error' 125 | expect(bezier(0.5, 0.5, 1.000001+Math.random(), 0.5)).toBe undefined 126 | expect(mojs.h.error).toHaveBeenCalled() 127 | -------------------------------------------------------------------------------- /spec/mix.coffee: -------------------------------------------------------------------------------- 1 | mix = mojs.easing.mix 2 | 3 | describe 'mix ->', -> 4 | it 'should return new function', -> 5 | res = mix({to: .5, value: 4}) 6 | expect(typeof res).toBe 'function' 7 | 8 | describe 'returned function ->', -> 9 | it 'should return value on the progress', -> 10 | res = mix({to: .5, value: 4}, {to: .7, value: 3}) 11 | expect(res(.4)).toBe 4 12 | expect(res(.6)).toBe 3 13 | it 'should evaluate function if passed', -> 14 | res = mix({to: .5, value: (p)-> return 2*p }, {to: .7, value: 3}) 15 | expect(res(.4)).toBe 2*.4 16 | expect(res(.6)).toBe 3 17 | it 'should return 1 if not defined', -> 18 | res = mix({to: .5, value: (p)-> return 2*p }) 19 | expect(res(.6)).toBe 1 20 | it 'should parse easing', -> 21 | res = mix({to: .5, value: 'cubic.in' }, {to: .7, value: 'cubic.out' }) 22 | expect(res(.3)).toBe mojs.easing.cubic.in .3 23 | it 'should work with one value', -> 24 | res = mix({to: .5, value: 'cubic.in' }) 25 | expect(res(.3)).toBe mojs.easing.cubic.in .3 26 | it 'should work with array value', -> 27 | res = mix({to: .5, value: [0.42, 0, 1, 1] }) 28 | expect(res(.3)).toBe mojs.easing.ease.in .3 29 | -------------------------------------------------------------------------------- /spec/mix.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | var mix; 3 | 4 | mix = mojs.easing.mix; 5 | 6 | describe('mix ->', function() { 7 | it('should return new function', function() { 8 | var res; 9 | res = mix({ 10 | to: .5, 11 | value: 4 12 | }); 13 | return expect(typeof res).toBe('function'); 14 | }); 15 | return describe('returned function ->', function() { 16 | it('should return value on the progress', function() { 17 | var res; 18 | res = mix({ 19 | to: .5, 20 | value: 4 21 | }, { 22 | to: .7, 23 | value: 3 24 | }); 25 | expect(res(.4)).toBe(4); 26 | return expect(res(.6)).toBe(3); 27 | }); 28 | it('should evaluate function if passed', function() { 29 | var res; 30 | res = mix({ 31 | to: .5, 32 | value: function(p) { 33 | return 2 * p; 34 | } 35 | }, { 36 | to: .7, 37 | value: 3 38 | }); 39 | expect(res(.4)).toBe(2 * .4); 40 | return expect(res(.6)).toBe(3); 41 | }); 42 | it('should return 1 if not defined', function() { 43 | var res; 44 | res = mix({ 45 | to: .5, 46 | value: function(p) { 47 | return 2 * p; 48 | } 49 | }); 50 | return expect(res(.6)).toBe(1); 51 | }); 52 | it('should parse easing', function() { 53 | var res; 54 | res = mix({ 55 | to: .5, 56 | value: 'cubic.in' 57 | }, { 58 | to: .7, 59 | value: 'cubic.out' 60 | }); 61 | return expect(res(.3)).toBe(mojs.easing.cubic["in"](.3)); 62 | }); 63 | it('should work with one value', function() { 64 | var res; 65 | res = mix({ 66 | to: .5, 67 | value: 'cubic.in' 68 | }); 69 | return expect(res(.3)).toBe(mojs.easing.cubic["in"](.3)); 70 | }); 71 | return it('should work with array value', function() { 72 | var res; 73 | res = mix({ 74 | to: .5, 75 | value: [0.42, 0, 1, 1] 76 | }); 77 | return expect(res(.3)).toBe(mojs.easing.ease["in"](.3)); 78 | }); 79 | }); 80 | }); 81 | 82 | }).call(this); 83 | -------------------------------------------------------------------------------- /spec/mojs.coffee: -------------------------------------------------------------------------------- 1 | describe 'mojs ->', -> 2 | it 'should have revision', -> 3 | expect(typeof mojs.revision).toBe 'string' 4 | it 'should have isDebug = true', -> 5 | expect(mojs.isDebug).toBe true 6 | it 'should have helpers defined', -> 7 | expect(mojs.helpers).toBeDefined() 8 | it 'should expose helpers to h variable', -> 9 | expect(mojs.h).toBe mojs.helpers 10 | it 'should expose h.delta mojs', -> 11 | expect(mojs.delta).toBe mojs.helpers.delta 12 | it 'should expose shapesMap.addShape mojs', -> 13 | expect(mojs.addShape).toBe mojs.shapesMap.addShape 14 | it 'should expose shapesMap.customShape mojs', -> 15 | expect(mojs.CustomShape).toBe mojs.shapesMap.custom 16 | it 'should have Burst', -> 17 | expect(mojs.Burst).toBeDefined() 18 | it 'should have Shape', -> 19 | expect(mojs.Shape).toBeDefined() 20 | it 'should have Transit alias', -> 21 | expect(mojs.Transit).toBe mojs.Shape 22 | it 'should have Html', -> 23 | expect(mojs.Html).toBeDefined() 24 | it 'should have ShapeSwirl', -> 25 | expect(mojs.ShapeSwirl).toBeDefined() 26 | it 'should have Swirl alias', -> 27 | expect(mojs.Swirl).toBe mojs.ShapeSwirl 28 | it 'should have stagger', -> 29 | expect(mojs.stagger).toBeDefined() 30 | it 'should have Spriter', -> 31 | expect(mojs.Spriter).toBeDefined() 32 | it 'should have MotionPath', -> 33 | expect(mojs.MotionPath).toBeDefined() 34 | it 'should have Timeline', -> 35 | expect(mojs.Timeline).toBeDefined() 36 | it 'should have Tween', -> 37 | expect(mojs.Tween).toBeDefined() 38 | it 'should have Tweenable', -> 39 | expect(mojs.Tweenable).toBeDefined() 40 | it 'should have Thenable', -> 41 | expect(mojs.Thenable).toBeDefined() 42 | it 'should have Tunable', -> 43 | expect(mojs.Tunable).toBeDefined() 44 | it 'should have Module', -> 45 | expect(mojs.Module).toBeDefined() 46 | it 'should have tweener', -> 47 | expect(mojs.tweener).toBeDefined() 48 | it 'should have easing', -> 49 | expect(mojs.easing).toBeDefined() 50 | it 'should have shapesMap', -> 51 | expect(mojs.shapesMap).toBeDefined() 52 | 53 | it 'should have _pool', -> 54 | expect(typeof mojs._pool).toBe 'object' 55 | expect(mojs._pool).toBe mojs._pool 56 | it 'should have delta', -> 57 | expect(mojs._pool.Delta).toBeDefined() 58 | it 'should have deltas', -> 59 | expect(mojs._pool.Deltas).toBeDefined() 60 | -------------------------------------------------------------------------------- /spec/mojs.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | describe('mojs ->', function() { 3 | it('should have revision', function() { 4 | return expect(typeof mojs.revision).toBe('string'); 5 | }); 6 | it('should have isDebug = true', function() { 7 | return expect(mojs.isDebug).toBe(true); 8 | }); 9 | it('should have helpers defined', function() { 10 | return expect(mojs.helpers).toBeDefined(); 11 | }); 12 | it('should expose helpers to h variable', function() { 13 | return expect(mojs.h).toBe(mojs.helpers); 14 | }); 15 | it('should expose h.delta mojs', function() { 16 | return expect(mojs.delta).toBe(mojs.helpers.delta); 17 | }); 18 | it('should expose shapesMap.addShape mojs', function() { 19 | return expect(mojs.addShape).toBe(mojs.shapesMap.addShape); 20 | }); 21 | it('should expose shapesMap.customShape mojs', function() { 22 | return expect(mojs.CustomShape).toBe(mojs.shapesMap.custom); 23 | }); 24 | it('should have Burst', function() { 25 | return expect(mojs.Burst).toBeDefined(); 26 | }); 27 | it('should have Shape', function() { 28 | return expect(mojs.Shape).toBeDefined(); 29 | }); 30 | it('should have Transit alias', function() { 31 | return expect(mojs.Transit).toBe(mojs.Shape); 32 | }); 33 | it('should have Html', function() { 34 | return expect(mojs.Html).toBeDefined(); 35 | }); 36 | it('should have ShapeSwirl', function() { 37 | return expect(mojs.ShapeSwirl).toBeDefined(); 38 | }); 39 | it('should have Swirl alias', function() { 40 | return expect(mojs.Swirl).toBe(mojs.ShapeSwirl); 41 | }); 42 | it('should have stagger', function() { 43 | return expect(mojs.stagger).toBeDefined(); 44 | }); 45 | it('should have Spriter', function() { 46 | return expect(mojs.Spriter).toBeDefined(); 47 | }); 48 | it('should have MotionPath', function() { 49 | return expect(mojs.MotionPath).toBeDefined(); 50 | }); 51 | it('should have Timeline', function() { 52 | return expect(mojs.Timeline).toBeDefined(); 53 | }); 54 | it('should have Tween', function() { 55 | return expect(mojs.Tween).toBeDefined(); 56 | }); 57 | it('should have Tweenable', function() { 58 | return expect(mojs.Tweenable).toBeDefined(); 59 | }); 60 | it('should have Thenable', function() { 61 | return expect(mojs.Thenable).toBeDefined(); 62 | }); 63 | it('should have Tunable', function() { 64 | return expect(mojs.Tunable).toBeDefined(); 65 | }); 66 | it('should have Module', function() { 67 | return expect(mojs.Module).toBeDefined(); 68 | }); 69 | it('should have tweener', function() { 70 | return expect(mojs.tweener).toBeDefined(); 71 | }); 72 | it('should have easing', function() { 73 | return expect(mojs.easing).toBeDefined(); 74 | }); 75 | it('should have shapesMap', function() { 76 | return expect(mojs.shapesMap).toBeDefined(); 77 | }); 78 | it('should have _pool', function() { 79 | expect(typeof mojs._pool).toBe('object'); 80 | return expect(mojs._pool).toBe(mojs._pool); 81 | }); 82 | it('should have delta', function() { 83 | return expect(mojs._pool.Delta).toBeDefined(); 84 | }); 85 | return it('should have deltas', function() { 86 | return expect(mojs._pool.Deltas).toBeDefined(); 87 | }); 88 | }); 89 | 90 | }).call(this); 91 | -------------------------------------------------------------------------------- /spec/shapes/circle.coffee: -------------------------------------------------------------------------------- 1 | Circle = mojs.shapesMap.getShape('circle') 2 | Bit = mojs.shapesMap.getShape('bit') 3 | ns = 'http://www.w3.org/2000/svg' 4 | svg = document.createElementNS?(ns, "svg") 5 | circle = new Circle ctx: svg 6 | 7 | describe 'Circle ->', -> 8 | it 'should extend Bit', -> 9 | expect(circle instanceof Bit).toBe(true) 10 | describe '_draw method ->', -> 11 | it 'should add properties to el', -> 12 | # svg = document.createElementNS?(ns, "svg") 13 | circle = new Circle 14 | radius: 20 15 | radiusX: 40 16 | radiusY: 35 17 | y: 50 18 | width: 100 19 | height: 100 20 | circle._draw() 21 | rx = circle.el.getAttribute('rx') 22 | ry = circle.el.getAttribute('ry') 23 | cx = circle.el.getAttribute('cx') 24 | cy = circle.el.getAttribute('cy') 25 | expect(rx).toBe('40') 26 | expect(ry).toBe('35') 27 | expect(cx).toBe('50') 28 | expect(cy).toBe('50') 29 | 30 | it 'should fallback to radius', -> 31 | svg = document.createElementNS?(ns, "svg") 32 | circle = new Circle 33 | radius: 20 34 | radiusY: 35 35 | 36 | circle._draw() 37 | 38 | rx = circle.el.getAttribute('rx') 39 | ry = circle.el.getAttribute('ry') 40 | expect(rx).toBe('20') 41 | expect(ry).toBe('35') 42 | 43 | it 'should call super method', -> 44 | svg = document.createElementNS?(ns, "svg") 45 | circle = new Circle ctx: svg 46 | spyOn(Circle.__super__, '_draw') 47 | circle._draw() 48 | expect(Circle.__super__._draw).toHaveBeenCalled() 49 | 50 | describe 'getLength method', -> 51 | it 'should calculate total length of the path', -> 52 | radius = 100 53 | bit = new Circle 54 | ctx: document.createElementNS ns, 'svg' 55 | radius: radius 56 | expect(bit._getLength()).toBe 2*Math.PI*radius 57 | 58 | it 'should calculate total length of the with different radiusX/Y', -> 59 | radiusX = 100 60 | radiusY = 50 61 | bit = new Circle 62 | ctx: document.createElementNS ns, 'svg' 63 | radiusX: radiusX 64 | radiusY: radiusY 65 | sqrt = Math.sqrt (radiusX*radiusX + radiusY*radiusY)/2 66 | expect(bit._getLength()).toBe 2*Math.PI*sqrt 67 | 68 | -------------------------------------------------------------------------------- /spec/shapes/circle.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | var Bit, Circle, circle, ns, svg; 3 | 4 | Circle = mojs.shapesMap.getShape('circle'); 5 | 6 | Bit = mojs.shapesMap.getShape('bit'); 7 | 8 | ns = 'http://www.w3.org/2000/svg'; 9 | 10 | svg = typeof document.createElementNS === "function" ? document.createElementNS(ns, "svg") : void 0; 11 | 12 | circle = new Circle({ 13 | ctx: svg 14 | }); 15 | 16 | describe('Circle ->', function() { 17 | it('should extend Bit', function() { 18 | return expect(circle instanceof Bit).toBe(true); 19 | }); 20 | describe('_draw method ->', function() { 21 | it('should add properties to el', function() { 22 | var cx, cy, rx, ry; 23 | circle = new Circle({ 24 | radius: 20, 25 | radiusX: 40, 26 | radiusY: 35, 27 | y: 50, 28 | width: 100, 29 | height: 100 30 | }); 31 | circle._draw(); 32 | rx = circle.el.getAttribute('rx'); 33 | ry = circle.el.getAttribute('ry'); 34 | cx = circle.el.getAttribute('cx'); 35 | cy = circle.el.getAttribute('cy'); 36 | expect(rx).toBe('40'); 37 | expect(ry).toBe('35'); 38 | expect(cx).toBe('50'); 39 | return expect(cy).toBe('50'); 40 | }); 41 | it('should fallback to radius', function() { 42 | var rx, ry; 43 | svg = typeof document.createElementNS === "function" ? document.createElementNS(ns, "svg") : void 0; 44 | circle = new Circle({ 45 | radius: 20, 46 | radiusY: 35 47 | }); 48 | circle._draw(); 49 | rx = circle.el.getAttribute('rx'); 50 | ry = circle.el.getAttribute('ry'); 51 | expect(rx).toBe('20'); 52 | return expect(ry).toBe('35'); 53 | }); 54 | return it('should call super method', function() { 55 | svg = typeof document.createElementNS === "function" ? document.createElementNS(ns, "svg") : void 0; 56 | circle = new Circle({ 57 | ctx: svg 58 | }); 59 | spyOn(Circle.__super__, '_draw'); 60 | circle._draw(); 61 | return expect(Circle.__super__._draw).toHaveBeenCalled(); 62 | }); 63 | }); 64 | return describe('getLength method', function() { 65 | it('should calculate total length of the path', function() { 66 | var bit, radius; 67 | radius = 100; 68 | bit = new Circle({ 69 | ctx: document.createElementNS(ns, 'svg'), 70 | radius: radius 71 | }); 72 | return expect(bit._getLength()).toBe(2 * Math.PI * radius); 73 | }); 74 | return it('should calculate total length of the with different radiusX/Y', function() { 75 | var bit, radiusX, radiusY, sqrt; 76 | radiusX = 100; 77 | radiusY = 50; 78 | bit = new Circle({ 79 | ctx: document.createElementNS(ns, 'svg'), 80 | radiusX: radiusX, 81 | radiusY: radiusY 82 | }); 83 | sqrt = Math.sqrt((radiusX * radiusX + radiusY * radiusY) / 2); 84 | return expect(bit._getLength()).toBe(2 * Math.PI * sqrt); 85 | }); 86 | }); 87 | }); 88 | 89 | }).call(this); 90 | -------------------------------------------------------------------------------- /spec/shapes/cross.coffee: -------------------------------------------------------------------------------- 1 | Cross = mojs.shapesMap.getShape('cross') 2 | Bit = mojs.shapesMap.getShape('bit') 3 | ns = 'http://www.w3.org/2000/svg' 4 | svg = document.createElementNS?(ns, "svg") 5 | cross = new Cross ctx: svg 6 | 7 | describe 'Cross ->', -> 8 | it 'should extend Bit', -> 9 | expect(cross instanceof Bit).toBe(true) 10 | 11 | describe '_declareDefaults method ->', -> 12 | it 'should call super', -> 13 | spyOn Bit::, '_declareDefaults' 14 | cross._declareDefaults() 15 | expect(Bit::_declareDefaults).toHaveBeenCalled() 16 | 17 | it 'should set `shape` to `path`', -> 18 | expect(cross._defaults.tag).toBe 'path' 19 | 20 | describe '_draw method ->', -> 21 | it 'should add properties to el', -> 22 | # svg = document.createElementNS?(ns, "svg") 23 | cross = new Cross radius: 20, width: 40, height: 40 24 | cross._draw() 25 | d = cross.el.getAttribute('d') 26 | isD = d is 'M0,20 L40,20 M20,0 L20,40' 27 | isIE9D = d is 'M 0 20 L 40 20 M 20 0 L 20 40' 28 | expect(isD or isIE9D).toBe true 29 | it 'should work with radiusX and fallback to radius', -> 30 | # svg = document.createElementNS?(ns, "svg") 31 | cross = new Cross 32 | # ctx: svg 33 | radius: 20 34 | radiusX: 40 35 | 36 | cross._draw() 37 | 38 | d = cross.el.getAttribute('d') 39 | isD = d is 'M-40,0 L40,0 M0,-20 L0,20' 40 | isIE9D = d is 'M -40 0 L 40 0 M 0 -20 L 0 20' 41 | expect(isD or isIE9D).toBe true 42 | it 'should work with radiusY and fallback to radius', -> 43 | # svg = document.createElementNS?(ns, "svg") 44 | cross = new Cross 45 | # ctx: svg 46 | radius: 20 47 | radiusY: 40 48 | 49 | cross._draw() 50 | 51 | d = cross.el.getAttribute('d') 52 | isD = d is 'M-20,0 L20,0 M0,-40 L0,40' 53 | isIE9D = d is 'M -20 0 L 20 0 M 0 -40 L 0 40' 54 | expect(isD or isIE9D).toBe true 55 | it 'should call super method', -> 56 | # svg = document.createElementNS?(ns, "svg") 57 | cross = new Cross 58 | spyOn(Cross.__super__, '_draw') 59 | cross._draw() 60 | expect(Cross.__super__._draw).toHaveBeenCalled() 61 | 62 | it 'should not set `d` attribute if nothing changed', -> 63 | cross = new Cross 64 | radius: 20 65 | points: 10 66 | cross._draw() 67 | spyOn cross.el, 'setAttribute' 68 | cross._draw() 69 | expect( cross.el.setAttribute ).not.toHaveBeenCalled() 70 | 71 | it 'should set `d` attribute if `radiusX` changed', -> 72 | cross = new Cross 73 | radius: 20 74 | points: 10 75 | cross._draw() 76 | spyOn cross.el, 'setAttribute' 77 | cross._props.radiusX = 30 78 | cross._draw() 79 | expect( cross.el.setAttribute ).toHaveBeenCalled() 80 | 81 | it 'should set `d` attribute if `radiusY` changed', -> 82 | cross = new Cross 83 | radius: 20 84 | points: 10 85 | cross._draw() 86 | spyOn cross.el, 'setAttribute' 87 | cross._props.radiusY = 30 88 | cross._draw() 89 | expect( cross.el.setAttribute ).toHaveBeenCalled() 90 | 91 | describe '_getLength method', -> 92 | it 'should calculate total length of the path', -> 93 | bit = new Cross 94 | ctx: document.createElementNS ns, 'svg' 95 | radius: 100 96 | expect(bit._getLength()).toBe 400 97 | 98 | it 'should calculate total length of the with different radiusX/Y', -> 99 | bit = new Cross 100 | ctx: document.createElementNS ns, 'svg' 101 | radiusX: 100 102 | radiusY: 50 103 | expect(bit._getLength()).toBe 300 104 | -------------------------------------------------------------------------------- /spec/shapes/cross.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | var Bit, Cross, cross, ns, svg; 3 | 4 | Cross = mojs.shapesMap.getShape('cross'); 5 | 6 | Bit = mojs.shapesMap.getShape('bit'); 7 | 8 | ns = 'http://www.w3.org/2000/svg'; 9 | 10 | svg = typeof document.createElementNS === "function" ? document.createElementNS(ns, "svg") : void 0; 11 | 12 | cross = new Cross({ 13 | ctx: svg 14 | }); 15 | 16 | describe('Cross ->', function() { 17 | it('should extend Bit', function() { 18 | return expect(cross instanceof Bit).toBe(true); 19 | }); 20 | describe('_declareDefaults method ->', function() { 21 | it('should call super', function() { 22 | spyOn(Bit.prototype, '_declareDefaults'); 23 | cross._declareDefaults(); 24 | return expect(Bit.prototype._declareDefaults).toHaveBeenCalled(); 25 | }); 26 | return it('should set `shape` to `path`', function() { 27 | return expect(cross._defaults.tag).toBe('path'); 28 | }); 29 | }); 30 | describe('_draw method ->', function() { 31 | it('should add properties to el', function() { 32 | var d, isD, isIE9D; 33 | cross = new Cross({ 34 | radius: 20, 35 | width: 40, 36 | height: 40 37 | }); 38 | cross._draw(); 39 | d = cross.el.getAttribute('d'); 40 | isD = d === 'M0,20 L40,20 M20,0 L20,40'; 41 | isIE9D = d === 'M 0 20 L 40 20 M 20 0 L 20 40'; 42 | return expect(isD || isIE9D).toBe(true); 43 | }); 44 | it('should work with radiusX and fallback to radius', function() { 45 | var d, isD, isIE9D; 46 | cross = new Cross({ 47 | radius: 20, 48 | radiusX: 40 49 | }); 50 | cross._draw(); 51 | d = cross.el.getAttribute('d'); 52 | isD = d === 'M-40,0 L40,0 M0,-20 L0,20'; 53 | isIE9D = d === 'M -40 0 L 40 0 M 0 -20 L 0 20'; 54 | return expect(isD || isIE9D).toBe(true); 55 | }); 56 | it('should work with radiusY and fallback to radius', function() { 57 | var d, isD, isIE9D; 58 | cross = new Cross({ 59 | radius: 20, 60 | radiusY: 40 61 | }); 62 | cross._draw(); 63 | d = cross.el.getAttribute('d'); 64 | isD = d === 'M-20,0 L20,0 M0,-40 L0,40'; 65 | isIE9D = d === 'M -20 0 L 20 0 M 0 -40 L 0 40'; 66 | return expect(isD || isIE9D).toBe(true); 67 | }); 68 | it('should call super method', function() { 69 | cross = new Cross; 70 | spyOn(Cross.__super__, '_draw'); 71 | cross._draw(); 72 | return expect(Cross.__super__._draw).toHaveBeenCalled(); 73 | }); 74 | it('should not set `d` attribute if nothing changed', function() { 75 | cross = new Cross({ 76 | radius: 20, 77 | points: 10 78 | }); 79 | cross._draw(); 80 | spyOn(cross.el, 'setAttribute'); 81 | cross._draw(); 82 | return expect(cross.el.setAttribute).not.toHaveBeenCalled(); 83 | }); 84 | it('should set `d` attribute if `radiusX` changed', function() { 85 | cross = new Cross({ 86 | radius: 20, 87 | points: 10 88 | }); 89 | cross._draw(); 90 | spyOn(cross.el, 'setAttribute'); 91 | cross._props.radiusX = 30; 92 | cross._draw(); 93 | return expect(cross.el.setAttribute).toHaveBeenCalled(); 94 | }); 95 | return it('should set `d` attribute if `radiusY` changed', function() { 96 | cross = new Cross({ 97 | radius: 20, 98 | points: 10 99 | }); 100 | cross._draw(); 101 | spyOn(cross.el, 'setAttribute'); 102 | cross._props.radiusY = 30; 103 | cross._draw(); 104 | return expect(cross.el.setAttribute).toHaveBeenCalled(); 105 | }); 106 | }); 107 | return describe('_getLength method', function() { 108 | it('should calculate total length of the path', function() { 109 | var bit; 110 | bit = new Cross({ 111 | ctx: document.createElementNS(ns, 'svg'), 112 | radius: 100 113 | }); 114 | return expect(bit._getLength()).toBe(400); 115 | }); 116 | return it('should calculate total length of the with different radiusX/Y', function() { 117 | var bit; 118 | bit = new Cross({ 119 | ctx: document.createElementNS(ns, 'svg'), 120 | radiusX: 100, 121 | radiusY: 50 122 | }); 123 | return expect(bit._getLength()).toBe(300); 124 | }); 125 | }); 126 | }); 127 | 128 | }).call(this); 129 | -------------------------------------------------------------------------------- /spec/shapes/curve.coffee: -------------------------------------------------------------------------------- 1 | Curve = mojs.shapesMap.getShape('curve') 2 | Bit = mojs.shapesMap.getShape('bit') 3 | ns = 'http://www.w3.org/2000/svg' 4 | svg = document.createElementNS?(ns, "svg") 5 | curve = new Curve ctx: svg 6 | 7 | describe 'Curve ->', -> 8 | it 'should extend Bit', -> 9 | expect(curve instanceof Bit).toBe(true) 10 | 11 | describe '_declareDefaults method ->', -> 12 | it 'should call super', -> 13 | spyOn Bit::, '_declareDefaults' 14 | curve._declareDefaults() 15 | expect(Bit::_declareDefaults).toHaveBeenCalled() 16 | 17 | it 'should set `shape` to `path`', -> 18 | expect(curve._defaults.tag).toBe 'path' 19 | 20 | # nope 21 | # describe '_extendDefaults method', -> 22 | # it 'should call super', -> 23 | # spyOn Bit::, '_extendDefaults' 24 | # curve._extendDefaults() 25 | # expect(Bit::_extendDefaults).toHaveBeenCalled() 26 | # it '`radiusX` should fallback to `radius`', -> 27 | # curve = new Curve ctx: svg, radius: 20 28 | # curve._extendDefaults() 29 | # expect(curve._props.radiusX).toBe 20 30 | # it '`radiusX` should not fallback to `radius` if falsy', -> 31 | # curve = new Curve ctx: svg, radius: 20, radiusX: 0 32 | # curve._extendDefaults() 33 | # expect(curve._props.radiusX).toBe 0 34 | # it '`radiusY` should fallback to `radius`', -> 35 | # curve = new Curve ctx: svg, radius: 20 36 | # curve._extendDefaults() 37 | # expect(curve._props.radiusY).toBe 20 38 | # it '`radiusY` should not fallback to `radius` if falsy', -> 39 | # curve = new Curve ctx: svg, radius: 20, radiusY: 0 40 | # curve._extendDefaults() 41 | # expect(curve._props.radiusY).toBe 0 42 | 43 | describe '_draw method ->', -> 44 | it 'should call super', -> 45 | spyOn Bit::, '_draw' 46 | curve._draw() 47 | expect(Bit::_draw).toHaveBeenCalled() 48 | it 'should call `el.setAttribute` for `d` attribute ', -> 49 | radiusX = 20; radiusY = 30 50 | curve = new Curve ctx: svg, radiusX, radiusY 51 | curve._draw() 52 | 53 | p = curve._props 54 | 55 | radiusX = if p.radiusX? then p.radiusX else p.radius 56 | radiusY = if p.radiusY? then p.radiusY else p.radius 57 | 58 | x = p.width/2 59 | y = p.height/2 60 | 61 | x1 = x - radiusX 62 | x2 = x 63 | x3 = x + radiusX 64 | y1 = y - radiusY 65 | y2 = y 66 | y3 = y + radiusY 67 | 68 | d = curve.el.getAttribute('d') 69 | isD1 = d is "M#{x1} #{y} Q #{x2} #{ y - 2*radiusY } #{x3} #{y}" 70 | isD2 = d is "M #{x1} #{y} Q #{x2} #{ y - 2*radiusY } #{x3} #{y}" 71 | expect( isD1 or isD2 ).toBe true 72 | 73 | it 'should save `radiusX/radiusY/points` properties', -> 74 | radiusX = 20; radiusY = 30 75 | curve = new Curve ctx: svg, radiusX, radiusY 76 | curve._draw() 77 | 78 | p = curve._props 79 | 80 | radiusX = if p.radiusX? then p.radiusX else p.radius 81 | radiusY = if p.radiusY? then p.radiusY else p.radius 82 | 83 | x = 1*p.x 84 | y = 1*p.y 85 | 86 | x1 = x - radiusX 87 | x2 = x 88 | x3 = x + radiusX 89 | y1 = y - radiusY 90 | y2 = y 91 | y3 = y + radiusY 92 | 93 | expect( curve._prevRadiusX ).toBe radiusX 94 | expect( curve._prevRadiusY ).toBe radiusY 95 | expect( curve._prevPoints ).toBe p.points 96 | 97 | it 'should not set the `d` attribute if nothing changed', -> 98 | radiusX = 20; radiusY = 30 99 | curve = new Curve ctx: svg, radiusX, radiusY 100 | 101 | curve._draw() 102 | spyOn curve.el, 'setAttribute' 103 | curve._draw() 104 | 105 | expect( curve.el.setAttribute ) 106 | .not.toHaveBeenCalled() 107 | 108 | it 'should set `d` attribute if `radiusX` changed', -> 109 | curve = new Curve 110 | ctx: document.createElementNS?(ns, "svg") 111 | radius: 20 112 | points: 10 113 | curve._draw() 114 | spyOn curve.el, 'setAttribute' 115 | curve._props.radiusX = 30 116 | curve._draw() 117 | expect( curve.el.setAttribute ).toHaveBeenCalled() 118 | 119 | it 'should set `d` attribute if `radiusY` changed', -> 120 | curve = new Curve 121 | ctx: document.createElementNS?(ns, "svg") 122 | radius: 20 123 | points: 10 124 | curve._draw() 125 | spyOn curve.el, 'setAttribute' 126 | curve._props.radiusY = 30 127 | curve._draw() 128 | expect( curve.el.setAttribute ).toHaveBeenCalled() 129 | 130 | it 'should set `d` attribute if `points` changed', -> 131 | curve = new Curve 132 | ctx: document.createElementNS?(ns, "svg") 133 | radius: 20 134 | points: 10 135 | curve._draw() 136 | spyOn curve.el, 'setAttribute' 137 | curve._props.points = 30 138 | curve._draw() 139 | expect( curve.el.setAttribute ).toHaveBeenCalled() 140 | 141 | describe 'getLength method', -> 142 | it 'should calculate total length of the path', -> 143 | radiusX = 20; radiusY = 30 144 | curve = new Curve ctx: svg, radiusX, radiusY 145 | 146 | p = curve._props 147 | 148 | radiusX = if p.radiusX? then p.radiusX else p.radius 149 | radiusY = if p.radiusY? then p.radiusY else p.radius 150 | 151 | dRadius = radiusX + radiusY 152 | sqrt = Math.sqrt((3*radiusX + radiusY)*(radiusX + 3*radiusY)) 153 | len = .5 * Math.PI * ( 3*dRadius - sqrt ); 154 | 155 | expect( curve._getLength() ).toBe len 156 | 157 | -------------------------------------------------------------------------------- /spec/shapes/equal.coffee: -------------------------------------------------------------------------------- 1 | Equal = mojs.shapesMap.getShape('equal') 2 | Bit = mojs.shapesMap.getShape('bit') 3 | ns = 'http://www.w3.org/2000/svg' 4 | svg = document.createElementNS?(ns, "svg") 5 | 6 | describe 'Equal ->', -> 7 | it 'should extend Bit', -> 8 | equal = new Equal 9 | expect(equal instanceof Bit).toBe(true) 10 | 11 | describe '_declareDefaults method ->', -> 12 | it 'should call super', -> 13 | equal = new Equal 14 | spyOn Bit::, '_declareDefaults' 15 | equal._declareDefaults() 16 | expect(Bit::_declareDefaults).toHaveBeenCalled() 17 | 18 | it 'should set `shape` to `path`', -> 19 | equal = new Equal 20 | expect(equal._defaults.tag).toBe 'path' 21 | it 'should set `points` to `2`', -> 22 | equal = new Equal 23 | expect(equal._defaults.points).toBe 2 24 | 25 | describe '_draw method ->', -> 26 | it 'should define points', -> 27 | equal = new Equal radius: 20 28 | equal._draw() 29 | expect(equal.el.getAttribute('d')).toBeTruthy() 30 | it 'should not work with 0 points', -> 31 | equal = new Equal 32 | radius: 20 33 | points: 0 34 | expect(equal.el.getAttribute('points')).toBeFalsy() 35 | 36 | it 'should not set `d` attribute if nothing changed', -> 37 | equal = new Equal 38 | radius: 20 39 | points: 10 40 | equal._draw() 41 | spyOn equal.el, 'setAttribute' 42 | equal._draw() 43 | expect( equal.el.setAttribute ).not.toHaveBeenCalled() 44 | 45 | it 'should set `d` attribute if `radiusX` changed', -> 46 | equal = new Equal 47 | radius: 20 48 | points: 10 49 | equal._draw() 50 | spyOn equal.el, 'setAttribute' 51 | equal._props.radiusX = 30 52 | equal._draw() 53 | expect( equal.el.setAttribute ).toHaveBeenCalled() 54 | 55 | it 'should set `d` attribute if `radiusY` changed', -> 56 | equal = new Equal 57 | radius: 20 58 | points: 10 59 | equal._draw() 60 | spyOn equal.el, 'setAttribute' 61 | equal._props.radiusY = 30 62 | equal._draw() 63 | expect( equal.el.setAttribute ).toHaveBeenCalled() 64 | 65 | it 'should set `d` attribute if `points` changed', -> 66 | equal = new Equal 67 | radius: 20 68 | points: 10 69 | equal._draw() 70 | spyOn equal.el, 'setAttribute' 71 | equal._props.points = 30 72 | equal._draw() 73 | expect( equal.el.setAttribute ).toHaveBeenCalled() 74 | 75 | describe 'getLength method', -> 76 | it 'should calculate total length of the path', -> 77 | radius = 100 78 | bit = new Equal 79 | radius: radius 80 | expect(bit._getLength()).toBe 2*radius 81 | 82 | it 'should calculate total length of the with different radiusX/Y', -> 83 | radiusX = 100 84 | radiusY = 50 85 | bit = new Equal 86 | radiusX: radiusX 87 | radiusY: radiusY 88 | expect(bit._getLength()).toBe 2*radiusX 89 | -------------------------------------------------------------------------------- /spec/shapes/equal.coffee.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | - equal.coffee 6 | 7 | 8 | 9 | 10 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 |
24 | 25 | 26 | ../spec/shapes/equal.coffee 27 | 28 | 29 |
30 |
31 | 32 |
33 | 34 | 35 |
Equal = mojs.Equal 36 | Bit = mojs.Bit 37 | ns = 'http://www.w3.org/2000/svg' 38 | svg = document.createElementNS?(ns, "svg") 39 | 40 | describe 'Equal', -> 41 | it 'should extend Bit', -> 42 | equal = new Equal ctx: svg 43 | expect(equal instanceof Bit).toBe(true) 44 | it 'have type of path', -> 45 | equal = new Equal ctx: svg 46 | expect(equal.type).toBe 'path' 47 | it 'have ratio of 1.43', -> 48 | equal = new Equal ctx: svg 49 | expect(equal.ratio).toBe 1.43 50 | describe 'methods ->', -> 51 | describe 'draw method ->', -> 52 | it 'should define points', -> 53 | equal = new Equal 54 | ctx: document.createElementNS?(ns, "svg") 55 | radius: 20 56 | expect(equal.el.getAttribute('d')).toBeTruthy() 57 | it 'should not work with 0 points', -> 58 | equal = new Equal 59 | ctx: document.createElementNS?(ns, "svg") 60 | radius: 20 61 | points: 0 62 | expect(equal.el.getAttribute('points')).toBeFalsy() 63 | 64 | describe 'getLength method', -> 65 | it 'should calculate total length of the path', -> 66 | radius = 100 67 | bit = new Equal 68 | ctx: document.createElementNS ns, 'svg' 69 | radius: radius 70 | expect(bit.getLength()).toBe 2*radius 71 | 72 | it 'should calculate total length of the with different radiusX/Y', -> 73 | radiusX = 100 74 | radiusY = 50 75 | bit = new Equal 76 | ctx: document.createElementNS ns, 'svg' 77 | radiusX: radiusX 78 | radiusY: radiusY 79 | expect(bit.getLength()).toBe 2*radiusX 80 |
81 | 82 |
83 | 84 |
85 |
86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 96 | 97 | 98 | 99 | -------------------------------------------------------------------------------- /spec/shapes/equal.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | var Bit, Equal, ns, svg; 3 | 4 | Equal = mojs.shapesMap.getShape('equal'); 5 | 6 | Bit = mojs.shapesMap.getShape('bit'); 7 | 8 | ns = 'http://www.w3.org/2000/svg'; 9 | 10 | svg = typeof document.createElementNS === "function" ? document.createElementNS(ns, "svg") : void 0; 11 | 12 | describe('Equal ->', function() { 13 | it('should extend Bit', function() { 14 | var equal; 15 | equal = new Equal; 16 | return expect(equal instanceof Bit).toBe(true); 17 | }); 18 | describe('_declareDefaults method ->', function() { 19 | it('should call super', function() { 20 | var equal; 21 | equal = new Equal; 22 | spyOn(Bit.prototype, '_declareDefaults'); 23 | equal._declareDefaults(); 24 | return expect(Bit.prototype._declareDefaults).toHaveBeenCalled(); 25 | }); 26 | it('should set `shape` to `path`', function() { 27 | var equal; 28 | equal = new Equal; 29 | return expect(equal._defaults.tag).toBe('path'); 30 | }); 31 | return it('should set `points` to `2`', function() { 32 | var equal; 33 | equal = new Equal; 34 | return expect(equal._defaults.points).toBe(2); 35 | }); 36 | }); 37 | describe('_draw method ->', function() { 38 | it('should define points', function() { 39 | var equal; 40 | equal = new Equal({ 41 | radius: 20 42 | }); 43 | equal._draw(); 44 | return expect(equal.el.getAttribute('d')).toBeTruthy(); 45 | }); 46 | it('should not work with 0 points', function() { 47 | var equal; 48 | equal = new Equal({ 49 | radius: 20, 50 | points: 0 51 | }); 52 | return expect(equal.el.getAttribute('points')).toBeFalsy(); 53 | }); 54 | it('should not set `d` attribute if nothing changed', function() { 55 | var equal; 56 | equal = new Equal({ 57 | radius: 20, 58 | points: 10 59 | }); 60 | equal._draw(); 61 | spyOn(equal.el, 'setAttribute'); 62 | equal._draw(); 63 | return expect(equal.el.setAttribute).not.toHaveBeenCalled(); 64 | }); 65 | it('should set `d` attribute if `radiusX` changed', function() { 66 | var equal; 67 | equal = new Equal({ 68 | radius: 20, 69 | points: 10 70 | }); 71 | equal._draw(); 72 | spyOn(equal.el, 'setAttribute'); 73 | equal._props.radiusX = 30; 74 | equal._draw(); 75 | return expect(equal.el.setAttribute).toHaveBeenCalled(); 76 | }); 77 | it('should set `d` attribute if `radiusY` changed', function() { 78 | var equal; 79 | equal = new Equal({ 80 | radius: 20, 81 | points: 10 82 | }); 83 | equal._draw(); 84 | spyOn(equal.el, 'setAttribute'); 85 | equal._props.radiusY = 30; 86 | equal._draw(); 87 | return expect(equal.el.setAttribute).toHaveBeenCalled(); 88 | }); 89 | return it('should set `d` attribute if `points` changed', function() { 90 | var equal; 91 | equal = new Equal({ 92 | radius: 20, 93 | points: 10 94 | }); 95 | equal._draw(); 96 | spyOn(equal.el, 'setAttribute'); 97 | equal._props.points = 30; 98 | equal._draw(); 99 | return expect(equal.el.setAttribute).toHaveBeenCalled(); 100 | }); 101 | }); 102 | return describe('getLength method', function() { 103 | it('should calculate total length of the path', function() { 104 | var bit, radius; 105 | radius = 100; 106 | bit = new Equal({ 107 | radius: radius 108 | }); 109 | return expect(bit._getLength()).toBe(2 * radius); 110 | }); 111 | return it('should calculate total length of the with different radiusX/Y', function() { 112 | var bit, radiusX, radiusY; 113 | radiusX = 100; 114 | radiusY = 50; 115 | bit = new Equal({ 116 | radiusX: radiusX, 117 | radiusY: radiusY 118 | }); 119 | return expect(bit._getLength()).toBe(2 * radiusX); 120 | }); 121 | }); 122 | }); 123 | 124 | }).call(this); 125 | -------------------------------------------------------------------------------- /spec/shapes/line.coffee: -------------------------------------------------------------------------------- 1 | Line = mojs.shapesMap.getShape('line') 2 | Bit = mojs.shapesMap.getShape('bit') 3 | ns = 'http://www.w3.org/2000/svg' 4 | svg = document.createElementNS?(ns, "svg") 5 | line = new Line ctx: svg 6 | 7 | describe 'Line', -> 8 | it 'should extend Bit', -> 9 | expect(line instanceof Bit).toBe(true) 10 | it 'should add itself to context', -> 11 | line = new Line ctx: svg 12 | expect(svg.firstChild).toBeDefined() 13 | 14 | describe '_declareDefaults method ->', -> 15 | it 'should call super', -> 16 | line = new Line ctx: svg 17 | spyOn Bit::, '_declareDefaults' 18 | line._declareDefaults() 19 | expect(Bit::_declareDefaults).toHaveBeenCalled() 20 | 21 | it 'should set `shape` to `path`', -> 22 | line = new Line ctx: svg 23 | expect(line._defaults.tag).toBe 'line' 24 | 25 | describe 'methods ->', -> 26 | describe 'draw method ->', -> 27 | it 'should add properties to el', -> 28 | svg = document.createElementNS?(ns, "svg") 29 | line = new Line 30 | ctx: svg 31 | radius: 20 32 | 33 | line._draw() 34 | attr1 = parseInt line.el.getAttribute('x1'), 10 35 | attr2 = parseInt line.el.getAttribute('x2'), 10 36 | delta = attr2 - attr1 37 | expect(delta).toBe(40) 38 | it 'should work with radiusX', -> 39 | # svg = document.createElementNS?(ns, "svg") 40 | line = new Line 41 | # ctx: svg 42 | radius: 20 43 | radiusX: 40 44 | line._draw() 45 | attr1 = parseInt line.el.getAttribute('x1'), 10 46 | attr2 = parseInt line.el.getAttribute('x2'), 10 47 | delta = attr2 - attr1 48 | expect(delta).toBe(80) 49 | it 'should call super method', -> 50 | svg = document.createElementNS?(ns, "svg") 51 | line = new Line ctx: svg 52 | spyOn(Line.__super__, '_draw') 53 | line._draw() 54 | expect(Line.__super__._draw).toHaveBeenCalled() 55 | 56 | describe 'getLength method', -> 57 | it 'should calculate total length of the path', -> 58 | bit = new Line 59 | ctx: document.createElementNS ns, 'svg' 60 | radius: 100 61 | expect(bit._getLength()).toBe 200 62 | 63 | 64 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /spec/shapes/line.coffee.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | - line.coffee 6 | 7 | 8 | 9 | 10 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 |
24 | 25 | 26 | ../spec/shapes/line.coffee 27 | 28 | 29 |
30 |
31 | 32 |
33 | 34 | 35 |
Line = mojs.Line 36 | Bit = mojs.Bit 37 | ns = 'http://www.w3.org/2000/svg' 38 | svg = document.createElementNS?(ns, "svg") 39 | line = new Line ctx: svg 40 | 41 | describe 'Line', -> 42 | it 'should extend Bit', -> 43 | expect(line instanceof Bit).toBe(true) 44 | it 'should add itself to context', -> 45 | line = new Line ctx: svg 46 | expect(svg.firstChild).toBeDefined() 47 | 48 | describe 'methods ->', -> 49 | describe 'draw method ->', -> 50 | it 'should add properties to el', -> 51 | svg = document.createElementNS?(ns, "svg") 52 | line = new Line 53 | ctx: svg 54 | radius: 20 55 | attr1 = parseInt line.el.getAttribute('x1'), 10 56 | attr2 = parseInt line.el.getAttribute('x2'), 10 57 | delta = attr2 - attr1 58 | expect(delta).toBe(40) 59 | it 'should work with radiusX', -> 60 | svg = document.createElementNS?(ns, "svg") 61 | line = new Line 62 | ctx: svg 63 | radius: 20 64 | radiusX: 40 65 | attr1 = parseInt line.el.getAttribute('x1'), 10 66 | attr2 = parseInt line.el.getAttribute('x2'), 10 67 | delta = attr2 - attr1 68 | expect(delta).toBe(80) 69 | it 'should call super method', -> 70 | svg = document.createElementNS?(ns, "svg") 71 | line = new Line ctx: svg 72 | spyOn(Line.__super__, 'draw') 73 | line.draw() 74 | expect(Line.__super__.draw).toHaveBeenCalled() 75 | 76 | describe 'getLength method', -> 77 | it 'should calculate total length of the path', -> 78 | bit = new Line 79 | ctx: document.createElementNS ns, 'svg' 80 | radius: 100 81 | expect(bit.getLength()).toBe 200 82 | 83 | 84 | 85 | 86 | 87 |
88 | 89 |
90 | 91 |
92 |
93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 103 | 104 | 105 | 106 | -------------------------------------------------------------------------------- /spec/shapes/line.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | var Bit, Line, line, ns, svg; 3 | 4 | Line = mojs.shapesMap.getShape('line'); 5 | 6 | Bit = mojs.shapesMap.getShape('bit'); 7 | 8 | ns = 'http://www.w3.org/2000/svg'; 9 | 10 | svg = typeof document.createElementNS === "function" ? document.createElementNS(ns, "svg") : void 0; 11 | 12 | line = new Line({ 13 | ctx: svg 14 | }); 15 | 16 | describe('Line', function() { 17 | it('should extend Bit', function() { 18 | return expect(line instanceof Bit).toBe(true); 19 | }); 20 | it('should add itself to context', function() { 21 | line = new Line({ 22 | ctx: svg 23 | }); 24 | return expect(svg.firstChild).toBeDefined(); 25 | }); 26 | describe('_declareDefaults method ->', function() { 27 | it('should call super', function() { 28 | line = new Line({ 29 | ctx: svg 30 | }); 31 | spyOn(Bit.prototype, '_declareDefaults'); 32 | line._declareDefaults(); 33 | return expect(Bit.prototype._declareDefaults).toHaveBeenCalled(); 34 | }); 35 | return it('should set `shape` to `path`', function() { 36 | line = new Line({ 37 | ctx: svg 38 | }); 39 | return expect(line._defaults.tag).toBe('line'); 40 | }); 41 | }); 42 | describe('methods ->', function() { 43 | return describe('draw method ->', function() { 44 | it('should add properties to el', function() { 45 | var attr1, attr2, delta; 46 | svg = typeof document.createElementNS === "function" ? document.createElementNS(ns, "svg") : void 0; 47 | line = new Line({ 48 | ctx: svg, 49 | radius: 20 50 | }); 51 | line._draw(); 52 | attr1 = parseInt(line.el.getAttribute('x1'), 10); 53 | attr2 = parseInt(line.el.getAttribute('x2'), 10); 54 | delta = attr2 - attr1; 55 | return expect(delta).toBe(40); 56 | }); 57 | it('should work with radiusX', function() { 58 | var attr1, attr2, delta; 59 | line = new Line({ 60 | radius: 20, 61 | radiusX: 40 62 | }); 63 | line._draw(); 64 | attr1 = parseInt(line.el.getAttribute('x1'), 10); 65 | attr2 = parseInt(line.el.getAttribute('x2'), 10); 66 | delta = attr2 - attr1; 67 | return expect(delta).toBe(80); 68 | }); 69 | return it('should call super method', function() { 70 | svg = typeof document.createElementNS === "function" ? document.createElementNS(ns, "svg") : void 0; 71 | line = new Line({ 72 | ctx: svg 73 | }); 74 | spyOn(Line.__super__, '_draw'); 75 | line._draw(); 76 | return expect(Line.__super__._draw).toHaveBeenCalled(); 77 | }); 78 | }); 79 | }); 80 | return describe('getLength method', function() { 81 | return it('should calculate total length of the path', function() { 82 | var bit; 83 | bit = new Line({ 84 | ctx: document.createElementNS(ns, 'svg'), 85 | radius: 100 86 | }); 87 | return expect(bit._getLength()).toBe(200); 88 | }); 89 | }); 90 | }); 91 | 92 | }).call(this); 93 | -------------------------------------------------------------------------------- /spec/shapes/polygon.coffee: -------------------------------------------------------------------------------- 1 | Polygon = mojs.shapesMap.getShape('polygon') 2 | Bit = mojs.shapesMap.getShape('bit') 3 | ns = 'http://www.w3.org/2000/svg' 4 | svg = document.createElementNS?(ns, "svg") 5 | 6 | describe 'Polygon ->', -> 7 | it 'should extend Bit', -> 8 | polygon = new Polygon 9 | expect(polygon instanceof Bit).toBe true 10 | 11 | describe '_declareDefaults method ->', -> 12 | it 'should call super', -> 13 | polygon = new Polygon 14 | spyOn Bit::, '_declareDefaults' 15 | polygon._declareDefaults() 16 | expect(Bit::._declareDefaults).toHaveBeenCalled() 17 | it 'should defaults for `tag` and `points`', -> 18 | polygon = new Polygon 19 | expect(polygon._defaults.tag).toBe 'path' 20 | expect(polygon._defaults.points).toBe 3 21 | 22 | it 'should have points prop', -> 23 | tri = new Polygon 24 | expect(tri._props.points).toBe 3 25 | it 'should have recieve points prop', -> 26 | tri = new Polygon points: 8 27 | expect(tri._props.points).toBe 8 28 | it 'should calculate _radialPoints', -> 29 | tri = new Polygon 30 | tri._draw() 31 | expect(tri._radialPoints).toBeDefined() 32 | expect(tri._radialPoints.length).toBe tri._props.points 33 | 34 | describe '_draw method ->', -> 35 | it 'should add properties to el', -> 36 | tri = new Polygon 37 | radius: 20 38 | 39 | tri._draw() 40 | d = tri.el.getAttribute('d') 41 | d2 = 'M0.0000,-20.0000 L17.3205,10.0000 L-17.3205,10.0000 z' 42 | isD = d is d2 43 | isIE9D = d is 'M 0 -20 L 17.3205 10 L -17.3205 10 Z' 44 | expect(isD or isIE9D).toBe true 45 | 46 | it 'should work with radiusX and fallback to radius', -> 47 | # svg = document.createElementNS?(ns, "svg") 48 | tri = new Polygon 49 | # ctx: svg 50 | radius: 20 51 | radiusX: 40 52 | tri._draw() 53 | d = tri.el.getAttribute('d') 54 | d2 = 'M0.0000,-20.0000 L34.6410,10.0000 L-34.6410,10.0000 z' 55 | isD = d is d2 56 | isIE9D = d is 'M 0 -20 L 34.641 10 L -34.641 10 Z' 57 | expect(isD or isIE9D).toBe true 58 | 59 | it 'should work with radiusY and fallback to radius', -> 60 | # svg = document.createElementNS?(ns, "svg") 61 | tri = new Polygon 62 | # ctx: svg 63 | radius: 20 64 | radiusY: 40 65 | 66 | tri._draw() 67 | 68 | d = tri.el.getAttribute('d') 69 | d2 = 'M0.0000,-40.0000 L17.3205,20.0000 L-17.3205,20.0000 z' 70 | isD = d is d2 71 | isIE9D = d is 'M 0 -40 L 17.3205 20 L -17.3205 20 Z' 72 | expect(isD or isIE9D).toBe true 73 | it 'should call super method', -> 74 | polygon = new Polygon 75 | spyOn(Polygon.__super__, '_draw') 76 | polygon._draw() 77 | expect(Polygon.__super__._draw).toHaveBeenCalled() 78 | 79 | it 'should not set `d` attribute if nothing changed', -> 80 | polygon = new Polygon 81 | radius: 20 82 | points: 10 83 | polygon._draw() 84 | spyOn polygon.el, 'setAttribute' 85 | polygon._draw() 86 | expect( polygon.el.setAttribute ).not.toHaveBeenCalled() 87 | 88 | it 'should set `d` attribute if `radiusX` changed', -> 89 | polygon = new Polygon 90 | radius: 20 91 | points: 10 92 | polygon._draw() 93 | spyOn polygon.el, 'setAttribute' 94 | polygon._props.radiusX = 30 95 | polygon._draw() 96 | expect( polygon.el.setAttribute ).toHaveBeenCalled() 97 | 98 | it 'should set `d` attribute if `radiusY` changed', -> 99 | polygon = new Polygon 100 | radius: 20 101 | points: 10 102 | polygon._draw() 103 | spyOn polygon.el, 'setAttribute' 104 | polygon._props.radiusY = 30 105 | polygon._draw() 106 | expect( polygon.el.setAttribute ).toHaveBeenCalled() 107 | 108 | it 'should set `d` attribute if `points` changed', -> 109 | polygon = new Polygon 110 | radius: 20 111 | points: 10 112 | polygon._draw() 113 | spyOn polygon.el, 'setAttribute' 114 | polygon._props.points = 30 115 | polygon._draw() 116 | expect( polygon.el.setAttribute ).toHaveBeenCalled() 117 | 118 | describe 'getLength method', -> 119 | it 'should calculate sum between all points', -> 120 | polygon = new Polygon radiusX: 100, radiusY: 200 121 | polygon._draw() 122 | 123 | expect( polygon._getLength() ) 124 | .toBe polygon._getPointsPerimiter( polygon._radialPoints ) 125 | 126 | describe '_pointsDelta method ->', -> 127 | it 'should return distance between points', -> 128 | tri = new Polygon 129 | 130 | for i in [ 0...50 ] 131 | point1 = { x: 20*i, y: 120+i } 132 | point2 = { x: 200+i, y: -120*i } 133 | dx = Math.abs( point1.x - point2.x ) 134 | dy = Math.abs( point1.y - point2.y ) 135 | expect( tri._pointsDelta( point1, point2 ) ) 136 | .toBe Math.sqrt( dx*dx + dy*dy ) 137 | 138 | describe '_getPointsPerimiter method', -> 139 | it 'should calculate sum between all points', -> 140 | tri = new Polygon 141 | # create points for test 142 | points = [] 143 | for i in [1...20] 144 | points.push( { x: 100*Math.random(), y: 100*Math.random() } ) 145 | 146 | sum = 0 147 | for i in [1...points.length] 148 | sum += tri._pointsDelta points[i-1], points[i] 149 | 150 | sum += tri._pointsDelta points[0], mojs.h.getLastItem points 151 | 152 | expect( tri._getPointsPerimiter( points ) ).toBe sum 153 | -------------------------------------------------------------------------------- /spec/shapes/rect.coffee: -------------------------------------------------------------------------------- 1 | Rect = mojs.shapesMap.getShape('rect') 2 | Bit = mojs.shapesMap.getShape('bit') 3 | ns = 'http://www.w3.org/2000/svg' 4 | svg = document.createElementNS?(ns, "svg") 5 | rect = new Rect ctx: svg 6 | 7 | describe 'Rect ->', -> 8 | it 'should extend Bit', -> 9 | expect(rect instanceof Bit).toBe(true) 10 | describe '_declareDefaults method ->', -> 11 | it 'should call super', -> 12 | rect = new Rect 13 | spyOn Bit::, '_declareDefaults' 14 | rect._declareDefaults() 15 | expect(Bit::_declareDefaults).toHaveBeenCalled() 16 | it 'should set `shape` to `path`', -> 17 | rect = new Rect 18 | expect(rect._defaults.tag).toBe 'rect' 19 | it 'should set `rx` to `0`', -> 20 | rect = new Rect 21 | expect(rect._defaults.rx).toBe 0 22 | it 'should set `ry` to `0`', -> 23 | rect = new Rect 24 | expect(rect._defaults.ry).toBe 0 25 | describe '_draw method ->', -> 26 | it 'should add properties to el', -> 27 | # svg = document.createElementNS?(ns, "svg") 28 | rect = new Rect 29 | radius: 20, 30 | x: 50, y: 100, 31 | rx: 10, ry: 20 32 | width: 50, height: 50 33 | 34 | rect._draw() 35 | 36 | width = rect.el.getAttribute 'width' 37 | height = rect.el.getAttribute 'height' 38 | x = rect.el.getAttribute 'x' 39 | y = rect.el.getAttribute 'y' 40 | rx = rect.el.getAttribute 'rx' 41 | ry = rect.el.getAttribute 'ry' 42 | expect(width) .toBe '40' 43 | expect(height).toBe '40' 44 | expect(x).toBe '5' 45 | expect(y).toBe '5' 46 | expect(rx).toBe '10px' 47 | expect(ry).toBe '20px' 48 | it 'should work with radiusX/radiusY props', -> 49 | # svg = document.createElementNS?(ns, "svg") 50 | rect = new Rect 51 | radiusY: 50, radiusX: 40, width: 100, height: 200 52 | rect._draw() 53 | width = rect.el.getAttribute 'width' 54 | height = rect.el.getAttribute 'height' 55 | expect(width) .toBe '80' 56 | expect(height).toBe '100' 57 | x = rect.el.getAttribute 'x' 58 | y = rect.el.getAttribute 'y' 59 | expect(x).toBe '10' 60 | expect(y).toBe '50' 61 | it 'should call super method', -> 62 | svg = document.createElementNS?(ns, "svg") 63 | rect = new Rect 64 | spyOn(Bit::, '_draw') 65 | rect._draw() 66 | expect(Bit::_draw).toHaveBeenCalled() 67 | describe 'getLength method', -> 68 | it 'should calculate total length of the path', -> 69 | radius = 100 70 | bit = new Rect 71 | ctx: document.createElementNS ns, 'svg' 72 | radius: radius 73 | expect(bit._getLength()).toBe 800 74 | 75 | it 'should calculate total length of the with different radiusX/Y', -> 76 | radiusX = 100 77 | radiusY = 50 78 | bit = new Rect 79 | ctx: document.createElementNS ns, 'svg' 80 | radiusX: radiusX 81 | radiusY: radiusY 82 | expect(bit._getLength()).toBe 2*(2*radiusX + 2*radiusY) 83 | 84 | 85 | 86 | 87 | 88 | 89 | -------------------------------------------------------------------------------- /spec/shapes/rect.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | var Bit, Rect, ns, rect, svg; 3 | 4 | Rect = mojs.shapesMap.getShape('rect'); 5 | 6 | Bit = mojs.shapesMap.getShape('bit'); 7 | 8 | ns = 'http://www.w3.org/2000/svg'; 9 | 10 | svg = typeof document.createElementNS === "function" ? document.createElementNS(ns, "svg") : void 0; 11 | 12 | rect = new Rect({ 13 | ctx: svg 14 | }); 15 | 16 | describe('Rect ->', function() { 17 | it('should extend Bit', function() { 18 | return expect(rect instanceof Bit).toBe(true); 19 | }); 20 | describe('_declareDefaults method ->', function() { 21 | it('should call super', function() { 22 | rect = new Rect; 23 | spyOn(Bit.prototype, '_declareDefaults'); 24 | rect._declareDefaults(); 25 | return expect(Bit.prototype._declareDefaults).toHaveBeenCalled(); 26 | }); 27 | it('should set `shape` to `path`', function() { 28 | rect = new Rect; 29 | return expect(rect._defaults.tag).toBe('rect'); 30 | }); 31 | it('should set `rx` to `0`', function() { 32 | rect = new Rect; 33 | return expect(rect._defaults.rx).toBe(0); 34 | }); 35 | return it('should set `ry` to `0`', function() { 36 | rect = new Rect; 37 | return expect(rect._defaults.ry).toBe(0); 38 | }); 39 | }); 40 | describe('_draw method ->', function() { 41 | it('should add properties to el', function() { 42 | var height, rx, ry, width, x, y; 43 | rect = new Rect({ 44 | radius: 20, 45 | x: 50, 46 | y: 100, 47 | rx: 10, 48 | ry: 20, 49 | width: 50, 50 | height: 50 51 | }); 52 | rect._draw(); 53 | width = rect.el.getAttribute('width'); 54 | height = rect.el.getAttribute('height'); 55 | x = rect.el.getAttribute('x'); 56 | y = rect.el.getAttribute('y'); 57 | rx = rect.el.getAttribute('rx'); 58 | ry = rect.el.getAttribute('ry'); 59 | expect(width).toBe('40'); 60 | expect(height).toBe('40'); 61 | expect(x).toBe('5'); 62 | expect(y).toBe('5'); 63 | expect(rx).toBe('10px'); 64 | return expect(ry).toBe('20px'); 65 | }); 66 | it('should work with radiusX/radiusY props', function() { 67 | var height, width, x, y; 68 | rect = new Rect({ 69 | radiusY: 50, 70 | radiusX: 40, 71 | width: 100, 72 | height: 200 73 | }); 74 | rect._draw(); 75 | width = rect.el.getAttribute('width'); 76 | height = rect.el.getAttribute('height'); 77 | expect(width).toBe('80'); 78 | expect(height).toBe('100'); 79 | x = rect.el.getAttribute('x'); 80 | y = rect.el.getAttribute('y'); 81 | expect(x).toBe('10'); 82 | return expect(y).toBe('50'); 83 | }); 84 | return it('should call super method', function() { 85 | svg = typeof document.createElementNS === "function" ? document.createElementNS(ns, "svg") : void 0; 86 | rect = new Rect; 87 | spyOn(Bit.prototype, '_draw'); 88 | rect._draw(); 89 | return expect(Bit.prototype._draw).toHaveBeenCalled(); 90 | }); 91 | }); 92 | return describe('getLength method', function() { 93 | it('should calculate total length of the path', function() { 94 | var bit, radius; 95 | radius = 100; 96 | bit = new Rect({ 97 | ctx: document.createElementNS(ns, 'svg'), 98 | radius: radius 99 | }); 100 | return expect(bit._getLength()).toBe(800); 101 | }); 102 | return it('should calculate total length of the with different radiusX/Y', function() { 103 | var bit, radiusX, radiusY; 104 | radiusX = 100; 105 | radiusY = 50; 106 | bit = new Rect({ 107 | ctx: document.createElementNS(ns, 'svg'), 108 | radiusX: radiusX, 109 | radiusY: radiusY 110 | }); 111 | return expect(bit._getLength()).toBe(2 * (2 * radiusX + 2 * radiusY)); 112 | }); 113 | }); 114 | }); 115 | 116 | }).call(this); 117 | -------------------------------------------------------------------------------- /spec/shapes/shapesMap.coffee: -------------------------------------------------------------------------------- 1 | shapesMap = window.mojs.shapesMap 2 | h = mojs.h 3 | 4 | describe 'shapesMap ->', -> 5 | it 'should have all available shapes', -> 6 | expect(shapesMap.bit) .toBeDefined() 7 | expect(shapesMap.custom) .toBeDefined() 8 | expect(shapesMap.circle) .toBeDefined() 9 | expect(shapesMap.line) .toBeDefined() 10 | expect(shapesMap.zigzag) .toBeDefined() 11 | expect(shapesMap.rect) .toBeDefined() 12 | expect(shapesMap.polygon) .toBeDefined() 13 | expect(shapesMap.cross) .toBeDefined() 14 | expect(shapesMap.equal) .toBeDefined() 15 | expect(shapesMap.curve) .toBeDefined() 16 | describe 'getShape', -> 17 | it 'should get bit by string', -> 18 | expect(shapesMap.getShape('bit')).toBeDefined() 19 | it 'should console.error if bit was not found', -> 20 | spyOn h, 'error' 21 | shapesMap.getShape('') 22 | expect(h.error).toHaveBeenCalled() 23 | describe 'addShape method ->', -> 24 | it 'should add shape to the shape map', -> 25 | Custom = shapesMap.getShape('custom') 26 | class Shape extends Custom 27 | shapesMap.addShape 'shape', Shape 28 | expect( shapesMap.getShape( 'shape' ) ).toBe Shape 29 | 30 | it 'should be hard bound to shapesMap', -> 31 | Module = {} 32 | mojs.addShape 'shape', Module 33 | expect( shapesMap.getShape( 'shape' ) ).toBe Module 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /spec/shapes/shapesMap.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | var h, shapesMap, 3 | __hasProp = {}.hasOwnProperty, 4 | __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }; 5 | 6 | shapesMap = window.mojs.shapesMap; 7 | 8 | h = mojs.h; 9 | 10 | describe('shapesMap ->', function() { 11 | it('should have all available shapes', function() { 12 | expect(shapesMap.bit).toBeDefined(); 13 | expect(shapesMap.custom).toBeDefined(); 14 | expect(shapesMap.circle).toBeDefined(); 15 | expect(shapesMap.line).toBeDefined(); 16 | expect(shapesMap.zigzag).toBeDefined(); 17 | expect(shapesMap.rect).toBeDefined(); 18 | expect(shapesMap.polygon).toBeDefined(); 19 | expect(shapesMap.cross).toBeDefined(); 20 | expect(shapesMap.equal).toBeDefined(); 21 | return expect(shapesMap.curve).toBeDefined(); 22 | }); 23 | describe('getShape', function() { 24 | it('should get bit by string', function() { 25 | return expect(shapesMap.getShape('bit')).toBeDefined(); 26 | }); 27 | return it('should console.error if bit was not found', function() { 28 | spyOn(h, 'error'); 29 | shapesMap.getShape(''); 30 | return expect(h.error).toHaveBeenCalled(); 31 | }); 32 | }); 33 | return describe('addShape method ->', function() { 34 | it('should add shape to the shape map', function() { 35 | var Custom, Shape; 36 | Custom = shapesMap.getShape('custom'); 37 | Shape = (function(_super) { 38 | __extends(Shape, _super); 39 | 40 | function Shape() { 41 | return Shape.__super__.constructor.apply(this, arguments); 42 | } 43 | 44 | return Shape; 45 | 46 | })(Custom); 47 | shapesMap.addShape('shape', Shape); 48 | return expect(shapesMap.getShape('shape')).toBe(Shape); 49 | }); 50 | return it('should be hard bound to shapesMap', function() { 51 | var Module; 52 | Module = {}; 53 | mojs.addShape('shape', Module); 54 | return expect(shapesMap.getShape('shape')).toBe(Module); 55 | }); 56 | }); 57 | }); 58 | 59 | }).call(this); 60 | -------------------------------------------------------------------------------- /spec/shapes/zigzag.coffee: -------------------------------------------------------------------------------- 1 | Zigzag = mojs.shapesMap.getShape('zigzag') 2 | Bit = mojs.shapesMap.getShape('bit') 3 | ns = 'http://www.w3.org/2000/svg' 4 | svg = document.createElementNS?(ns, 'svg') 5 | document.body.appendChild svg 6 | 7 | describe 'Zigzag ->', -> 8 | it 'should extend Bit', -> 9 | line = new Zigzag ctx: svg 10 | expect(line instanceof Bit).toBe(true) 11 | it 'should add itself to context', -> 12 | line = new Zigzag ctx: svg 13 | expect(svg.firstChild).toBeDefined() 14 | describe '_declareDefaults method ->', -> 15 | it 'should call super', -> 16 | zigzag = new Zigzag 17 | spyOn Bit::, '_declareDefaults' 18 | zigzag._declareDefaults() 19 | expect(Bit::_declareDefaults).toHaveBeenCalled() 20 | it 'should set `shape` to `path`', -> 21 | zigzag = new Zigzag 22 | expect(zigzag._defaults.tag).toBe 'path' 23 | it 'should set `points` to `3`', -> 24 | zigzag = new Zigzag 25 | expect(zigzag._defaults.points).toBe 3 26 | describe 'methods ->', -> 27 | describe '_draw method ->', -> 28 | it 'should add properties to el', -> 29 | zigzag = new Zigzag 30 | # ctx: document.createElementNS?(ns, "svg") 31 | radius: 20 32 | it 'should define points', -> 33 | zigzag = new Zigzag 34 | # ctx: document.createElementNS?(ns, "svg") 35 | radius: 20 36 | zigzag._draw() 37 | expect(zigzag.el.getAttribute('d')).toBeTruthy() 38 | it 'should not work with 0 points', -> 39 | zigzag = new Zigzag 40 | # ctx: document.createElementNS?(ns, "svg") 41 | radius: 20 42 | points: 0 43 | expect(zigzag.el.getAttribute('d')).toBeFalsy() 44 | it 'should calculate path length', -> 45 | zigzag = new Zigzag 46 | # ctx: document.createElementNS?(ns, "svg") 47 | radius: 20 48 | points: 10 49 | zigzag._draw() 50 | expect(zigzag._length).toBeCloseTo 184.390, 2 51 | 52 | it 'should set `d` attribute', -> 53 | zigzag = new Zigzag 54 | # ctx: document.createElementNS?(ns, "svg") 55 | radius: 20 56 | points: 10 57 | zigzag._draw() 58 | 59 | p = zigzag._props 60 | 61 | radiusX = if p.radiusX? then p.radiusX else p.radius 62 | radiusY = if p.radiusY? then p.radiusY else p.radius 63 | 64 | x = p.width/2 65 | y = p.height/2 66 | 67 | currentX = x-radiusX 68 | currentY = y 69 | stepX = (2*radiusX) / (p.points-1) 70 | yFlip = -1 71 | 72 | delta = Math.sqrt(stepX*stepX + radiusY*radiusY) 73 | length = -delta 74 | 75 | points = "M#{currentX}, #{y} " 76 | for i in [0...p.points] 77 | points += "L#{currentX}, #{currentY} " 78 | currentX += stepX 79 | length += delta 80 | 81 | currentY = if yFlip is -1 then y-radiusY else y 82 | yFlip = -yFlip 83 | 84 | 85 | isP1 = zigzag.el.getAttribute( 'd' ) is points 86 | isP2 = zigzag.el.getAttribute( 'd' ) is 'M -20 0 L -20 0 L -15.5556 -20 L -11.1111 0 L -6.66667 -20 L -2.22222 0 L 2.22222 -20 L 6.66667 0 L 11.1111 -20 L 15.5556 0 L 20 -20' 87 | 88 | expect( isP1 or isP2 ).toBe true 89 | expect( zigzag._prevRadiusX ).toBe radiusX 90 | expect( zigzag._prevRadiusY ).toBe radiusY 91 | expect( zigzag._prevPoints ).toBe p.points 92 | 93 | it 'should not set `d` attribute if nothing changed', -> 94 | zigzag = new Zigzag 95 | ctx: document.createElementNS?(ns, "svg") 96 | radius: 20 97 | points: 10 98 | zigzag._draw() 99 | spyOn zigzag.el, 'setAttribute' 100 | zigzag._draw() 101 | expect( zigzag.el.setAttribute ).not.toHaveBeenCalled() 102 | 103 | it 'should set `d` attribute if `radiusX` changed', -> 104 | zigzag = new Zigzag 105 | ctx: document.createElementNS?(ns, "svg") 106 | radius: 20 107 | points: 10 108 | zigzag._draw() 109 | spyOn zigzag.el, 'setAttribute' 110 | zigzag._props.radiusX = 30 111 | zigzag._draw() 112 | expect( zigzag.el.setAttribute ).toHaveBeenCalled() 113 | 114 | it 'should set `d` attribute if `radiusY` changed', -> 115 | zigzag = new Zigzag 116 | ctx: document.createElementNS?(ns, "svg") 117 | radius: 20 118 | points: 10 119 | zigzag._draw() 120 | spyOn zigzag.el, 'setAttribute' 121 | zigzag._props.radiusY = 30 122 | zigzag._draw() 123 | expect( zigzag.el.setAttribute ).toHaveBeenCalled() 124 | 125 | it 'should set `d` attribute if `points` changed', -> 126 | zigzag = new Zigzag 127 | ctx: document.createElementNS?(ns, "svg") 128 | radius: 20 129 | points: 10 130 | zigzag._draw() 131 | spyOn zigzag.el, 'setAttribute' 132 | zigzag._props.points = 30 133 | zigzag._draw() 134 | expect( zigzag.el.setAttribute ).toHaveBeenCalled() 135 | 136 | describe 'getLength method ->', -> 137 | it 'should calculate total length of the path', -> 138 | bit = new Zigzag 139 | ctx: document.createElementNS ns, 'svg' 140 | radiusX: 100 141 | radiusY: 550 142 | 143 | bit._draw() 144 | 145 | expect( bit._getLength() ).toBe bit._length 146 | -------------------------------------------------------------------------------- /spec/tween/pool.coffee: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mojs-contrib/mojs/856bf4c4cfdcabab3a78c18dfd419d429b78817e/spec/tween/pool.coffee -------------------------------------------------------------------------------- /spec/tween/pool.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 3 | 4 | }).call(this); 5 | -------------------------------------------------------------------------------- /todo.md: -------------------------------------------------------------------------------- 1 | ## TODO 2 | --- 3 | 4 | - check imediately return onRepeatComplete issue 5 | - check timeline + tween again 6 | - check callbacks order - onProgress should fire first 7 | 8 | - cover COVER labels at tweener 9 | - reafctor 10 | - remove clamp for setProgress on tween 11 | 12 | - fix coverage 13 | - release 14 | - docs 15 | - tuts 16 | 17 | - easing 18 | - add spring handler 19 | - add meta balls 20 | - add layers 21 | - add states 22 | - add shaker 23 | - add line trails 24 | - radial springs 25 | 26 | # FIXES 27 | - cover polyfills 28 | - foreign context coordinates in burst 29 | - add onChainUpdate to all the bits 30 | - transit 31 | - fix timeline tween options history transform 32 | - fallback to x/y if translate isnt supported (transit should set x/y on svg) 33 | - burst 34 | - add deltas for swirl swirlFrequency and swirlSize 35 | - then implementation 36 | 37 | # UNDER CONSIDERATION 38 | - add text tricks 39 | - add backgrounds 40 | - add camera glare 41 | 42 | 43 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | var path = require('path'); 2 | var webpack = require('webpack'); 3 | 4 | module.exports = { 5 | watch: true, 6 | context: __dirname + "/", 7 | entry: [ 8 | __dirname + '/js/mojs.babel.js' 9 | ], 10 | module: { 11 | loaders: [ 12 | { test: /\.(babel.js)$/, 13 | exclude: /node_modules/, 14 | loader: 'babel-loader', 15 | query: { 16 | presets: [ 'es2015-loose', 'babel-preset-stage-2' ], 17 | plugins: [ 'transform-runtime' ] 18 | } 19 | }, 20 | 21 | { test: /\.coffee$/, exclude: /node_modules/, loaders: ['coffee-loader?bare=true'] }, 22 | { test: /\.cjsx$/, loaders: ['coffee', 'cjsx']}, 23 | { test: /\.jade$/, loaders: ['jade'] }, 24 | { test: /\.styl$/, loader: 'style-loader!css-loader!autoprefixer-loader?browsers=last 4 version!stylus-loader?paths=node_modules/' }, 25 | { test: /\.html$/, loader: 'raw-loader' }, 26 | { 27 | test: /\.(eot|woff|ttf|svg|png|jpg|wav|mp3)$/, 28 | loader: 'url-loader?limit=30000&name=[name]-[hash].[ext]', 29 | // paths: ['/app/css/i/'] 30 | } 31 | ] 32 | }, 33 | output: { 34 | path: __dirname + '/build', 35 | filename: 'mo.js', 36 | publicPath: 'build/', 37 | library: 'mojs', 38 | libraryTarget: 'umd', 39 | umdNamedDefine: true 40 | }, 41 | plugins: [], 42 | resolve: { 43 | root: [ path.resolve('./'), path.resolve('./css/') ], 44 | moduleDirectories: ['node_modules'], 45 | target: 'node', 46 | extensions: [ 47 | '', '.js', '.es6', '.babel.js', '.coffee', 48 | '.styl', 49 | ] 50 | } 51 | }; 52 | --------------------------------------------------------------------------------