diff --git a/README.md b/README.md index 1076845..ee7c499 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ # follow-js -A dependency free script that let elements follow your cursor without a huge overhead. +A experimental, dependency free script that let elements follow your cursor without a huge overhead. ## Usage 1. Include the `follow.min.js` script to your project @@ -13,7 +13,7 @@ leave it empty, it is set to 10. 2. `` ### unpkg -1. `` +1. `` ## Options ### Dynamically add new elements @@ -23,7 +23,7 @@ elements to its initial position. Then add the new element and fire `follow.init ## Special behaviours ### position relative container If you have an element which is inside an `position: relative` container (as you can see in the examples in -`relative-container.html`) the element will be removed from the inside of the container and moved to the body. It will +`relative-container.html`) the element will be hidden from the inside of the container and copied to the body. It will stay at the exact position on the page as before but will correctly follow the cursor. This might generate some problems if you would like to have a special styling for those. diff --git a/dist/follow.min.js b/dist/follow.min.js index d1fe015..2966fd7 100644 --- a/dist/follow.min.js +++ b/dist/follow.min.js @@ -1 +1 @@ -!function(t){var e={};function o(i){if(e[i])return e[i].exports;var n=e[i]={i:i,l:!1,exports:{}};return t[i].call(n.exports,n,n.exports,o),n.l=!0,n.exports}o.m=t,o.c=e,o.d=function(t,e,i){o.o(t,e)||Object.defineProperty(t,e,{enumerable:!0,get:i})},o.r=function(t){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})},o.t=function(t,e){if(1&e&&(t=o(t)),8&e)return t;if(4&e&&"object"==typeof t&&t&&t.__esModule)return t;var i=Object.create(null);if(o.r(i),Object.defineProperty(i,"default",{enumerable:!0,value:t}),2&e&&"string"!=typeof t)for(var n in t)o.d(i,n,function(e){return t[e]}.bind(null,n));return i},o.n=function(t){var e=t&&t.__esModule?function(){return t.default}:function(){return t};return o.d(e,"a",e),e},o.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},o.p="",o(o.s=0)}([function(t,e,o){"use strict";function i(t,e,o="red",i=3e3){if(follow.debug.dot){let n=document.createElement("i");n.style.position="absolute",n.style.height="1px",n.style.width="1px",n.style.backgroundColor=o,n.style.zIndex="100",n.style.left=t+"px",n.style.top=e+"px",document.body.append(n),setTimeout(()=>{n.remove()},i)}}function n(t,e){follow.debug.log&&(e?console.log(t,e):console.log(t))}o.r(e),window.follow={debug:{log:!1,dot:!1},elements:[],attribute:"data-follow",defaultFactor:10,init:void 0,destroy:void 0,animate:void 0},follow.init=()=>{let t=document.querySelectorAll(`[${follow.attribute}]`);n("targets",t);for(let e of t){let t={factor:e.getAttribute(follow.attribute)?e.getAttribute(follow.attribute):follow.defaultFactor,target:e,x:0,y:0,dimensions:{height:0,width:0},initialPosition:{x:0,y:0}},o=document.body.getBoundingClientRect(),n=t.target.getBoundingClientRect(),l=n.left-o.left,r=n.top-o.top;t.dimensions.height=t.target.offsetHeight,t.dimensions.width=t.target.offsetWidth,t.initialPosition.x=l+t.dimensions.width/2,t.initialPosition.y=r+t.dimensions.height/2,i(t.initialPosition.x,t.initialPosition.y,"red",1e4);let s=t.target.parentElement;for(;"BODY"!==s.tagName;){if("relative"===getComputedStyle(s).position){let e=t.target.cloneNode(!0);e.style.position="absolute",e.style.left=t.initialPosition.x-t.dimensions.width/2+"px",e.style.top=t.initialPosition.y-t.dimensions.height/2+"px",document.body.append(e),t.target.remove(),t.target=e;break}s=s.parentElement}follow.elements.push(t)}document.addEventListener("mousemove",follow.animate),n("elements",follow.elements)},follow.destroy=()=>{n("follow.destroy called"),document.removeEventListener("mousemove",follow.animate);for(const t of follow.elements)t.target.style.left=t.initialPosition.x-t.dimensions.width/2+"px",t.target.style.top=t.initialPosition.y-t.dimensions.height/2+"px";n("follow.destroy() finished")},follow.animate=t=>{let e=t.clientX,o=t.clientY;-1!==navigator.userAgent.search("Firefox")&&(e+=9,o+=15),i(e,o,"blue"),n("mouseX",e),n("mouseY",o);for(let t of follow.elements){n("element",t);let l=(e-t.initialPosition.x)/t.factor,r=(o-t.initialPosition.y)/t.factor,s=t.initialPosition.x+l,a=t.initialPosition.y+r;i(s,a,"green"),t.target.style.left=s-t.dimensions.width/2+"px",t.target.style.top=a-t.dimensions.height/2+"px",n("future position x",s),n("future position y",a)}},follow.init()}]); \ No newline at end of file +!function(t){var e={};function o(i){if(e[i])return e[i].exports;var n=e[i]={i:i,l:!1,exports:{}};return t[i].call(n.exports,n,n.exports,o),n.l=!0,n.exports}o.m=t,o.c=e,o.d=function(t,e,i){o.o(t,e)||Object.defineProperty(t,e,{enumerable:!0,get:i})},o.r=function(t){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})},o.t=function(t,e){if(1&e&&(t=o(t)),8&e)return t;if(4&e&&"object"==typeof t&&t&&t.__esModule)return t;var i=Object.create(null);if(o.r(i),Object.defineProperty(i,"default",{enumerable:!0,value:t}),2&e&&"string"!=typeof t)for(var n in t)o.d(i,n,function(e){return t[e]}.bind(null,n));return i},o.n=function(t){var e=t&&t.__esModule?function(){return t.default}:function(){return t};return o.d(e,"a",e),e},o.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},o.p="",o(o.s=0)}([function(t,e,o){"use strict";function i(t){let e=t.target.cloneNode(!0);e.style.position="absolute",e.style.height=t.dimensions.height+"px",e.style.width=t.dimensions.width+"px",e.style.left=t.initialPosition.x-t.dimensions.width/2+"px",e.style.top=t.initialPosition.y-t.dimensions.height/2+"px",document.body.append(e),t.target.style.opacity="0",t.before=t.target,t.target=e}function n(t,e,o="red",i=3e3){if(follow.debug.dot){let n=document.createElement("i");n.style.position="absolute",n.style.height="1px",n.style.width="1px",n.style.backgroundColor=o,n.style.zIndex="100",n.style.left=t+"px",n.style.top=e+"px",document.body.append(n),setTimeout(()=>{n.remove()},i)}}function l(t,e){follow.debug.log&&(e?console.log(t,e):console.log(t))}o.r(e),window.follow={debug:{log:!1,dot:!1},elements:[],attribute:"data-follow",defaultFactor:10,init:void 0,destroy:void 0,animate:void 0},follow.init=()=>{let t=document.querySelectorAll(`[${follow.attribute}]`);l("targets",t);for(let e of t){let t={factor:e.getAttribute(follow.attribute)?e.getAttribute(follow.attribute):follow.defaultFactor,target:e,before:void 0,x:0,y:0,dimensions:{height:0,width:0},initialPosition:{x:0,y:0}},o=document.body.getBoundingClientRect(),l=t.target.getBoundingClientRect(),r=l.left-o.left,s=l.top-o.top;if(t.dimensions.height=t.target.offsetHeight,t.dimensions.width=t.target.offsetWidth,t.initialPosition.x=r+t.dimensions.width/2,t.initialPosition.y=s+t.dimensions.height/2,n(t.initialPosition.x,t.initialPosition.y,"red",1e4),"absolute"!==getComputedStyle(t.target).position)i(t);else{let e=t.target.parentElement;for(;"BODY"!==e.tagName;){if("relative"===getComputedStyle(e).position){i(t);break}e=e.parentElement}}follow.elements.push(t)}document.addEventListener("mousemove",follow.animate),l("elements",follow.elements)},follow.destroy=()=>{l("follow.destroy called"),document.removeEventListener("mousemove",follow.animate);for(const t of follow.elements)t.before?(t.before.style.opacity="1",t.target.remove()):(t.target.style.left=t.initialPosition.x-t.dimensions.width/2+"px",t.target.style.top=t.initialPosition.y-t.dimensions.height/2+"px");l("follow.destroy() finished")},follow.animate=t=>{let e=t.clientX,o=t.clientY;-1!==navigator.userAgent.search("Firefox")&&(e+=9,o+=15),n(e,o,"blue"),l("mouseX",e),l("mouseY",o);for(let t of follow.elements){l("element",t);let i=(e-t.initialPosition.x)/t.factor,r=(o-t.initialPosition.y)/t.factor,s=t.initialPosition.x+i,a=t.initialPosition.y+r;n(s,a,"green"),t.target.style.left=s-t.dimensions.width/2+"px",t.target.style.top=a-t.dimensions.height/2+"px",l("future position x",s),l("future position y",a)}},follow.init()}]); \ No newline at end of file diff --git a/package.json b/package.json index 7af0514..3a130e7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "follow-js", - "version": "1.1.0", + "version": "1.1.1", "description": "let elements follow your cursor", "main": "dist/follow.js", "repository": { diff --git a/src/helper.js b/src/helper.js new file mode 100644 index 0000000..252dc36 --- /dev/null +++ b/src/helper.js @@ -0,0 +1,27 @@ +export function clone (element) { + // clone element + let absolute = element.target.cloneNode(true) + + // give to the new element position absolute + absolute.style.position = 'absolute' + + // add the correct dimensions to the element + absolute.style.height = element.dimensions.height + 'px' + absolute.style.width = element.dimensions.width + 'px' + + // position element to exact spot + absolute.style.left = element.initialPosition.x - (element.dimensions.width / 2) + 'px' + absolute.style.top = element.initialPosition.y - (element.dimensions.height / 2) + 'px' + + // append absolute element to the body + document.body.append(absolute) + + // hide old element but not remove it to keep the space + element.target.style.opacity = '0' + + // save element as before to add it back + element.before = element.target + + // add new element as new target + element.target = absolute +} \ No newline at end of file diff --git a/src/index.js b/src/index.js index 44129ed..ead8b9c 100644 --- a/src/index.js +++ b/src/index.js @@ -1,3 +1,4 @@ +import * as helper from './helper' import * as debug from './debug' window.follow = { @@ -28,6 +29,7 @@ follow.init = () => { let element = { factor: factor, target: target, + before: undefined, x: 0, y: 0, dimensions: { @@ -57,35 +59,24 @@ follow.init = () => { // if center helper is wanted debug.dot(element.initialPosition.x, element.initialPosition.y, 'red', 10000) - // if container has position relative, remove element and add back on top of all - let parent = element.target.parentElement - - while (true) { - if (parent.tagName === 'BODY') { - break - } - if (getComputedStyle(parent).position === 'relative') { - // clone element - let absoluteElement = element.target.cloneNode(true) - - // give to the new element position absolute - absoluteElement.style.position = 'absolute' - - // position element to exact spot - absoluteElement.style.left = element.initialPosition.x - (element.dimensions.width / 2) + 'px' - absoluteElement.style.top = element.initialPosition.y - (element.dimensions.height / 2) + 'px' - - // append absolute element to the body - document.body.append(absoluteElement) - - // remove old element - element.target.remove() - - // add new element as new target - element.target = absoluteElement - break + // if position isn't absolute clone element + if (getComputedStyle(element.target).position !== 'absolute') { + helper.clone(element) + } else { + // if container has position relative, remove element and add back on top of all + let parent = element.target.parentElement + + while (true) { + // if parent is body break out of while + if (parent.tagName === 'BODY') { + break + // else if position is relative + } else if (getComputedStyle(parent).position === 'relative') { + helper.clone(element) + break + } + parent = parent.parentElement } - parent = parent.parentElement } // push element to array @@ -106,10 +97,16 @@ follow.destroy = () => { // remove the event listener document.removeEventListener('mousemove', follow.animate) - // set the initial position for all elements for (const element of follow.elements) { - element.target.style.left = element.initialPosition.x - (element.dimensions.width / 2) + 'px' - element.target.style.top = element.initialPosition.y - (element.dimensions.height / 2) + 'px' + if (element.before) { + // if before element exists, bring it back and remove absolute element + element.before.style.opacity = '1' + element.target.remove() + } else { + // set the initial position for all elements + element.target.style.left = element.initialPosition.x - (element.dimensions.width / 2) + 'px' + element.target.style.top = element.initialPosition.y - (element.dimensions.height / 2) + 'px' + } } // log