{"id":17133280,"url":"https://github.com/gilbox/spark-scroll","last_synced_at":"2025-04-10T04:34:27.774Z","repository":{"id":21233449,"uuid":"24548744","full_name":"gilbox/spark-scroll","owner":"gilbox","description":"scroll-based actions and animations for angularjs","archived":false,"fork":false,"pushed_at":"2016-12-03T18:37:13.000Z","size":1659,"stargazers_count":81,"open_issues_count":3,"forks_count":16,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-03-24T05:44:51.529Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"HTML","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/gilbox.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2014-09-28T03:07:57.000Z","updated_at":"2023-01-28T21:43:52.000Z","dependencies_parsed_at":"2022-09-09T04:50:32.326Z","dependency_job_id":null,"html_url":"https://github.com/gilbox/spark-scroll","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gilbox%2Fspark-scroll","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gilbox%2Fspark-scroll/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gilbox%2Fspark-scroll/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gilbox%2Fspark-scroll/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/gilbox","download_url":"https://codeload.github.com/gilbox/spark-scroll/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248157958,"owners_count":21057097,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-10-14T19:41:49.463Z","updated_at":"2025-04-10T04:34:27.743Z","avatar_url":"https://github.com/gilbox.png","language":"HTML","funding_links":[],"categories":[],"sub_categories":[],"readme":"spark-scroll\n============\n\n[![Gitter](https://badges.gitter.im/Join Chat.svg)](https://gitter.im/gilbox/spark-scroll?utm_source=badge\u0026utm_medium=badge\u0026utm_campaign=pr-badge\u0026utm_content=badge)\n\nangular directive for scroll-based actions. An element with the `spark-scroll` \ndirective can be associated with any number of scroll positions. At each scroll \nposition you can trigger any number of actions. An action can call any function \non the scope, add or remove a class to the element, and/or broadcast an event \nfrom the element's scope . You can also easily define your own actions if the \nbuilt-in actions aren't enough.\n\nAdditionally, the `spark-scroll-animate` directive includes all of the features\nof `spark-scroll` plus the ability to animate CSS properties in sync \nwith the browser scroll position.\n\nFor animating SVG elements with `spark-scroll-animate`, use [the GSAP plugin](#custom-animation-engine-tweenmax-gsap)\n\n**[spark-scroll demo](http://gilbox.github.io/spark-scroll/demo/)** \n\nIn the Wild\n===========\n\nspark-scroll is being used in the following websites. ([add your website to this list](https://github.com/gilbox/spark-scroll/edit/master/README.md))\n\n- [iSeatz](http://iseatz.com/)\n\nCompatibility\n=============\n\n- IE9+ (IE8 untested), all modern browsers\n- Angular 1.x\n\nInspiration\n===========\n\n`spark-scroll` is heavily inspired by and borrows many ideas from\n[ScrollMagic](http://janpaepke.github.io/ScrollMagic/), including [the `spark-scroll` demo](http://gilbox.github.io/spark-scroll/demo/) \nwhich attempts to re-create [ScrollMagic's demo page](http://janpaepke.github.io/ScrollMagic).\n\n\nDownload\n========\n\n- via bower: `bower install spark-scroll`\n- [via github](https://github.com/gilbox/spark-scroll/archive/master.zip)\n\nSetup\n=====\n\n### HTML\n``` html\n\u003cscript src=\"js/angular.js\"\u003e\u003c/script\u003e\n\u003cscript src=\"js/lodash.js\"\u003e\u003c/script\u003e\n\u003cscript src=\"js/spark-scroll.js\"\u003e\u003c/script\u003e\n\u003cscript src=\"js/animation-frame.js\"\u003e\u003c/script\u003e\n```\n\n**To use `spark-scroll-animate` requires additional JavaScript files, see the [Dependencies](#dependencies)**\n    \n### JavaScript\n``` javascript\nangular.module('app', ['gilbox.sparkScroll']);\n```\n\nDependencies\n=====\n\nDepending on the desired features, spark-scroll won't try to load in any libraries \nthat don't get utilized.\n\n- `spark-scroll`\n    - lodash or underscore\n    - [animation-frame](https://github.com/kof/animation-frame)\n- `spark-scroll-animate`\n    - lodash or underscore\n    - [animation-frame](https://github.com/kof/animation-frame)\n    - Your choice of:\n        - [shifty](https://github.com/jeremyckahn/shifty), and [Rekapi](http://rekapi.com/)\n        - or any [custom animation engine](#custom-animation-engine)\n\nUsage\n=====\n\n## Basic Example (spark-scroll)\n``` html\n\u003ch1 spark-scroll=\"{\n    120:{ onUp: myUpFn },\n    121:{ 'onUp,onDown': myUpDownFn, 'downAddClass,upRemoveClass': 'my-class my-other-class' },\n    140:{ 'upBroadcast': 'event-to-broadcast', 'upEmit,downEmit': 'event-to-emit' }\n    }\"\u003e\n  This Title is Spark\n\u003c/h1\u003e\n```\n\n## Formula Example (spark-scroll)\n``` html\n\u003ch1 spark-scroll=\"{\n            topTop:{ onUp: myUpFn },\n    'topCenter-20':{ 'onUp,onDown': myUpDownFn, 'downAddClass,upRemoveClass': 'my-class my-other-class' },\n         topBottom:{ 'upBroadcast': 'event-to-broadcast', 'upEmit,downEmit': 'event-to-emit' }\n    }\"\u003e\n  This Title is Spark\n\u003c/h1\u003e\n```\n\n## Animated Example (spark-scroll-animate)\n``` html\n\u003ch1 spark-scroll-animate=\"{\n            topTop:{ color: '#f00', marginLeft: '50px' },\n         topBottom:{ color: '#000', marginLeft: '0px' }\n    }\"\u003e\n  This Title is Spark Animated\n\u003c/h1\u003e\n```\n\n## Animated Less-Basic Example with easing (spark-scroll-animate)\n``` html\n\u003ch1 spark-scroll-animate=\"{\n    ease:'easeOutQuad',\n    120:{opacity:'0'},\n    121:{opacity:'0.8', top:'151px', color:'#fff'},\n    140:{opacity:'1.0', top:'0px', color:'#444'}\n    }\"\u003e\n  This Title is Sparky\n\u003c/h1\u003e\n```\n \n## Animated Example with Override element-wide easing at a specific keyframe (spark-scroll-animate)\n``` html\n\u003ch1 spark-scroll-animate=\"{\n    ease:'easeOutQuad',\n    120:{opacity:'0'},\n    121:{opacity:'0.8', top:'151px', color:'#fff'},\n    140:{opacity:'1.0', top:'0px', color:'#444', ease: 'linear'}\n    }\"\u003e\n  This Title is Sparky\n\u003c/h1\u003e\n```\n \n## spark-scroll-callback: Callback on Scroll Event\n\nThis attribute expects a string value, that when evaluated in the element's scope should return a function reference.\nThe function reference will be called for every *frame* of scrolling. `spark-scroll` internally debounces scroll events \nso the callback will not necessarily be called on all native scroll events. Note that the concept of a *frame*\ncan be further affected by the [`spark-scroll-ease`](#spark-scroll-ease-scroll-easing) property.\n\nEvery time the function is called, it is provided one argument, `ratio` which is a decimal value\nbetween 0 and 1 representing the progress of scroll within the limits of the maximum and minimum \nscroll positions of the `spark-scroll` or `spark-scroll-animate` attributes. The simplest use of \n`spark-scroll-callback` would look something like this:\n``` html\n\u003ch1 spark-scroll-callback=\"myFunctionOnScope\"\n    spark-scroll=\"{ topBottom:0, topTop:0 }\"\u003e\n```\nWhen `spark-scroll` calls `myFunctionOnScope(ratio)`, the `ratio` is calculated based on the current scroll position,\nand the `topBottom` and `topTop` formulas.\n\nNote that in the preceding example instead of assigning an object to the keyframes, we simply\nassign `0`. However, if we wanted to use a callback while at the same time taking advantage of *action* and\n*animation* properties we could do something like this:\n``` html\n\u003ch1 spark-scroll-callback=\"myOtherFunctionOnScope\"\n    spark-scroll-animate=\"{\n            topTop:{ opacity: 0 },\n         topCenter:{ opacity: 1, 'downAddClass,upRemoveClass': 'my-class my-other-class' },\n         topBottom:{ 'upBroadcast': 'event-to-broadcast' }\n    }\"\u003e\n  This Title is Spark\n\u003c/h1\u003e\n```\nNote that in the preceding example, when `spark-scroll-animate` calls `myOtherFunctionOnScope(ratio)`, the `ratio` argument\nis calculated using the `topTop` and `topBottom` formulas because they are at the extremes of the \nkeyframe range for this element.\n\n\n## spark-scroll-bind-once: One-Time Binding\n \nIf you are using a version of angular (\u003c=1.3) which doesn't support one-time lazy binding\nyou can use the `spark-scroll-bind-once` attribute to achieve the same thing. Because `spark-scroll`\nwill create a rather expensive deep watch for the `spark-scroll(-animate)` attribute, it's a good idea to use \none-time binding whenever possible.\n\n\n## spark-scroll-ease: Scroll Easing\n\nAdd this attribute to ease the position of the scrollbar so jumps in scroll position will have smooth animation.\nThis may cause scrolling to feel laggy but animations will look smoother.\n \n## Built-in Actions\n\n    .constant 'sparkActionProps', {\n\n      # When the up, down fns are called, `this` is the current keyFrame object and `o` is the action object\n      # therefore @element and @scope refer to the current element and it's scope\n\n      # fn reference that is called when scrolled down past keyframe\n      'onDown':\n        down: (o)-\u003e o.val(@, 'onDown', o)\n\n      # fn reference that is called when scrolled up past keyframe\n      'onUp':\n        up: (o)-\u003e o.val(@, 'onUp', o)\n\n      # class(es) added when scrolled down past keyframe,\n      'downAddClass':\n        down: (o)-\u003e @element.addClass(o.val)\n\n      # class(es) added when scrolled up past keyframe,\n      'upAddClass':\n        up: (o)-\u003e @element.addClass(o.val)\n\n      # class(es) removed when scrolled down past keyframe\n      'downRemoveClass':\n        down: (o)-\u003e @element.removeClass(o.val)\n\n      # class(es) removed when scrolled up past keyframe\n      'upRemoveClass':\n        up: (o)-\u003e @element.removeClass(o.val)\n\n      # broadcasts an event when scrolled down past keyframe\n      'downBroadcast':\n        down: (o)-\u003e @scope.$broadcast(o.val, @)\n\n      # broadcasts an event when scrolled up past keyframe\n      'upBroadcast':\n        up: (o)-\u003e @scope.$broadcast(o.val, @)\n\n      # emits an event when scrolled down past keyframe\n      'downEmit':\n        down: (o)-\u003e @scope.$emit(o.val, @)\n\n      # emits an event when scrolled up past keyframe\n      'upEmit':\n        up: (o)-\u003e @scope.$emit(o.val, @)\n    }\n\n\n## Register a custom action\n``` javascript\napp.config(function(sparkActionProps) {\n\n    angular.extend(sparkActionProps, {\n\n        // Change the element's text when scrolling down past it\n        downText: {\n            down: function(o) {\n                this.element.text(o.val);\n            }\n        },\n\n        // Change the element's text when scrolling up past it\n        upText: {\n            up: function(o) {\n                this.element.text(o.val);\n            }\n        }\n\n    });\n\n});\n```\n\nHere's the same thing in Coffeescript:\n``` javascript\napp.config (sparkActionProps) -\u003e\n    angular.extend sparkActionProps, \n        downText:\n            down: (o)-\u003e @element.text(o.val)\n\n        upText:\n            up: (o)-\u003e @element.text(o.val)\n```\n\n## Built-in Formulas\n``` coffeescript\n.constant 'sparkFormulas', {\n\n  # formulas are always in the format: variable or variable\u003coffset\u003e\n  #   (note that you cannot combine formula variables)\n  # for example:\n  #\n  #      topTop+40\n  #      topBottom-120\n  #      topCenter\n  #      centerTop\n  #      centerCenter-111\n  #\n  # are valid formulas. (topTop40 is valid as well but less intuitive)\n  #\n  # each property of the sparkFormulas object is a formula variable\n\n  # top of the element hits the top of the viewport\n  topTop: `function topTop(element, container, rect, containerRect, offset) { return ~~(rect.top - containerRect.top + offset) }`\n\n  # top of the element hits the center of the viewport\n  topCenter: `function topCenter(element, container, rect, containerRect, offset) { return ~~(rect.top - containerRect.top - container.clientHeight/2 + offset) }`\n\n  # top of the element hits the bottom of the viewport\n  topBottom: `function topBottom(element, container, rect, containerRect, offset) {  return ~~(rect.top - containerRect.top - container.clientHeight + offset) }`\n\n  # center of the element hits the top of the viewport\n  centerTop: `function centerTop(element, container, rect, containerRect, offset) { return ~~(rect.top + rect.height/2 - containerRect.top + offset) }`\n\n  # center of the element hits the center of the viewport\n  centerCenter: `function centerCenter(element, container, rect, containerRect, offset) { return ~~(rect.top + rect.height/2 - containerRect.top - container.clientHeight/2 + offset) }`\n\n  # center of the element hits the bottom of the viewport\n  centerBottom: `function centerBottom(element, container, rect, containerRect, offset) {  return ~~(rect.top + rect.height/2 - containerRect.top - container.clientHeight + offset) }`\n\n  # bottom of the element hits the top of the viewport\n  bottomTop: `function bottomTop(element, container, rect, containerRect, offset) { return ~~(rect.bottom - containerRect.top + offset) }`\n\n  # bottom of the element hits the bottom of the viewport\n  bottomBottom: `function bottomBottom(element, container, rect, containerRect, offset) { return ~~(rect.bottom - containerRect.top - container.clientHeight + offset) }`\n\n  # bottom of the element hits the center of the viewport\n  bottomCenter: `function bottomCenter(element, container, rect, containerRect, offset) { return ~~(rect.bottom - containerRect.top - container.clientHeight/2 + offset) }`\n}\n```\n## Register a Custom Formula\n``` javascript\napp.config (sparkFormulas) -\u003e\n    angular.extend sparkFormulas, \n        # similar to the built-in topBottom formula, except that offset is calculated as a percentage of the viewport height\n        topBottomPct: (element, container, rect, containerRect, offset) -\u003e  ~~(rect.bottom - containerRect.top + offset*containerRect.clientHeight/100)\n```\n\n## Keeping Formulas up-to-date\n\nAn element with a spark-scroll(-animate) directive which utilizes formulas will need to \nrecalculate formula-based scroll positions any time the element changes position relative to the document element.\nWith the exception of a window resize event (which spark-scroll watches for automatically), spark-scroll\ndoesn't know when to update formula-calculated scroll positions. In order to keep spark-scroll up-to-date \nemploy one or both of the following techniques:\n\n- Detect when an element's position changes and $broadcast the `sparkInvalidate` event on `$rootScope` or any\n scope which encapsulates all `spark-scroll`ed elements with formulas.\n- Inject the `sparkSetup` service and call:\n    - `sparkSetup.enableInvalidationInterval(delay)` to automatically broadcast `sparkInvalidate` on the \n    $rootScope every `delay` ms. `delay` is optional and by default is 1000 ms.\n    - `sparkSetup.disableInvalidationInterval()` to disable the automatic broadcast interval. **Be sure to call this function in the scope's `$destroy` event handler.**\n\n\n## Debugging\n\nInject `sparkSetup` and enable console logging messages with:\n\n``` javascript\nsparkSetup.debug = true;\n```\n    \n## Globally disabling\n\nInject `sparkSetup` and disable all `spark-scroll` directives with:\n\n``` javascript\nsparkSetup.disableSparkScroll = true;\n```\n\nInject `sparkSetup` and disable all `spark-scroll-animate` directives with:\n\n``` javascript\nsparkSetup.disableSparkScrollAnimate = true;\n```\n\n## Custom Animation Engine\n\n\n`sparkAnimator` can be overridden to use any animation engine\nso long as the `sparkAnimator` service supports the following [Rekapi](http://rekapi.com)-like\ninterface:\n\n``` javascript\nanimator = sparkAnimator.instance()   # returns a new instance\nactor = animator.addActor({ context: \u003cdom element\u003e })  # works just like rekapi.addActor(...)\nactor.keyframe(...)\nactor.moveKeyframe(...)\nactor.removeAllKeyframes()\nanimator.update(...)       # works just like rekapi.update(...)\n```\n\nSee below and the [Rekapi docs](http://rekapi.com/dist/doc/) for implementation details. \n\nNote that overriding the `sparkAnimator` service eliminates the Rekapi and shifty dependencies for `spark-scroll-animate` directive.\n\n### actor.keyframe(scrollY, animations, ease)\n\nCreates a new keyframe.\n\n#### scrollY\n\nThe vertical scroll position (the library will treat this as time)\n\n#### animations\n\nSimple object with css properties and values\n\n- `{marginLeft: \"0px\", opacity: 1}`\n- `{borderRight: \"5px\", opacity: 0}`\n\n#### ease\n\nSimple object with property for each property in `animations` object (see above)\n\n- `{marginLeft: \"easeOutSine\", opacity: \"bouncePast\"}`\n- `{borderRight: \"linear\", opacity: \"easeinSine\"}`\n\n\n### actor.finishedAddingKeyframes\n\nactors can optionally expose this function which will be called when parsing has completed\n\n\n### actor.moveKeyframe(from, to)\n\nMoves a keyframe to a different time (scroll) value.\n\n#### from\n\nSource keyframe\n\n#### to\n\nDestination keyframe\n\n\n### animator.update(scrollY) \n\nUpdates the animation to a specific keyframe.\n\n#### scrollY\n\nThe vertical scroll position (the library will treat this as time)\n\n## Custom Animation Engine: TweenMax (GSAP)\n\nThis repo includes a plugin for TweenMax, `spark-scroll-gsap.js` which allows you to use TweenMax in place of \nShifty and Rekapi for animation. The syntax when using TweenMax will differ slightly \nbecause TweenMax has some differences in the animation properties it supports. For example, \nwhile Rekapi supports the `rotate` property which takes a string value like `360deg`, TweenMax \ninstead supports `rotation` which takes a numeric value like `360`. TweenMax also supports \na rather different set of [easing](http://greensock.com/roughease) equations than [Rekapi](http://rekapi.com/ease.html).\n\n**[spark-scroll TweenMax demo](http://gilbox.github.io/spark-scroll/demo/gsap/)** \n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgilbox%2Fspark-scroll","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgilbox%2Fspark-scroll","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgilbox%2Fspark-scroll/lists"}