diff --git a/README.md b/README.md
index 95c0082..a61d53f 100644
--- a/README.md
+++ b/README.md
@@ -1,9 +1,11 @@
+#BETA
+- values range changed from 0-1 to 0-10
+- you can define "percentage" like this: v-parallax="{speed: 3, percentage: 1}" more more fine control
+
#vue-parallax-js
vue component for parallax effect on elements.
- no dependencies.
-- for Vue.js 2
- lightweight
-- 1.6 kb minified
##Setup
```bash
@@ -25,7 +27,7 @@ Vue.use(VueParallaxJs)
##Usage
when everything is setup you can use the directive like this:
```html
-
vue-parallax-js
+vue-parallax-js
```
##Options and Modifiers
diff --git a/package.json b/package.json
index 994f9d8..9d37a8c 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "vue-parallax-js",
- "version": "0.0.4",
+ "version": "0.1.0",
"description": "Vue component for easy parallax with directive",
"main": "vue-parallax-js.js",
"directories": {
diff --git a/src/vue-parallax-js.js b/src/vue-parallax-js.js
index 2425891..491810c 100644
--- a/src/vue-parallax-js.js
+++ b/src/vue-parallax-js.js
@@ -1,114 +1,239 @@
-let parallaxjs = function (options) {
- this.options = options
-}
+let gop = {};
+
+const vueParallaxJS = function(el, options) {
+ var self = Object.create(vueParallaxJS.prototype);
+
+ var posY = 0;
+ var screenY = 0;
+ var blocks = [];
+ var pause = false;
+
+ let active = true;
+ let minWidth = gop.minWidth || 0;
+
+ var loop = window.requestAnimationFrame ||
+ window.webkitRequestAnimationFrame ||
+ window.mozRequestAnimationFrame ||
+ window.msRequestAnimationFrame ||
+ window.oRequestAnimationFrame ||
+ function(callback) {
+ setTimeout(callback, 1000 / 60);
+ };
+
+ // check which transform property to use
+ var transformProp = window.transformProp || (function() {
+ var testEl = document.createElement('div');
+ if (testEl.style.transform == null) {
+ var vendors = ['Webkit', 'Moz', 'ms'];
+ for (var vendor in vendors) {
+ if (testEl.style[vendors[vendor] + 'Transform'] !== undefined) {
+ return vendors[vendor] + 'Transform';
+ }
+ }
+ }
+ return 'transform';
+ })();
+
+ // limit the given number in the range [min, max]
+ var clamp = function(num, min, max) {
+ return (num <= min) ? min : ((num >= max) ? max : num);
+ };
+
+ self.options = {
+ speed: -2,
+ center: false,
+ round: true,
+ };
+
+
+ // self.options.speed = clamp(self.options.speed, -10, 10);
+
+
+ let elements = [el]
+
+ // Now query selector
+ if (elements.length > 0) {
+ self.elems = elements;
+ }
-parallaxjs.prototype = {
- items: [],
- active: true,
-
- setStyle (item, value) {
- if (item.modifiers.centerX)
- value += ' translateX(-50%)'
-
- let el = item.el;
- let prop = 'Transform';
- el.style["webkit" + prop] = value;
- el.style["moz" + prop] = value;
- el.style["ms" + prop] = value;
- },
-
- add (el, binding) {
- let value = binding.value
- let arg = binding.arg
- let style = el.currentStyle || window.getComputedStyle(el);
-
- let height = binding.modifiers.absY ? window.innerHeight : el.clientHeight || el.offsetHeight || el.scrollHeight;
- this.items.push({
- el: el,
- initialOffsetTop: el.offsetTop + el.offsetParent.offsetTop - parseInt(style.marginTop),
- style,
- value,
- arg,
- modifiers: binding.modifiers,
- clientHeight: height,
- count: 0
- })
- },
-
- move () {
- if (!this.active) return
- if (window.innerWidth < this.options.minWidth || 0) {
- this.items.map((item) => {
- this.setStyle(item, 'translateY(' + 0 + 'px) translateZ(0px)')
- })
-
- return
+ // The elements don't exist
+ else {
+ throw new Error("The elements you're trying to select don't exist.");
}
- let scrollTop = window.scrollY || window.pageYOffset
- let windowHeight = window.innerHeight
- let windowWidth = window.innerWidth
- this.items.map((item) => {
- let pos = (scrollTop + windowHeight)
- let elH = item.clientHeight
- // if (item.count > 50) {
- // item.count = 0;
- // elH = item.el.clientHeight || item.el.offsetHeight || item.el.scrollHeight
- // }
+ // Let's kick this script off
+ // Build array for cached element values
+ // Bind scroll and resize to animate method
+ var init = function() {
+ screenY = window.innerHeight;
+ setPosition();
+
+ // Get and cache initial position of all elements
+ for (var i = 0; i < self.elems.length; i++) {
+ var block = createBlock(self.elems[i], options);
+ blocks.push(block);
+ }
+
+ window.addEventListener('resize', function() {
+ animate();
+ });
+
+ // Start the loop
+ update();
+ animate();
+ };
+
+
+ var createBlock = function(el, options) {
+ var dataPercentage = options.percentage;
+ var dataSpeed = options.speed;
+
+
+ var posY = dataPercentage || self.options.center ? (window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop) : 0;
+
+ var blockTop = posY + el.getBoundingClientRect().top;
+ var blockHeight = el.clientHeight || el.offsetHeight || el.scrollHeight;
+
+ var percentage = dataPercentage ? dataPercentage : (posY - blockTop + screenY) / (blockHeight + screenY);
+ if (self.options.center) {
+ percentage = 0.5;
+ }
+
+ var speed = dataSpeed ? clamp(dataSpeed, -10, 10) : self.options.speed;
+ if (dataPercentage || self.options.center) {
+ speed = clamp(dataSpeed || self.options.speed, -5, 5);
+ }
+
+ var base = updatePosition(percentage, speed);
+
+ var style = el.style.cssText;
+ var transform = '';
+
+ if (style.indexOf('transform') >= 0) {
+ // Get the index of the transform
+ var index = style.indexOf('transform');
+ var trimmedStyle = style.slice(index);
+ var delimiter = trimmedStyle.indexOf(';');
- pos = pos - (elH / 2)
- pos = pos - (windowHeight / 2)
- pos = pos * item.value
+ if (delimiter) {
+ transform = " " + trimmedStyle.slice(11, delimiter).replace(/\s/g, '');
+ } else {
+ transform = " " + trimmedStyle.slice(11).replace(/\s/g, '');
+ }
+ }
- let offset = item.initialOffsetTop
- offset = offset * -1
- offset = offset * item.value
- pos = pos + offset
+ return {
+ base: base,
+ top: blockTop,
+ height: blockHeight,
+ speed: speed,
+ style: style,
+ transform: transform
+ };
+ };
- pos = pos.toFixed(2)
+ var setPosition = function() {
+ var oldY = posY;
- // item.count++
- this.setStyle(item, 'translateY(' + pos + 'px)')
- })
- }
+ if (window.pageYOffset !== undefined) {
+ posY = window.pageYOffset;
+ } else {
+ posY = (document.documentElement || document.body.parentNode || document.body).scrollTop;
+ }
+
+ if (oldY != posY) {
+ // scroll changed, return true
+ return true;
+ }
+
+ // scroll did not change
+ return false;
+ };
+
+
+ var updatePosition = function(percentage, speed) {
+
+ var value = (speed * (100 * (1 - percentage)));
+ return self.options.round ? Math.round(value) : value;
+
+ };
+
+
+ //
+ var update = function() {
+ if (setPosition() && pause === false) {
+ animate();
+ }
+
+ // loop again
+ loop(update);
+ };
+
+ // Transform3d on parallax element
+ var animate = function() {
+ for (var i = 0; i < self.elems.length; i++) {
+ var percentage = ((posY - blocks[i].top + screenY) / (blocks[i].height + screenY));
+
+ // Subtracting initialize value, so element stays in same spot as HTML
+ var position;
+ if (window.innerWidth >= minWidth && parallaxjs.api.active === true) {
+ position = updatePosition(percentage, blocks[i].speed) - blocks[i].base;
+ } else {
+ position = 0
+ }
+
+ // Move that element
+ // (Set the new translation and append initial inline transforms.)
+ var translate = 'translate3d(0,' + position + 'px,0) ' + blocks[i].transform;
+ self.elems[i].style[transformProp] = translate;
+ }
+ };
+
+
+ self.destroy = function() {
+ for (var i = 0; i < self.elems.length; i++) {
+ self.elems[i].style.cssText = blocks[i].style;
+ }
+ pause = true;
+ };
+
+
+ init();
+ return self;
+};
+
+
+const parallaxjs = {
+ api: {
+ active: true
+ },
+ add(el, binding) {
+ if (el.parallax)
+ return
+
+ let options = {};
+ if (typeof val === 'number') {
+ options.speed = binding.value
+ } else {
+ options = binding.value
+ }
+
+ el.parallax = new vueParallaxJS(el, options);
+ }
}
export default {
- install (Vue, options = {}) {
- var p = new parallaxjs(options)
-
- window.addEventListener('scroll', () => {
- requestAnimationFrame(() => {
- p.move(p)
- })
- }, {passive: true})
- window.addEventListener('resize', () => {
- requestAnimationFrame(() => {
- p.move(p)
- })
- }, {passive: true})
-
- Vue.prototype.$parallaxjs = p
- window.$parallaxjs = p
- Vue.directive('parallax', {
- bind (el, binding) {
- },
- inserted (el, binding) {
- p.add(el, binding)
- p.move(p)
- },
- // unbind(el, binding) {
- // p.remove(el)
- // }
- // bind: parallaxjs.add(parallaxjs),
- // update(value) {
- // parallaxjs.update(value)
- // },
- // update(el, binding) {
- // console.log("cup");
- // },
- })
- }
+ install(Vue, options = {}) {
+ gop = options;
+ window.parallaxjs = parallaxjs.api;
+ Vue.directive('parallax', {
+ bind(el, binding) {
+ },
+ inserted(el, binding) {
+ parallaxjs.add(el, binding)
+ },
+ })
+ }
}
diff --git a/vue-parallax-js.js b/vue-parallax-js.js
index ec2430d..180ada2 100644
--- a/vue-parallax-js.js
+++ b/vue-parallax-js.js
@@ -1,104 +1,224 @@
-var parallaxjs = function parallaxjs(options) {
- this.options = options;
-};
-
-parallaxjs.prototype = {
- items: [],
- active: true,
-
- setStyle: function setStyle(item, value) {
- if (item.modifiers.centerX) value += ' translateX(-50%)';
-
- var el = item.el;
- var prop = 'Transform';
- el.style["webkit" + prop] = value;
- el.style["moz" + prop] = value;
- el.style["ms" + prop] = value;
- },
- add: function add(el, binding) {
- var value = binding.value;
- var arg = binding.arg;
- var style = el.currentStyle || window.getComputedStyle(el);
-
- var height = binding.modifiers.absY ? window.innerHeight : el.clientHeight || el.offsetHeight || el.scrollHeight;
- this.items.push({
- el: el,
- initialOffsetTop: el.offsetTop + el.offsetParent.offsetTop - parseInt(style.marginTop),
- style: style,
- value: value,
- arg: arg,
- modifiers: binding.modifiers,
- clientHeight: height,
- count: 0
- });
- },
- move: function move() {
- var _this = this;
-
- if (!this.active) return;
- if (window.innerWidth < this.options.minWidth || 0) {
- this.items.map(function (item) {
- _this.setStyle(item, 'translateY(' + 0 + 'px) translateZ(0px)');
- });
-
- return;
+var gop = {};
+
+var vueParallaxJS = function vueParallaxJS(el, options) {
+ var self = Object.create(vueParallaxJS.prototype);
+
+ var posY = 0;
+ var screenY = 0;
+ var blocks = [];
+ var pause = false;
+
+ var active = true;
+ var minWidth = gop.minWidth || 0;
+
+ var loop = window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.msRequestAnimationFrame || window.oRequestAnimationFrame || function (callback) {
+ setTimeout(callback, 1000 / 60);
+ };
+
+ // check which transform property to use
+ var transformProp = window.transformProp || function () {
+ var testEl = document.createElement('div');
+ if (testEl.style.transform == null) {
+ var vendors = ['Webkit', 'Moz', 'ms'];
+ for (var vendor in vendors) {
+ if (testEl.style[vendors[vendor] + 'Transform'] !== undefined) {
+ return vendors[vendor] + 'Transform';
+ }
+ }
+ }
+ return 'transform';
+ }();
+
+ // limit the given number in the range [min, max]
+ var clamp = function clamp(num, min, max) {
+ return num <= min ? min : num >= max ? max : num;
+ };
+
+ self.options = {
+ speed: -2,
+ center: false,
+ round: true
+ };
+
+ // self.options.speed = clamp(self.options.speed, -10, 10);
+
+
+ var elements = [el];
+
+ // Now query selector
+ if (elements.length > 0) {
+ self.elems = elements;
}
- var scrollTop = window.scrollY || window.pageYOffset;
- var windowHeight = window.innerHeight;
- var windowWidth = window.innerWidth;
-
- this.items.map(function (item) {
- var pos = scrollTop + windowHeight;
- var elH = item.clientHeight;
- // if (item.count > 50) {
- // item.count = 0;
- // elH = item.el.clientHeight || item.el.offsetHeight || item.el.scrollHeight
- // }
-
-
- pos = pos - elH / 2;
- pos = pos - windowHeight / 2;
- pos = pos * item.value;
-
- var offset = item.initialOffsetTop;
- offset = offset * -1;
- offset = offset * item.value;
- pos = pos + offset;
-
- pos = pos.toFixed(2);
+ // The elements don't exist
+ else {
+ throw new Error("The elements you're trying to select don't exist.");
+ }
+
+ // Let's kick this script off
+ // Build array for cached element values
+ // Bind scroll and resize to animate method
+ var init = function init() {
+ screenY = window.innerHeight;
+ setPosition();
+
+ // Get and cache initial position of all elements
+ for (var i = 0; i < self.elems.length; i++) {
+ var block = createBlock(self.elems[i], options);
+ blocks.push(block);
+ }
+
+ window.addEventListener('resize', function () {
+ animate();
+ });
+
+ // Start the loop
+ update();
+ animate();
+ };
+
+ var createBlock = function createBlock(el, options) {
+ var dataPercentage = options.percentage;
+ var dataSpeed = options.speed;
+
+ var posY = dataPercentage || self.options.center ? window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop : 0;
+
+ var blockTop = posY + el.getBoundingClientRect().top;
+ var blockHeight = el.clientHeight || el.offsetHeight || el.scrollHeight;
+
+ var percentage = dataPercentage ? dataPercentage : (posY - blockTop + screenY) / (blockHeight + screenY);
+ if (self.options.center) {
+ percentage = 0.5;
+ }
+
+ var speed = dataSpeed ? clamp(dataSpeed, -10, 10) : self.options.speed;
+ if (dataPercentage || self.options.center) {
+ speed = clamp(dataSpeed || self.options.speed, -5, 5);
+ }
+
+ var base = updatePosition(percentage, speed);
+
+ var style = el.style.cssText;
+ var transform = '';
+
+ if (style.indexOf('transform') >= 0) {
+ // Get the index of the transform
+ var index = style.indexOf('transform');
+
+ var trimmedStyle = style.slice(index);
+ var delimiter = trimmedStyle.indexOf(';');
+
+ if (delimiter) {
+ transform = " " + trimmedStyle.slice(11, delimiter).replace(/\s/g, '');
+ } else {
+ transform = " " + trimmedStyle.slice(11).replace(/\s/g, '');
+ }
+ }
+
+ return {
+ base: base,
+ top: blockTop,
+ height: blockHeight,
+ speed: speed,
+ style: style,
+ transform: transform
+ };
+ };
+
+ var setPosition = function setPosition() {
+ var oldY = posY;
+
+ if (window.pageYOffset !== undefined) {
+ posY = window.pageYOffset;
+ } else {
+ posY = (document.documentElement || document.body.parentNode || document.body).scrollTop;
+ }
+
+ if (oldY != posY) {
+ // scroll changed, return true
+ return true;
+ }
+
+ // scroll did not change
+ return false;
+ };
+
+ var updatePosition = function updatePosition(percentage, speed) {
+
+ var value = speed * (100 * (1 - percentage));
+ return self.options.round ? Math.round(value) : value;
+ };
+
+ //
+ var update = function update() {
+ if (setPosition() && pause === false) {
+ animate();
+ }
+
+ // loop again
+ loop(update);
+ };
+
+ // Transform3d on parallax element
+ var animate = function animate() {
+ for (var i = 0; i < self.elems.length; i++) {
+ var percentage = (posY - blocks[i].top + screenY) / (blocks[i].height + screenY);
+
+ // Subtracting initialize value, so element stays in same spot as HTML
+ var position;
+ if (window.innerWidth >= minWidth && parallaxjs.api.active === true) {
+ position = updatePosition(percentage, blocks[i].speed) - blocks[i].base;
+ } else {
+ position = 0;
+ }
+
+ // Move that element
+ // (Set the new translation and append initial inline transforms.)
+ var translate = 'translate3d(0,' + position + 'px,0) ' + blocks[i].transform;
+ self.elems[i].style[transformProp] = translate;
+ }
+ };
+
+ self.destroy = function () {
+ for (var i = 0; i < self.elems.length; i++) {
+ self.elems[i].style.cssText = blocks[i].style;
+ }
+ pause = true;
+ };
+
+ init();
+ return self;
+};
- // item.count++
- _this.setStyle(item, 'translateY(' + pos + 'px)');
- });
- }
+var parallaxjs = {
+ api: {
+ active: true
+ },
+ add: function add(el, binding) {
+ if (el.parallax) return;
+
+ var options = {};
+ if (typeof val === 'number') {
+ options.speed = binding.value;
+ } else {
+ options = binding.value;
+ }
+
+ el.parallax = new vueParallaxJS(el, options);
+ }
};
export default {
- install: function install(Vue) {
- var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
-
- var p = new parallaxjs(options);
-
- window.addEventListener('scroll', function () {
- requestAnimationFrame(function () {
- p.move(p);
- });
- }, { passive: true });
- window.addEventListener('resize', function () {
- requestAnimationFrame(function () {
- p.move(p);
- });
- }, { passive: true });
-
- Vue.prototype.$parallaxjs = p;
- window.$parallaxjs = p;
- Vue.directive('parallax', {
- bind: function bind(el, binding) {},
- inserted: function inserted(el, binding) {
- p.add(el, binding);
- p.move(p);
- }
- });
- }
+ install: function install(Vue) {
+ var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
+
+ gop = options;
+ window.parallaxjs = parallaxjs.api;
+ Vue.directive('parallax', {
+ bind: function bind(el, binding) {},
+ inserted: function inserted(el, binding) {
+ parallaxjs.add(el, binding);
+ }
+ });
+ }
};