Skip to content

Commit

Permalink
Merge branch 'release/1.1.0'
Browse files Browse the repository at this point in the history
  • Loading branch information
poteto committed Apr 24, 2015
2 parents 0f5a00c + 4d99245 commit cec2c52
Show file tree
Hide file tree
Showing 3 changed files with 64 additions and 50 deletions.
28 changes: 27 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,34 @@ export default Ember.Component.extend(InViewportMixin, {

### Basic usage
#### Available hooks
##### `didEnterViewport`, `didExitViewport`
These hooks fire once whenever the `Component` enters or exits the viewport. You can handle them the same way you would handle any other native Ember hook:

```js
export default Ember.Component.extend(InViewportMixin, {

// with prototype extensions disabled
handleDidEnterViewport: Ember.on('didEnterViewport', function() {
console.log('entered');
}),

handleDidExitViewport: Ember.on('didExitViewport', function() {
console.log('exited');
}),

// with prototype extensions enabled
didEnterViewport() {
console.log('entered');
},

didExitViewport() {
console.log('exited');
}
});
```

##### `viewportEntered`
This hook fires whenever the `Component` enters the viewport. To apply an `.active` class to your `Component` when it enters the viewport, you can simply bind the `active` class to the mixed in property `viewportEntered`, like so:
To apply an `.active` class to your `Component` when it enters the viewport, you can simply bind the `active` class to the mixed in property `viewportEntered`, like so:

```js
export default Ember.Component.extend(InViewportMixin, {
Expand Down
84 changes: 36 additions & 48 deletions addon/mixins/in-viewport.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@ export default Ember.Mixin.create({

_setInitialState: on('init', function() {
setProperties(this, {
$viewportCachedEl : undefined,
viewportUseRAF : canUseRAF(),
viewportEntered : false,
viewportSpy : false,
Expand All @@ -53,13 +52,10 @@ export default Ember.Mixin.create({
_setupElement: on('didInsertElement', function() {
if (!canUseDOM) { return; }

const viewportUseRAF = get(this, 'viewportUseRAF');

this._setInitialViewport(window);
this._addObserverIfNotSpying();
this._setViewportEntered(window);

if (!viewportUseRAF) {
if (!get(this, 'viewportUseRAF')) {
forEach(listeners, (listener) => {
const { context, event } = listener;
this._bindListeners(context, event);
Expand All @@ -72,49 +68,52 @@ export default Ember.Mixin.create({
}),

_addObserverIfNotSpying() {
const viewportSpy = get(this, 'viewportSpy');

if (!viewportSpy) {
this.addObserver('viewportEntered', this, this._viewportDidEnter);
if (!get(this, 'viewportSpy')) {
this.addObserver('viewportEntered', this, this._unbindIfEntered);
}
},

_setViewportEntered(context = null) {
Ember.assert('You must pass a valid context to _setViewportEntered', context);

const $viewportCachedEl = get(this, '$viewportCachedEl');
const viewportUseRAF = get(this, 'viewportUseRAF');
const elementId = get(this, 'elementId');
const tolerance = get(this, 'viewportTolerance');
const height = $(context) ? $(context).height() : 0;
const width = $(context) ? $(context).width() : 0;

let boundingClientRect;

if ($viewportCachedEl) {
boundingClientRect = $viewportCachedEl[0].getBoundingClientRect();
} else {
boundingClientRect = set(this, '$viewportCachedEl', this.$())[0].getBoundingClientRect();
}

const viewportEntered = isInViewport(boundingClientRect, height, width, tolerance);
const viewportUseRAF = get(this, 'viewportUseRAF');
const viewportTolerance = get(this, 'viewportTolerance');
const elementId = get(this, 'elementId');
const boundingClientRect = get(this, 'element').getBoundingClientRect();
const contextEl = $(context);
const height = contextEl.height();
const width = contextEl.width();

set(this, 'viewportEntered', viewportEntered);
this._triggerDidEnterViewport(
isInViewport(boundingClientRect, height, width, viewportTolerance)
);

if ($viewportCachedEl && viewportUseRAF) {
if (boundingClientRect && viewportUseRAF) {
rAFIDS[elementId] = window.requestAnimationFrame(
bind(this, this._setViewportEntered, context)
);
}
},

_viewportDidEnter() {
const viewportEntered = get(this, 'viewportEntered');
const viewportSpy = get(this, 'viewportSpy');
_triggerDidEnterViewport(hasEnteredViewport = false) {
const viewportEntered = get(this, 'viewportEntered');
const didEnter = !viewportEntered && hasEnteredViewport;
const didLeave = viewportEntered && !hasEnteredViewport;
let triggeredEventName = '';

if (didEnter) { triggeredEventName = 'didEnterViewport'; }
if (didLeave) { triggeredEventName = 'didExitViewport'; }

this.trigger(triggeredEventName);

if (!viewportSpy && viewportEntered) {
set(this, 'viewportEntered', hasEnteredViewport);
},

_unbindIfEntered() {
if (!get(this, 'viewportSpy') && get(this, 'viewportEntered')) {
this._unbindListeners();
this.removeObserver('viewportEntered', this, this._viewportDidEnter);
this.removeObserver('viewportEntered', this, this._unbindIfEntered);
set(this, 'viewportEntered', true);
}
},

Expand All @@ -129,35 +128,24 @@ export default Ember.Mixin.create({
_scrollHandler(context = null) {
Ember.assert('You must pass a valid context to _scrollHandler', context);

const viewportRefreshRate = get(this, 'viewportRefreshRate');

debounce(this, function() {
debounce(this, () => {
this._setViewportEntered(context);
}, viewportRefreshRate);
}, get(this, 'viewportRefreshRate'));
},

_bindListeners(context = null, event = null) {
Ember.assert('You must pass a valid context to _bindListeners', context);
Ember.assert('You must pass a valid event to _bindListeners', event);

const elementId = get(this, 'elementId');

Ember.warn('No elementId was found on this Object, `viewportSpy` will' +
'not work as expected', elementId);

$(context).on(`${event}#${elementId}`, () => {
$(context).on(`${event}#${get(this, 'elementId')}`, () => {
this._scrollHandler(context);
});
},

_unbindListeners() {
const elementId = get(this, 'elementId');
const viewportUseRAF = get(this, 'viewportUseRAF');

Ember.warn('No elementId was found on this Object, `viewportSpy` will' +
'not work as expected', elementId);
const elementId = get(this, 'elementId');

if (viewportUseRAF) {
if (get(this, 'viewportUseRAF')) {
next(this, () => {
window.cancelAnimationFrame(rAFIDS[elementId]);
rAFIDS[elementId] = null;
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "ember-in-viewport",
"version": "1.0.0",
"version": "1.1.0",
"description": "Detect if an Ember View or Component is in the viewport @ 60FPS",
"directories": {
"doc": "doc",
Expand Down

0 comments on commit cec2c52

Please sign in to comment.