{"id":15015818,"url":"https://github.com/dockyard/ember-in-viewport","last_synced_at":"2025-05-16T04:05:49.503Z","repository":{"id":30319095,"uuid":"33871338","full_name":"DockYard/ember-in-viewport","owner":"DockYard","description":"Detect if an Ember View or Component is in the viewport @ 60FPS","archived":false,"fork":false,"pushed_at":"2024-05-21T16:01:40.000Z","size":5083,"stargazers_count":244,"open_issues_count":28,"forks_count":92,"subscribers_count":10,"default_branch":"master","last_synced_at":"2025-05-16T04:05:45.039Z","etag":null,"topics":["ember","intersectionobserver","viewport"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","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/DockYard.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE.md","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2015-04-13T13:51:50.000Z","updated_at":"2025-03-17T19:00:11.000Z","dependencies_parsed_at":"2024-06-18T12:35:34.790Z","dependency_job_id":"43d5ecff-202e-48f1-83f4-63f8e963414f","html_url":"https://github.com/DockYard/ember-in-viewport","commit_stats":{"total_commits":333,"total_committers":48,"mean_commits":6.9375,"dds":0.7417417417417418,"last_synced_commit":"58c3f97bcfdac85d862f21730b184c9755355901"},"previous_names":[],"tags_count":85,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DockYard%2Fember-in-viewport","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DockYard%2Fember-in-viewport/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DockYard%2Fember-in-viewport/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DockYard%2Fember-in-viewport/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/DockYard","download_url":"https://codeload.github.com/DockYard/ember-in-viewport/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254464895,"owners_count":22075570,"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":["ember","intersectionobserver","viewport"],"created_at":"2024-09-24T19:47:59.775Z","updated_at":"2025-05-16T04:05:49.470Z","avatar_url":"https://github.com/DockYard.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# ember-in-viewport\n*Detect if an Ember View or Component is in the viewport @ 60FPS*\n\n**[ember-in-viewport is built and maintained by DockYard, contact us for expert Ember.js consulting](https://dockyard.com/ember-consulting)**.\n\n[Read the blogpost](https://medium.com/delightful-ui-for-ember-apps/creating-an-ember-cli-addon-detecting-ember-js-components-entering-or-leaving-the-viewport-7d95ceb4f5ed)\n\n![Download count all time](https://img.shields.io/npm/dt/ember-in-viewport.svg)\n[![npm version](https://badge.fury.io/js/ember-in-viewport.svg)](http://badge.fury.io/js/ember-in-viewport)\n[![GitHub Actions Build Status](https://img.shields.io/github/workflow/status/DockYard/ember-in-viewport/CI/master)](https://github.com/DockYard/ember-in-viewport/actions/workflows/ci.yml?query=branch%3Amaster)\n[![Ember Observer Score](http://emberobserver.com/badges/ember-in-viewport.svg)](http://emberobserver.com/addons/ember-in-viewport)\n\nThis Ember addon adds a simple, highly performant Service or modifier to your app. This library will allow you to check if a `Component` or DOM element has entered the browser's viewport. By default, this uses the `IntersectionObserver` API if it detects it the DOM element is in your user's browser – failing which, it falls back to using `requestAnimationFrame`, then if not available, the Ember run loop and event listeners.\n\nWe utilize pooling techniques to reuse Intersection Observers and rAF observers in order to make your app as performant as possible and do as little works as possible.\n\n## Demo or examples\n- Lazy loading responsive images (see `dummy-artwork` for an example artwork component).  Visit `http://localhost:4200/infinity-modifier` to see it in action\n- Dummy app (`ember serve`): https://github.com/DockYard/ember-in-viewport/tree/master/tests/dummy\n- Use with Ember [Modifiers](#modifiers) and [@ember/render-modifiers](https://github.com/emberjs/ember-render-modifiers)\n- Use with [Native Classes](#classes)\n- [ember-infinity](https://github.com/ember-infinity/ember-infinity)\n- [ember-light-table](https://github.com/offirgolan/ember-light-table)\n- Tracking advertisement impressions\n- Occlusion culling\n\n\n# Table of Contents\n\n- [Installation](#installation)\n  * [Usage](#usage)\n    + [Configuration](#configuration)\n    + [Global options](#global-options)\n    + [Modifiers](#modifiers)\n  * [**IntersectionObserver**'s Browser Support](#intersectionobservers-browser-supportscrollableArea)\n    + [Out of the box](#out-of-the-box)\n  * [Running](#running)\n  * [Running Tests](#running-tests)\n  * [Building](#building)\n  * [Legal](#legal)\n\n\n\n# Installation\n\n```\nember install ember-in-viewport\n```\n\n## Usage\nUsage is simple. First, inject the service to your component and start \"watching\" DOM elements.\n\n```js\nimport Component from '@glimmer/component';\nimport { action } from '@ember/object';\nimport { inject as service } from '@ember/service';\n\nexport default class MyClass extends Component {\n  @service inViewport\n\n  @action\n  setupInViewport() {\n    const loader = document.getElementById('loader');\n    const viewportTolerance = { bottom: 200 };\n    const { onEnter, _onExit } = this.inViewport.watchElement(loader, { viewportTolerance });\n    // pass the bound method to `onEnter` or `onExit`\n    onEnter(this.didEnterViewport.bind(this));\n  }\n\n  didEnterViewport() {\n    // do some other stuff\n    this.infinityLoad();\n  }\n\n  willDestroy() {\n    // need to manage cache yourself\n    const loader = document.getElementById('loader');\n    this.inViewport.stopWatching(loader);\n\n    super.willDestroy(...arguments);\n  }\n}\n```\n\n```hbs\n\u003cul\u003e\n  \u003cli\u003e\u003c/li\u003e\n  ...\n\u003c/ul\u003e\n\u003cdiv id=\"loader\"\u003e\u003c/div\u003e\n```\n\nYou can also use [`Modifiers`](#modifiers) as well.  Using modifiers cleans up the boilerplate needed and is shown in a later example.\n\n### Configuration\nTo use with the service based approach, simply pass in the options to `watchElement` as the second argument.\n\n```js\nimport Component from '@glimmer/component';\nimport { inject as service }  from '@ember/service';\n\nexport default class MyClass extends Component {\n  @service inViewport\n\n  @action\n  setupInViewport() {\n    const loader = document.getElementById('loader');\n\n    const { onEnter, _onExit } = this.inViewport.watchElement(\n      loader,\n      {\n        viewportTolerance: { bottom: 200 },\n        intersectionThreshold: 0.25,\n        scrollableArea: '#scrollable-area'\n      }\n    );\n  }\n}\n```\n\n### Global options\n\nYou can set application wide defaults for `ember-in-viewport` in your app (they are still manually overridable inside of a Component). To set new defaults, just add a config object to `config/environment.js`, like so:\n\n```js\nmodule.exports = function(environment) {\n  var ENV = {\n    // ...\n    viewportConfig: {\n      viewportUseRAF                  : true,\n      viewportSpy                     : false,\n      viewportListeners               : [],\n      intersectionThreshold           : 0,\n      scrollableArea                  : null,\n      viewportTolerance: {\n        top    : 0,\n        left   : 0,\n        bottom : 0,\n        right  : 0\n      }\n    }\n  };\n};\n\n// Note if you want to disable right and left in-viewport triggers, set these values to `Infinity`.\n```\n\n### Modifiers\n\nUsing with [Modifiers](https://blog.emberjs.com/2019/03/06/coming-soon-in-ember-octane-part-4.html) is easy.\n\nYou can either use our built in modifier `{{in-viewport}}` or a more verbose, but potentially more flexible generic modifier. Let's start with the former.\n\n1. Use `{{in-viewport}}` modifier on target element\n2. Ensure you have a callbacks in context for enter and/or exit\n3. `options` are optional - see [Advanced usage (options)](#advanced-usage-options)\n\n```hbs\n\u003cul class=\"list\"\u003e\n  \u003cli\u003e\u003c/li\u003e\n  \u003cli\u003e\u003c/li\u003e\n  \u003cdiv {{in-viewport onEnter=(fn this.onEnter artwork) onExit=this.onExit scrollableArea=\".list\"}}\u003e\n    List sentinel\n  \u003c/div\u003e\n\u003c/ul\u003e\n```\n\nThis modifier is useful for a variety of scenarios where you need to watch a sentinel.  With template only components, functionality like this is even more important!  If you have logic that currently uses the `did-insert` modifier to start watching an element, try this one out!\n\nIf you need more than our built in modifier...\n\n1.  Install [@ember/render-modifiers](https://github.com/emberjs/ember-render-modifiers)\n2.  Use the `did-insert` hook inside a component\n3.  Wire up the component like so\n\nNote - This is in lieu of a `did-enter-viewport` modifier, which we plan on adding in the future.  Compared to the solution below, `did-enter-viewport` won't need a container (`this`) passed to it.  But for now, to start using modifiers, this is the easy path.\n\n```js\nimport Component from '@glimmer/component';\nimport { action } from '@ember/object';\nimport { inject as service } from '@ember/service';\n\nexport default class MyClass extends Component {\n  @service inViewport\n\n  @action\n  setupInViewport() {\n    const loader = document.getElementById('loader');\n    const viewportTolerance = { bottom: 200 };\n    const { onEnter, _onExit } = this.inViewport.watchElement(loader, { viewportTolerance });\n    onEnter(this.didEnterViewport.bind(this));\n  }\n\n  didEnterViewport() {\n    // do some other stuff\n    this.infinityLoad();\n  }\n\n  willDestroy() {\n    // need to manage cache yourself\n    const loader = document.getElementById('loader');\n    this.inViewport.stopWatching(loader);\n\n    super.willDestroy(...arguments);\n  }\n}\n```\n\n```hbs\n\u003cdiv {{did-insert this.setupInViewport}}\u003e\n  {{yield}}\n\u003c/div\u003e\n```\n\nOptions as the second argument to `inViewport.watchElement` include:\n- `intersectionThreshold: decimal or array`\n\n  Default: 0\n\n  A single number or array of numbers between 0.0 and 1.0.  A value of 0.0 means the target will be visible when the first pixel enters the viewport.  A value of 1.0 means the entire target must be visible to fire the didEnterViewport hook.\n  Similarily, [0, .25, .5, .75, 1] will fire didEnterViewport every 25% of the target that is visible.\n  (https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API#Thresholds)\n\n  Some notes:\n  - If the target is offscreen, you will get a notification via `didExitViewport` that the target is initially offscreen.  Similarily, this is possible to notify if onscreen when your site loads.\n  - If intersectionThreshold is set to anything greater than 0, you will not see `didExitViewport` hook fired due to our use of the `isIntersecting` property.  See last comment here: https://bugs.chromium.org/p/chromium/issues/detail?id=713819 for purpose of `isIntersecting`\n  - To get around the above issue and have `didExitViewport` fire, set your `intersectionThreshold` to `[0, 1.0]`.  When set to just `1.0`, when the element is 99% visible and still has isIntersecting as true, when the element leaves the viewport, the element isn't applicable to the observer anymore, so the callback isn't called again.\n  - If your intersectionThreshold is set to 0 you will get notified if the target `didEnterViewport` and `didExitViewport` at the appropriate time.\n\n- `scrollableArea: string | HTMLElement`\n\n  Default: null\n\n  A CSS selector for the scrollable area.  e.g. `\".my-list\"`\n\n- `viewportSpy: boolean`\n\n  Default: `false`\n\n  `viewportSpy: true` is often useful when you have \"infinite lists\" that need to keep loading more data.\n  `viewportSpy: false` is often useful for one time loading of artwork, metrics, etc when the come into the viewport.\n\n  If you support IE11 and detect and run logic `onExit`, then it is necessary to have this `true` to that the requestAnimationFrame watching your sentinel is not torn down.\n\n  When `true`, the library will continually watch the `Component` and re-fire hooks whenever it enters or leaves the viewport. Because this is expensive, this behaviour is opt-in. When false, the intersection observer will only watch the `Component` until it enters the viewport once, and then it unbinds listeners. This reduces the load on the Ember run loop and your application.\n\n  NOTE: If using IntersectionObserver (default), viewportSpy wont put too much of a tax on your application.  However, for browsers (Safari \u003c 12.1) that don't currently support IntersectionObserver, we fallback to rAF.  Depending on your use case, the default of `false` may be acceptable.\n\n- `viewportTolerance: object`\n\n  Default: `{ top: 0, left: 0, bottom: 0, right: 0 }`\n\n  This option determines how accurately the `Component` needs to be within the viewport for it to be considered as entered.  Add bottom margin to preemptively trigger didEnterViewport.\n\n  For IntersectionObserver, this property interpolates to [rootMargin](https://developer.mozilla.org/en-US/docs/Web/API/IntersectionObserver/rootMargin).\n  For rAF, this property will use `bottom` tolerance and measure against the height of the container to determine when to trigger didEnterViewport.\n\n  Also, if your sentinel (the watched element) is a zero-height element, ensure that the sentinel actually is able to enter the viewport.\n\n\n## [**IntersectionObserver**'s Browser Support](https://platform-status.mozilla.org/)\n\n### Out of the box\n\n\u003ctable\u003e\n    \u003ctr\u003e\n        \u003ctd\u003eChrome\u003c/td\u003e\n        \u003ctd\u003e51 \u003csup\u003e[1]\u003c/sup\u003e\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n        \u003ctd\u003eFirefox (Gecko)\u003c/td\u003e\n        \u003ctd\u003e55 \u003csup\u003e[2]\u003c/sup\u003e\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n        \u003ctd\u003eMS Edge\u003c/td\u003e\n        \u003ctd\u003e15\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n        \u003ctd\u003eInternet Explorer\u003c/td\u003e\n        \u003ctd\u003eNot supported\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n        \u003ctd\u003eOpera \u003csup\u003e[1]\u003c/sup\u003e\u003c/td\u003e\n        \u003ctd\u003e38\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n        \u003ctd\u003eSafari\u003c/td\u003e\n        \u003ctd\u003eSafari Technology Preview\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n        \u003ctd\u003eChrome for Android\u003c/td\u003e\n        \u003ctd\u003e59\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n        \u003ctd\u003eAndroid Browser\u003c/td\u003e\n        \u003ctd\u003e56\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n        \u003ctd\u003eOpera Mobile\u003c/td\u003e\n        \u003ctd\u003e37\u003c/td\u003e\n    \u003c/tr\u003e\n\u003c/table\u003e\n\n* [1] [Reportedly available](https://www.chromestatus.com/features/5695342691483648), it didn't trigger the events on initial load and lacks `isIntersecting` until later versions.\n* [2] This feature was implemented in Gecko 53.0 (Firefox 53.0 / Thunderbird 53.0 / SeaMonkey 2.50) behind the preference `dom.IntersectionObserver.enabled`.\n\n## Running\n\n* `ember serve`\n* Visit your app at http://localhost:4200.\n\n## Running Tests\n\n* `ember test`\n* `ember test --serve`\n\n## Building\n\n* `ember build`\n\nFor more information on using ember-cli, visit [http://www.ember-cli.com/](http://www.ember-cli.com/).\n\n## Legal\n\n[DockYard](http://dockyard.com/ember-consulting), Inc \u0026copy; 2015\n\n[@dockyard](http://twitter.com/dockyard)\n\n[Licensed under the MIT license](http://www.opensource.org/licenses/mit-license.php)\n\n## Contributors\n\nWe're grateful to these wonderful contributors who've contributed to `ember-in-viewport`:\n\n[//]: contributor-faces\n\u003ca href=\"https://github.com/poteto\"\u003e\u003cimg src=\"https://avatars0.githubusercontent.com/u/1390709?v=4\" title=\"poteto\" width=\"80\" height=\"80\"\u003e\u003c/a\u003e\n\u003ca href=\"https://github.com/snewcomer\"\u003e\u003cimg src=\"https://avatars0.githubusercontent.com/u/7374640?v=4\" title=\"snewcomer\" width=\"80\" height=\"80\"\u003e\u003c/a\u003e\n\u003ca href=\"https://github.com/kellyselden\"\u003e\u003cimg src=\"https://avatars1.githubusercontent.com/u/602423?v=4\" title=\"kellyselden\" width=\"80\" height=\"80\"\u003e\u003c/a\u003e\n\u003ca href=\"https://github.com/martndemus\"\u003e\u003cimg src=\"https://avatars2.githubusercontent.com/u/903637?v=4\" title=\"martndemus\" width=\"80\" height=\"80\"\u003e\u003c/a\u003e\n\u003ca href=\"https://github.com/cibernox\"\u003e\u003cimg src=\"https://avatars2.githubusercontent.com/u/265339?v=4\" title=\"cibernox\" width=\"80\" height=\"80\"\u003e\u003c/a\u003e\n\u003ca href=\"https://github.com/Alinaki\"\u003e\u003cimg src=\"https://avatars0.githubusercontent.com/u/832780?v=4\" title=\"Alinaki\" width=\"80\" height=\"80\"\u003e\u003c/a\u003e\n\u003ca href=\"https://github.com/bcardarella\"\u003e\u003cimg src=\"https://avatars0.githubusercontent.com/u/18524?v=4\" title=\"bcardarella\" width=\"80\" height=\"80\"\u003e\u003c/a\u003e\n\u003ca href=\"https://github.com/danorton-cubic-austin\"\u003e\u003cimg src=\"https://avatars2.githubusercontent.com/u/11525236?v=4\" title=\"danorton-cubic-austin\" width=\"80\" height=\"80\"\u003e\u003c/a\u003e\n\u003ca href=\"https://github.com/michaeldupuisjr\"\u003e\u003cimg src=\"https://avatars3.githubusercontent.com/u/1060866?v=4\" title=\"michaeldupuisjr\" width=\"80\" height=\"80\"\u003e\u003c/a\u003e\n\u003ca href=\"https://github.com/miguelcobain\"\u003e\u003cimg src=\"https://avatars1.githubusercontent.com/u/631691?v=4\" title=\"miguelcobain\" width=\"80\" height=\"80\"\u003e\u003c/a\u003e\n\u003ca href=\"https://github.com/offirgolan\"\u003e\u003cimg src=\"https://avatars2.githubusercontent.com/u/575938?v=4\" title=\"offirgolan\" width=\"80\" height=\"80\"\u003e\u003c/a\u003e\n\u003ca href=\"https://github.com/twokul\"\u003e\u003cimg src=\"https://avatars2.githubusercontent.com/u/1131196?v=4\" title=\"twokul\" width=\"80\" height=\"80\"\u003e\u003c/a\u003e\n\u003ca href=\"https://github.com/langalex\"\u003e\u003cimg src=\"https://avatars0.githubusercontent.com/u/2173?v=4\" title=\"langalex\" width=\"80\" height=\"80\"\u003e\u003c/a\u003e\n\u003ca href=\"https://github.com/alvincrespo\"\u003e\u003cimg src=\"https://avatars0.githubusercontent.com/u/151311?v=4\" title=\"alvincrespo\" width=\"80\" height=\"80\"\u003e\u003c/a\u003e\n\u003ca href=\"https://github.com/blimmer\"\u003e\u003cimg src=\"https://avatars1.githubusercontent.com/u/630449?v=4\" title=\"blimmer\" width=\"80\" height=\"80\"\u003e\u003c/a\u003e\n\u003ca href=\"https://github.com/duggiefresh\"\u003e\u003cimg src=\"https://avatars2.githubusercontent.com/u/1206678?v=4\" title=\"duggiefresh\" width=\"80\" height=\"80\"\u003e\u003c/a\u003e\n\u003ca href=\"https://github.com/elidupuis\"\u003e\u003cimg src=\"https://avatars3.githubusercontent.com/u/196410?v=4\" title=\"elidupuis\" width=\"80\" height=\"80\"\u003e\u003c/a\u003e\n\u003ca href=\"https://github.com/Kjaer\"\u003e\u003cimg src=\"https://avatars0.githubusercontent.com/u/1381484?v=4\" title=\"Kjaer\" width=\"80\" height=\"80\"\u003e\u003c/a\u003e\n\u003ca href=\"https://github.com/jmurphyau\"\u003e\u003cimg src=\"https://avatars0.githubusercontent.com/u/445432?v=4\" title=\"jmurphyau\" width=\"80\" height=\"80\"\u003e\u003c/a\u003e\n\u003ca href=\"https://github.com/jeffplang\"\u003e\u003cimg src=\"https://avatars2.githubusercontent.com/u/102718?v=4\" title=\"jeffplang\" width=\"80\" height=\"80\"\u003e\u003c/a\u003e\n\u003ca href=\"https://github.com/jheth\"\u003e\u003cimg src=\"https://avatars0.githubusercontent.com/u/222011?v=4\" title=\"jheth\" width=\"80\" height=\"80\"\u003e\u003c/a\u003e\n\u003ca href=\"https://github.com/jpadilla\"\u003e\u003cimg src=\"https://avatars0.githubusercontent.com/u/83319?v=4\" title=\"jpadilla\" width=\"80\" height=\"80\"\u003e\u003c/a\u003e\n\u003ca href=\"https://github.com/les2\"\u003e\u003cimg src=\"https://avatars3.githubusercontent.com/u/1066080?v=4\" title=\"les2\" width=\"80\" height=\"80\"\u003e\u003c/a\u003e\n\u003ca href=\"https://github.com/LevelbossMike\"\u003e\u003cimg src=\"https://avatars3.githubusercontent.com/u/242299?v=4\" title=\"LevelbossMike\" width=\"80\" height=\"80\"\u003e\u003c/a\u003e\n\u003ca href=\"https://github.com/rwjblue\"\u003e\u003cimg src=\"https://avatars0.githubusercontent.com/u/12637?v=4\" title=\"rwjblue\" width=\"80\" height=\"80\"\u003e\u003c/a\u003e\n\u003ca href=\"https://github.com/vasind\"\u003e\u003cimg src=\"https://avatars0.githubusercontent.com/u/5652920?v=4\" title=\"vasind\" width=\"80\" height=\"80\"\u003e\u003c/a\u003e\n\u003ca href=\"https://github.com/hybridmuse\"\u003e\u003cimg src=\"https://avatars0.githubusercontent.com/u/27986936?v=4\" title=\"hybridmuse\" width=\"80\" height=\"80\"\u003e\u003c/a\u003e\n\u003ca href=\"https://github.com/csand\"\u003e\u003cimg src=\"https://avatars2.githubusercontent.com/u/142865?v=4\" title=\"csand\" width=\"80\" height=\"80\"\u003e\u003c/a\u003e\n\n[//]: contributor-faces\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdockyard%2Fember-in-viewport","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdockyard%2Fember-in-viewport","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdockyard%2Fember-in-viewport/lists"}