Skip to content

Instantly share code, notes, and snippets.

@kotarok
Created December 27, 2017 18:06
Show Gist options
  • Save kotarok/513305f8f5d254800455326d852371ff to your computer and use it in GitHub Desktop.
Save kotarok/513305f8f5d254800455326d852371ff to your computer and use it in GitHub Desktop.
var PositionObserver = function(callback, options) {
this.root = window;
this.rootMargin = this.buildRootMargin_('0');
this.threasholds = undefined;
this.els_ = [];
this.callback_ = callback;
this.init_(options);
this.listen_();
}
PositionObserver.prototype.init_ = function(option) {
if (!option) return false;
if (option.root) {
this.root = option.root;
}
if (option.rootMargin) {
this.rootMargin = this.buildRootMargin_(option.rootMargin);
}
if (option.threshold) {
this.threshold = option.threshold;
}
};
PositionObserver.prototype.listen_ = function() {
if (this.els_) {
this.els_.forEach(function(el) {
if (this.checkPosChangeOf_(el)) {
this.callback_(el, this);
}
}.bind(this));
}
window.requestAnimationFrame(this.listen_.bind(this));
};
PositionObserver.prototype.getRootRect_ = function() {
var root = this.root;
var rootRect = {};
if (root === window || undefined) {
rootRect = {
top: 0,
left: 0,
width: window.innerWidth,
height: window.innerHeight
}
} else {
rootRect = root.getBoundingClientRect();
}
return rootRect
};
PositionObserver.prototype.getRootBounds_ = function(el) {
var rootRect = this.getRootRect_();
var rootMargin = this.parseRootMargin_(this.rootMargin);
var newRootRect = {
top: rootMargin[0] - 0,
left: rootMargin[3] - 0,
width: rootRect.width - rootMargin[1],
height: rootRect.height - rootMargin[2]
};
newRootRect.right = newRootRect.left + newRootRect.width;
newRootRect.bottom = newRootRect.top + newRootRect.height;
return newRootRect;
};
PositionObserver.prototype.buildRootMargin_ = function(marginString) {
var margins = marginString.split(/\s+?/);
margins[1] = margins[1] || margins[0];
margins[2] = margins[2] || margins[0];
margins[3] = margins[3] || margins[1];
return margins.join(' ');
};
PositionObserver.prototype.parseRootMargin_ = function(marginString) {
marginString = this.buildRootMargin_(marginString);
return marginString.split(/\s+?/).map(function(margin, i) {
if (/%$/.test(margin)) {
var ratio = margin.replace(/%$/, '') / 100;
if (i % 2 === 1) {
return this.getRootRect_().width * ratio;
} else {
return this.getRootRect_().height * ratio;
}
} else {
return margin.replace(/px$/, '');
}
}.bind(this));
};
PositionObserver.prototype.checkPosChangeOf_ = function(el) {
var currentRect = el.getBoundingClientRect();
if (el.lastRect === undefined) {
el.lastRect = currentRect;
return true;
}
var isPositionMoved = (
el.lastRect.top !== currentRect.top ||
el.lastRect.left !== currentRect.left
);
if (isPositionMoved) {
return true;
};
el.lastRect = currentRect;
};
PositionObserver.prototype.getVisibilityOf = function(el) {
var rootBounds = this.getRootBounds_();
var targetRect = el.getBoundingClientRect();
var visibility = undefined;
if (
targetRect.top < rootBounds.height &&
targetRect.left < rootBounds.width &&
targetRect.bottom > rootBounds.top &&
targetRect.right > rootBounds.left
) {
visibility = true;
} else if (
targetRect.top > rootBounds.height &&
targetRect.left > rootBounds.width &&
targetRect.bottom < rootBounds.top &&
targetRect.right < rootBounds.left
) {
visibility = false;
}
return visibility;
};
PositionObserver.prototype.getRelativePositionOf = function(el) {
var rootBounds = this.getRootBounds_();
var targetRect = el.getBoundingClientRect();
var relativePosition = {
outerX: (targetRect.left + targetRect.width) / (rootBounds.width + targetRect.width),
outerY: (targetRect.top + targetRect.height) / (rootBounds.height + targetRect.height),
innerX: targetRect.left / rootBounds.width,
innerY: targetRect.top / rootBounds.height
};
return relativePosition;
};
PositionObserver.prototype.observe = function(el) {
this.els_.push(el);
};
PositionObserver.prototype.unobserve = function(el) {
this.els_.splice(this.els_.indexOf(el), 1);
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment