creepyface
Advanced tools
Comparing version
import { UserOptions } from './util/options'; | ||
import { Cancel } from './util/types'; | ||
import { Cancel, Observable } from './util/types'; | ||
import { Point } from './util/algebra'; | ||
declare const creepyface: { | ||
(img: HTMLImageElement, options?: UserOptions | undefined): Cancel; | ||
cancel(img: HTMLImageElement): void; | ||
registerObservable(name: string, observable: Observable<Point>): void; | ||
}; | ||
export default creepyface; |
import attach from './util/attach'; | ||
import watchElement from './util/watch-element'; | ||
import noop from './util/noop'; | ||
import * as observableStore from './observables/util/store'; | ||
const creepyface = (img, options) => { | ||
creepyface.cancel(img); | ||
let detach = noop; | ||
const creepyImage = img; | ||
const stopWatching = watchElement(img, () => { | ||
detach = attach(creepyImage, options); | ||
detach = attach(img, options); | ||
}, () => creepyface.cancel(img)); | ||
return (creepyImage.creepyfaceCancel = () => { | ||
return (img.creepyfaceCancel = () => { | ||
stopWatching(); | ||
detach(); | ||
delete creepyImage.creepyfaceCancel; | ||
delete img.creepyfaceCancel; | ||
}); | ||
}; | ||
creepyface.cancel = (img) => { | ||
const creepyImage = img; | ||
if (creepyImage.creepyfaceCancel) | ||
creepyImage.creepyfaceCancel(); | ||
const cancel = img.creepyfaceCancel; | ||
if (cancel) | ||
cancel(); | ||
}; | ||
creepyface.registerObservable = (name, observable) => { | ||
observableStore.register(name, observable); | ||
}; | ||
document.addEventListener('DOMContentLoaded', () => { | ||
; | ||
[...document.querySelectorAll('img[data-creepy]')].forEach(el => { | ||
const elements = document.querySelectorAll('img[data-creepy],img[data-creepyface]'); | ||
for (let i = 0; i < elements.length; i++) { | ||
const el = elements[i]; | ||
if (el instanceof HTMLImageElement) | ||
creepyface(el); | ||
}); | ||
} | ||
}); | ||
export default creepyface; |
@@ -1,1 +0,1 @@ | ||
!function(n,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):(n=n||self).creepyface=e()}(this,function(){"use strict";function c(n,e,t,r,o,i,u){try{var a=n[i](u),c=a.value}catch(n){return void t(n)}a.done?e(c):Promise.resolve(c).then(r,o)}function f(a){return function(){var n=this,u=arguments;return new Promise(function(e,t){var r=a.apply(n,u);function o(n){c(r,e,t,o,i,"next",n)}function i(n){c(r,e,t,o,i,"throw",n)}o(void 0)})}}function o(n,e){for(var t=0;t<e.length;t++){var r=e[t];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(n,r.key,r)}}function h(n,e){return function(n){if(Array.isArray(n))return n}(n)||function(n,e){var t=[],r=!0,o=!1,i=void 0;try{for(var u,a=n[Symbol.iterator]();!(r=(u=a.next()).done)&&(t.push(u.value),!e||t.length!==e);r=!0);}catch(n){o=!0,i=n}finally{try{r||null==a.return||a.return()}finally{if(o)throw i}}return t}(n,e)||function(){throw new TypeError("Invalid attempt to destructure non-iterable instance")}()}function n(n){return function(n){if(Array.isArray(n)){for(var e=0,t=new Array(n.length);e<n.length;e++)t[e]=n[e];return t}}(n)||function(n){if(Symbol.iterator in Object(n)||"[object Arguments]"===Object.prototype.toString.call(n))return Array.from(n)}(n)||function(){throw new TypeError("Invalid attempt to spread non-iterable instance")}()}var l=function e(r){if(!r)return Promise.reject();if("string"==typeof r){var n=r;(r=new Image).src=n}else{if(void 0!==r.length){var t=[].map.call(r,function(n){return e(n).catch(function(n){return n})});return Promise.all(t).then(function(n){var e=n.filter(function(n){return n.naturalWidth});return e.length===n.length?e:Promise.reject({loaded:e,errored:n.filter(function(n){return!n.naturalWidth})})})}if("IMG"!==r.tagName)return Promise.reject()}var o=new Promise(function(n,e){function t(){r.naturalWidth?n(r):e(r),r.removeEventListener("load",t),r.removeEventListener("error",t)}r.naturalWidth?n(r):r.complete?e(r):(r.addEventListener("load",t),r.addEventListener("error",t))});return o.image=r,o};var v=function(n,e){var t,r={};return void 0===(e=e||{}).separator&&(e.separator="-"),Array.prototype.slice.call(n.attributes).filter((t=e.pattern,function(n){var e;return e=/^data\-/.test(n.name),void 0===t?e:e&&t.test(n.name.slice(5))})).forEach(function(o){o.name.slice(5).split(e.separator).reduce(function(n,e,t,r){return"data"===e?n:(t===r.length-1?n[e]=o.value:n[e]=n[e]||{},n[e])},r)}),r};var p=function(){function e(n){!function(n,e){if(!(n instanceof e))throw new TypeError("Cannot call a class as a function")}(this,e),this.subscriber=n}var n,t,r;return n=e,(t=[{key:"subscribe",value:function(n){return{unsubscribe:this.subscriber({next:n})}}}])&&o(n.prototype,t),r&&o(n,r),e}(),t=[];document.addEventListener("mousemove",function(e){return t.forEach(function(n){return n.next([e.clientX,e.clientY])})},!0);var m=new p(function(n){return t.push(n),function(){t.splice(t.indexOf(n),1)}}),s=function(n,t){return n.map(function(n,e){return n+t[e]})},a=function(n){return n*Math.PI/180},d=function(n){return e=Math.atan2(n[1],n[0]),t=2*Math.PI,180*((t+e%t)%t)/Math.PI;var e,t},e=[];document.addEventListener("touchmove",function(c){return e.forEach(function(n){var e=[0,0],t=!0,r=!1,o=void 0;try{for(var i,u=c.touches[Symbol.iterator]();!(t=(i=u.next()).done);t=!0){var a=i.value;e=s(e,[a.clientX,a.clientY])}}catch(n){r=!0,o=n}finally{try{t||null==u.return||u.return()}finally{if(r)throw o}}n.next(e)})},!0);var b=new p(function(n){return e.push(n),function(){e.splice(e.indexOf(n),1)}}),y=function(){},g=function(n){for(var e=[],t=Object.keys(n),r=0;r<t.length;r++){var o=t[r],i=n[o];i&&e.push({angle:parseFloat(o),src:i})}return e};function w(n){var e,t,r,o,i,u,a,c,f,l,s=1<arguments.length&&void 0!==arguments[1]?arguments[1]:{},d=Object.assign({},(t=v(e=n),r=t.src,o=void 0===r?{}:r,i=t.fieldofvision,u=t.timetodefault,a=t.resetoncancel,c=t.throttle,f={src:e.getAttribute("src")||void 0},u&&(f.timeToDefault=parseFloat(u)),c&&(f.throttle=parseFloat(c)),i&&(f.fieldOfVision=parseFloat(i)),a&&(f.resetOnCancel="true"===a),o.hover&&(f.hover=o.hover),o.look&&(f.looks=g(o.look)),f),s);if(!d.src)throw new Error("A default URL must be specified");return{fieldOfVision:d.fieldOfVision||150,src:d.src,hover:d.hover||"",points:d.points||(l=[m,b],new p(function(n){var e=n.next.bind(n),t=l.map(function(n){return n.subscribe(e)});return function(){return t.forEach(function(n){return n.unsubscribe()})}})),looks:d.looks||[],timeToDefault:d.timeToDefault||1e3,resetOnCancel:!(!1===d.resetOnCancel),throttle:d.throttle||100,onDebug:d.onDebug||y,onAttach:d.onAttach||y,onDetach:d.onDetach||y}}var O=function(n){var e=n.getBoundingClientRect(),t=e.top+window.pageYOffset+e.height/2,r=e.left+window.pageXOffset+e.width/2;return{y:t,x:r}};var x=function(n,e){return d((o=s([window.scrollX,window.scrollY],e),u=O(n),i=[u.x,u.y],t=o.map(function(n,e){return n-i[e]}),r=90,[t[0]*Math.cos(a(r))-t[1]*Math.sin(a(r)),t[0]*Math.sin(a(r))+t[1]*Math.cos(a(r))]));var t,r,o,i,u},E=function(n){return 180<Math.abs(n)?n-360*((e=n)?e<0?-1:1:0):n;var e},M=function(n,e){return e.slice(0).sort((t=n,function(n,e){return Math.abs(E(n.angle-t))-Math.abs(E(e.angle-t))}))[0];var t},j=function(n,e,t){return e<=n&&n<=t},T=function(n,e){var t,r,o,i,u,a,c,f,l,s=h(e,2),d=s[0],v=s[1];return document.elementFromPoint?document.elementFromPoint(d,v)===n:(t=n.getBoundingClientRect(),r=[d,v],o=t.left,i=t.top,u=t.right,a=t.bottom,c=h(r,2),f=c[0],l=c[1],j(f,o,u)&&j(l,i,a))},r="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:{},D="Expected a function",i=NaN,u="[object Symbol]",A=/^\s+|\s+$/g,L=/^[-+]0x[0-9a-f]+$/i,P=/^0b[01]+$/i,C=/^0o[0-7]+$/i,I=parseInt,k="object"==typeof r&&r&&r.Object===Object&&r,R="object"==typeof self&&self&&self.Object===Object&&self,F=k||R||Function("return this")(),S=Object.prototype.toString,W=Math.max,N=Math.min,V=function(){return F.Date.now()};function X(r,o,n){var i,u,a,c,f,l,s=0,d=!1,v=!1,e=!0;if("function"!=typeof r)throw new TypeError(D);function h(n){var e=i,t=u;return i=u=void 0,s=n,c=r.apply(t,e)}function p(n){var e=n-l;return void 0===l||o<=e||e<0||v&&a<=n-s}function m(){var n,e,t=V();if(p(t))return b(t);f=setTimeout(m,(e=o-((n=t)-l),v?N(e,a-(n-s)):e))}function b(n){return f=void 0,e&&i?h(n):(i=u=void 0,c)}function t(){var n,e=V(),t=p(e);if(i=arguments,u=this,l=e,t){if(void 0===f)return s=n=l,f=setTimeout(m,o),d?h(n):c;if(v)return f=setTimeout(m,o),h(l)}return void 0===f&&(f=setTimeout(m,o)),c}return o=$(o)||0,Y(n)&&(d=!!n.leading,a=(v="maxWait"in n)?W($(n.maxWait)||0,o):a,e="trailing"in n?!!n.trailing:e),t.cancel=function(){void 0!==f&&clearTimeout(f),i=l=u=f=void(s=0)},t.flush=function(){return void 0===f?c:b(V())},t}function Y(n){var e=typeof n;return!!n&&("object"==e||"function"==e)}function $(n){if("number"==typeof n)return n;if("symbol"==typeof(e=n)||(t=e)&&"object"==typeof t&&S.call(e)==u)return i;var e,t;if(Y(n)){var r="function"==typeof n.valueOf?n.valueOf():n;n=Y(r)?r+"":r}if("string"!=typeof n)return 0===n?n:+n;n=n.replace(A,"");var o=P.test(n);return o||C.test(n)?I(n.slice(2),o?2:8):L.test(n)?i:+n}var B=function(n,e,t){var r=!0,o=!0;if("function"!=typeof n)throw new TypeError(D);return Y(t)&&(r="leading"in t?!!t.leading:r,o="trailing"in t?!!t.trailing:o),X(n,e,{leading:r,maxWait:e,trailing:o})},q=function(i,u){return new p(function(r){var o=function(e,t,r){var o,i,u,a,c;function f(){var n=Date.now()-a;n<t&&0<=n?o=setTimeout(f,t-n):(o=null,r||(c=e.apply(u,i),u=i=null))}null==t&&(t=100);var n=function(){u=this,i=arguments,a=Date.now();var n=r&&!o;return o||(o=setTimeout(f,t)),n&&(c=e.apply(u,i),u=i=null),c};return n.clear=function(){o&&(clearTimeout(o),o=null)},n.flush=function(){o&&(c=e.apply(u,i),u=i=null,clearTimeout(o),o=null)},n}(function(){return r.next({src:u.src,options:u})},u.timeToDefault),n=u.points.subscribe(B(function(n){var e=x(i,n),t=function(n,e,t,r){var o=r.looks,i=r.hover,u=r.fieldOfVision,a=r.src;if(i&&T(n,e))a=i;else{var c=M(t,o);Math.abs(E(c.angle-t))<u/2&&(a=c.src)}return a}(i,n,e,u);r.next({point:n,angle:e,src:t,options:u}),o()},u.throttle));return function(){o.clear(),n.unsubscribe()}})},G=function(e,n){var t,r,o,i=w(e,n),u=function(n){e.src=n},a=(t=e,l((r=i,o=r.looks.map(function(n){return n.src}),r.src&&o.push(r.src),r.hover&&o.push(r.hover),o)).then(function(n){return t.creepyfaceReachableImages=n,function(){delete t.creepyfaceReachableImages}})),c=a.then(function(){return i.onAttach(),q(e,i).subscribe(function(n){u(n.src),i.onDebug(n)})});return f(regeneratorRuntime.mark(function n(){var e,t;return regeneratorRuntime.wrap(function(n){for(;;)switch(n.prev=n.next){case 0:return n.next=2,c;case 2:return e=n.sent,n.next=5,a;case 5:t=n.sent,e.unsubscribe(),t(),i.resetOnCancel&&u(i.src),i.onDetach();case 10:case"end":return n.stop()}},n,this)}))},H=function n(e,t){n.cancel(e);var r=y,o=e,i=function(t,e,r){var o=function(n){return n?e():r()};if(MutationObserver){var i=document.body&&document.body.contains(t);i&&o(!0);var n=new MutationObserver(function(n){var e=document.body&&document.body.contains(t);!e&&i?(o(!1),i=!1):e&&!i&&(o(!0),i=!0)});return n.observe(document,{childList:!0,subtree:!0}),function(){return n.disconnect()}}return document.body&&document.body.contains(t)&&o(!0),t.addEventListener("DOMNodeInserted",function(){return o(!0)},!1),t.addEventListener("DOMNodeRemoved",function(){return o(!1)},!1),y}(e,function(){r=G(o,t)},function(){return n.cancel(e)});return o.creepyfaceCancel=function(){i(),r(),delete o.creepyfaceCancel}};return H.cancel=function(n){var e=n;e.creepyfaceCancel&&e.creepyfaceCancel()},document.addEventListener("DOMContentLoaded",function(){n(document.querySelectorAll("img[data-creepy]")).forEach(function(n){n instanceof HTMLImageElement&&H(n)})}),H}); | ||
!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):(t=t||self).creepyface=e()}(this,function(){"use strict";var l=function(o,i){var c=[];o.forEach(function(t){var e,n,r;e=t,n=function(t){c.push(t),c.length===o.length&&i(c)},(r=new Image).src=e,r.onload=r.onerror=function(){r.naturalWidth||console.error("Creepyface was unable to load ".concat(e)),delete r.onload,delete r.onerror,n(r)}})};var c=function(){},n=[];document.addEventListener("mousemove",function(e){return n.forEach(function(t){return t([e.clientX,e.clientY])})},!0);var u=function(t,e){return[t[0]+e[0],t[1]+e[1]]},a=function(t){return t*Math.PI/180},f=function(t){return e=Math.atan2(t[1],t[0]),n=2*Math.PI,180*((n+e%n)%n)/Math.PI;var e,n},e=[];document.addEventListener("touchmove",function(o){return e.forEach(function(t){for(var e=[0,0],n=0;n<o.touches.length;n++){var r=o.touches[n];e=u(e,[r.clientX,r.clientY])}t(e)})},!0);var r,o={pointer:(r=[function(t){return n.push(t),function(){n.splice(n.indexOf(t),1)}},function(t){return e.push(t),function(){e.splice(e.indexOf(t),1)}}],function(e){var t=r.map(function(t){return t(e)});return function(){return t.forEach(function(t){return t()})}})},i=function(t){var e=t?parseFloat(t):NaN;return isNaN(e)?void 0:e},s=function(t){return{hover:t.getAttribute("data-src-hover")||void 0,looks:function(t){for(var e=/data-src-look-(\d+)/i,n=[],r=0;r<t.attributes.length;r++){var o=t.attributes[r],i=e.exec(o.name);i&&n.push({angle:parseFloat(i[1]),src:o.value})}return n.length?n:void 0}(t),points:t.getAttribute("data-points")||void 0,timeToDefault:i(t.getAttribute("data-timetodefault")),throttle:i(t.getAttribute("data-throttle")),fieldOfVision:i(t.getAttribute("data-fieldofvision"))}},d=function(t){return"function"==typeof t.points?t.points:(e=t.points||"pointer",o[e]?o[e]:(console.error("No observable registered as '".concat(e,"', defaulting to pointer.")),o.pointer));var e};function v(t){var e=1<arguments.length&&void 0!==arguments[1]?arguments[1]:{},n=function(o){for(var t=1;t<arguments.length;t++){var i=null!=arguments[t]?arguments[t]:{},e=Object.keys(i);"function"==typeof Object.getOwnPropertySymbols&&(e=e.concat(Object.getOwnPropertySymbols(i).filter(function(t){return Object.getOwnPropertyDescriptor(i,t).enumerable}))),e.forEach(function(t){var e,n,r;e=o,r=i[n=t],n in e?Object.defineProperty(e,n,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[n]=r})}return o}({},s(t),e),r=t.getAttribute("src");if(!r)throw new Error("A default URL must be specified");return{fieldOfVision:n.fieldOfVision||150,src:r,hover:n.hover||"",points:d(n),looks:n.looks||[],timeToDefault:n.timeToDefault||1e3,throttle:n.throttle||100,onDebug:n.onDebug||c,onAttach:n.onAttach||c,onDetach:n.onDetach||c}}var h=function(t){var e=t.getBoundingClientRect(),n=e.top+window.pageYOffset+e.height/2,r=e.left+window.pageXOffset+e.width/2;return{y:n,x:r}};var m=function(t,e){return f((o=u([window.scrollX,window.scrollY],e),c=h(t),i=[c.x,c.y],n=[o[0]-i[0],o[1]-i[1]],r=90,[n[0]*Math.cos(a(r))-n[1]*Math.sin(a(r)),n[0]*Math.sin(a(r))+n[1]*Math.cos(a(r))]));var n,r,o,i,c},b=function(t){return 180<Math.abs(t)?t-360*((e=t)?e<0?-1:1:0):t;var e},g=function(t,e){return e.slice(0).sort((n=t,function(t,e){return Math.abs(b(t.angle-n))-Math.abs(b(e.angle-n))}))[0];var n},p=function(t,e,n){return e<=t&&t<=n},y=function(t,e){return document.elementFromPoint?document.elementFromPoint(e[0],e[1])===t:(n=t.getBoundingClientRect(),r=[e[0],e[1]],o=n.left,i=n.top,c=n.right,u=n.bottom,p(r[0],o,c)&&p(r[1],i,u));var n,r,o,i,c,u},O=function(h,p){return function(r){var t,e,n,o,i,c,u,a,f,l,s,d=(t=function(){return r({src:p.src,options:p})},e=p.timeToDefault,(o=function(){n&&clearTimeout(n),n=setTimeout(function(){n=null,t()},e)}).clear=function(){n&&(clearTimeout(n),n=null)},o),v=p.points((i=function(t){var e=m(h,t),n=function(t,e,n,r){var o=r.looks,i=r.hover,c=r.fieldOfVision,u=r.src;if(i&&y(t,e))u=i;else{var a=g(n,o);Math.abs(b(a.angle-n))<c/2&&(u=a.src)}return u}(h,t,e,p);r({point:t,angle:e,src:n,options:p}),d()},c=p.throttle,f=0,l=function(){f=Date.now(),u=null,i(a)},(s=function(t){var e=Date.now(),n=c-(e-f);a=t,n<=0||c<n?(u&&(clearTimeout(u),u=null),f=e,i(t)):u||(u=setTimeout(l,n))}).clear=function(){u&&clearTimeout(u),f=0,u=null},s));return function(){d.clear(),v()}}},w=function(n,t){var e,r,o,i,c=v(n,t),u=function(t){n.src=t},a=!1,f=function(){a=!0};return e=n,r=function(t){if(!a){c.onAttach();var e=O(n,c)(function(t){u(t.src),c.onDebug(t)});f=function(){e(),u(c.src),c.onDetach(),t()}}},l((i=(o=c).looks.map(function(t){return t.src}),o.src&&i.push(o.src),o.hover&&i.push(o.hover),i),function(t){e.creepyfaceReachableImages=t,r(function(){delete e.creepyfaceReachableImages})}),function(){return f()}},M=function t(e,n){t.cancel(e);var r=c,o=function(n,e,r){var o=function(t){return t?e():r()};if(MutationObserver){var i=document.body.contains(n);i&&o(!0);var t=new MutationObserver(function(t){var e=document.body.contains(n);!e&&i?(o(!1),i=!1):e&&!i&&(o(!0),i=!0)});return t.observe(document,{childList:!0,subtree:!0}),function(){return t.disconnect()}}return document.body.contains(n)&&o(!0),n.addEventListener("DOMNodeInserted",function(){return o(!0)},!1),n.addEventListener("DOMNodeRemoved",function(){return o(!1)},!1),c}(e,function(){r=w(e,n)},function(){return t.cancel(e)});return e.creepyfaceCancel=function(){o(),r(),delete e.creepyfaceCancel}};return M.cancel=function(t){var e=t.creepyfaceCancel;e&&e()},M.registerObservable=function(t,e){var n;n=e,o[t]=n},document.addEventListener("DOMContentLoaded",function(){for(var t=document.querySelectorAll("img[data-creepy],img[data-creepyface]"),e=0;e<t.length;e++){var n=t[e];n instanceof HTMLImageElement&&M(n)}}),M}); |
@@ -1,3 +0,4 @@ | ||
import Observable from './util/observable'; | ||
declare const _default: (observables: Observable<number[]>[]) => Observable<number[]>; | ||
import { Observable, Observer } from '../util/types'; | ||
import { Point } from '../util/algebra'; | ||
declare const _default: (observables: Observable<Point>[]) => (observer: Observer<Point>) => () => void; | ||
export default _default; |
@@ -1,6 +0,4 @@ | ||
import Observable from './util/observable'; | ||
export default (observables) => new Observable(observer => { | ||
const next = observer.next.bind(observer); | ||
const subscriptions = observables.map(o => o.subscribe(next)); | ||
return () => subscriptions.forEach(s => s.unsubscribe()); | ||
}); | ||
export default (observables) => (observer) => { | ||
const cancels = observables.map(o => o(observer)); | ||
return () => cancels.forEach(cancel => cancel()); | ||
}; |
@@ -1,5 +0,4 @@ | ||
import Observable from './util/observable'; | ||
import { Observable } from '../util/types'; | ||
import { Options, CreepyData } from '../util/options'; | ||
import { CreepyImage } from '../util/types'; | ||
declare const _default: (img: CreepyImage, options: Options) => Observable<CreepyData>; | ||
declare const _default: (img: HTMLImageElement, options: Options) => Observable<CreepyData>; | ||
export default _default; |
import getAngle from './util/get-angle'; | ||
import getSrc from './util/get-src'; | ||
import debounce from 'debounce'; | ||
import Observable from './util/observable'; | ||
import throttle from 'lodash.throttle'; | ||
export default (img, options) => new Observable(observer => { | ||
const backToDefault = debounce(() => observer.next({ src: options.src, options }), options.timeToDefault); | ||
const subscription = options.points.subscribe(throttle(point => { | ||
import debounce from './util/debounce'; | ||
import throttle from './util/throttle'; | ||
export default (img, options) => observer => { | ||
const backToDefault = debounce(() => observer({ src: options.src, options }), options.timeToDefault); | ||
const cancel = options.points(throttle(point => { | ||
const angle = getAngle(img, point); | ||
const src = getSrc(img, point, angle, options); | ||
observer.next({ point, angle, src, options }); | ||
observer({ point, angle, src, options }); | ||
backToDefault(); | ||
@@ -16,4 +15,4 @@ }, options.throttle)); | ||
backToDefault.clear(); | ||
subscription.unsubscribe(); | ||
cancel(); | ||
}; | ||
}); | ||
}; |
@@ -1,3 +0,4 @@ | ||
import Observable from './util/observable'; | ||
declare const _default: Observable<number[]>; | ||
import { Observer } from '../util/types'; | ||
import { Point } from '../util/algebra'; | ||
declare const _default: (observer: Observer<Point>) => () => void; | ||
export default _default; |
@@ -1,2 +0,1 @@ | ||
import Observable from './util/observable'; | ||
import { add } from '../util/algebra'; | ||
@@ -6,8 +5,9 @@ const observers = []; | ||
let point = [0, 0]; | ||
for (const touch of event.touches) { | ||
for (let i = 0; i < event.touches.length; i++) { | ||
const touch = event.touches[i]; | ||
point = add(point, [touch.clientX, touch.clientY]); | ||
} | ||
observer.next(point); | ||
observer(point); | ||
}), true); | ||
export default new Observable(observer => { | ||
export default (observer) => { | ||
observers.push(observer); | ||
@@ -17,2 +17,2 @@ return () => { | ||
}; | ||
}); | ||
}; |
@@ -1,3 +0,4 @@ | ||
import Observable from './util/observable'; | ||
declare const _default: Observable<number[]>; | ||
import { Observer } from '../util/types'; | ||
import { Point } from '../util/algebra'; | ||
declare const _default: (observer: Observer<Point>) => () => void; | ||
export default _default; |
@@ -1,5 +0,4 @@ | ||
import Observable from './util/observable'; | ||
const observers = []; | ||
document.addEventListener('mousemove', (event) => observers.forEach(observer => observer.next([event.clientX, event.clientY])), true); | ||
export default new Observable(observer => { | ||
document.addEventListener('mousemove', (event) => observers.forEach(observer => observer([event.clientX, event.clientY])), true); | ||
export default (observer) => { | ||
observers.push(observer); | ||
@@ -9,2 +8,2 @@ return () => { | ||
}; | ||
}); | ||
}; |
@@ -1,3 +0,3 @@ | ||
import { CreepyImage } from '../../util/types'; | ||
declare const _default: (img: CreepyImage, point: number[]) => number; | ||
import { Point } from '../../util/algebra'; | ||
declare const _default: (img: HTMLImageElement, point: Point) => number; | ||
export default _default; |
@@ -0,4 +1,4 @@ | ||
import { Point } from '../../util/algebra'; | ||
import { Options } from '../../util/options'; | ||
import { CreepyImage } from '../../util/types'; | ||
declare const _default: (img: CreepyImage, point: number[], angle: number, options: Options) => string; | ||
declare const _default: (img: HTMLImageElement, point: Point, angle: number, options: Options) => string; | ||
export default _default; |
@@ -6,9 +6,9 @@ import { sign } from '../../util/algebra'; | ||
const within = (n, a, b) => n >= a && n <= b; | ||
const rectContains = ({ left, top, right, bottom }, [x, y]) => within(x, left, right) && within(y, top, bottom); | ||
const elementContains = (img, [x, y]) => { | ||
const rectContains = ({ left, top, right, bottom }, point) => within(point[0], left, right) && within(point[1], top, bottom); | ||
const elementContains = (img, point) => { | ||
if (document.elementFromPoint) { | ||
return document.elementFromPoint(x, y) === img; | ||
return document.elementFromPoint(point[0], point[1]) === img; | ||
} | ||
else { | ||
return rectContains(img.getBoundingClientRect(), [x, y]); | ||
return rectContains(img.getBoundingClientRect(), [point[0], point[1]]); | ||
} | ||
@@ -15,0 +15,0 @@ }; |
@@ -1,7 +0,7 @@ | ||
export declare type Vector = Array<number>; | ||
export declare type Point = [number, number]; | ||
export declare type Degrees = number; | ||
export declare type Radians = number; | ||
export declare type Angle = Degrees; | ||
export declare const diff: (v1: number[], v2: number[]) => number[]; | ||
export declare const add: (v1: number[], v2: number[]) => number[]; | ||
export declare const diff: (v1: Point, v2: Point) => Point; | ||
export declare const add: (v1: Point, v2: Point) => Point; | ||
export declare const sign: (n: number) => 1 | 0 | -1; | ||
@@ -11,3 +11,3 @@ export declare const rad: (deg: number) => number; | ||
export declare const mod: (n: number, m: number) => number; | ||
export declare const getAngle: (v: number[]) => number; | ||
export declare const rotate: (v: number[], deg: number) => number[]; | ||
export declare const getAngle: (v: Point) => number; | ||
export declare const rotate: (v: Point, deg: number) => Point; |
@@ -1,3 +0,9 @@ | ||
export const diff = (v1, v2) => v1.map((x, i) => x - v2[i]); | ||
export const add = (v1, v2) => v1.map((x, i) => x + v2[i]); | ||
export const diff = (v1, v2) => [ | ||
v1[0] - v2[0], | ||
v1[1] - v2[1] | ||
]; | ||
export const add = (v1, v2) => [ | ||
v1[0] + v2[0], | ||
v1[1] + v2[1] | ||
]; | ||
export const sign = (n) => (n ? (n < 0 ? -1 : 1) : 0); | ||
@@ -4,0 +10,0 @@ export const rad = (deg) => (deg * Math.PI) / 180; |
import { UserOptions } from './options'; | ||
import { Cancel, CreepyImage } from './types'; | ||
declare const _default: (img: CreepyImage, userOptions?: UserOptions | undefined) => Cancel; | ||
import { Cancel } from './types'; | ||
declare const _default: (img: HTMLImageElement, userOptions?: UserOptions | undefined) => Cancel; | ||
export default _default; |
@@ -9,19 +9,22 @@ import preload from './preload'; | ||
}; | ||
const preloaded = preload(img, options); | ||
const subscribed = preloaded.then(() => { | ||
let cancelled = false; | ||
let cancel = () => { | ||
cancelled = true; | ||
}; | ||
preload(img, options, unload => { | ||
if (cancelled) | ||
return; | ||
options.onAttach(); | ||
return creepy(img, options).subscribe(data => { | ||
const stopCreepyface = creepy(img, options)(data => { | ||
setSrc(data.src); | ||
options.onDebug(data); | ||
}); | ||
cancel = () => { | ||
stopCreepyface(); | ||
setSrc(options.src); | ||
options.onDetach(); | ||
unload(); | ||
}; | ||
}); | ||
return async () => { | ||
const subscription = await subscribed; | ||
const unload = await preloaded; | ||
subscription.unsubscribe(); | ||
unload(); | ||
if (options.resetOnCancel) | ||
setSrc(options.src); | ||
options.onDetach(); | ||
}; | ||
return () => cancel(); | ||
}; |
@@ -1,4 +0,3 @@ | ||
import Observable from '../observables/util/observable'; | ||
import { Angle, Vector } from './algebra'; | ||
import { CreepyImage } from './types'; | ||
import { Observable } from './types'; | ||
import { Angle, Point } from './algebra'; | ||
export declare type Millis = number; | ||
@@ -12,3 +11,3 @@ export declare type Time = Millis; | ||
export declare type CreepyData = { | ||
point?: Vector; | ||
point?: Point; | ||
angle?: Angle; | ||
@@ -25,5 +24,4 @@ src: string; | ||
looks: Array<Look>; | ||
points: Observable<Vector>; | ||
points: Observable<Point>; | ||
timeToDefault: Time; | ||
resetOnCancel: boolean; | ||
throttle: Time; | ||
@@ -36,8 +34,6 @@ onDebug: Debug; | ||
fieldOfVision?: Angle; | ||
src?: ImageURL; | ||
hover?: ImageURL; | ||
looks?: Array<Look>; | ||
points?: Observable<Vector>; | ||
points?: Observable<Point> | string; | ||
timeToDefault?: Time; | ||
resetOnCancel?: boolean; | ||
throttle?: Time; | ||
@@ -48,2 +44,2 @@ onDebug?: Debug; | ||
}; | ||
export default function getOptions(img: CreepyImage, options?: UserOptions): Options; | ||
export default function getOptions(img: HTMLImageElement, options?: UserOptions): Options; |
@@ -1,47 +0,45 @@ | ||
import parseDataAttributes from 'data-attrs-to-js'; | ||
import mousePoints from '../observables/mouse'; | ||
import fingerPoints from '../observables/finger'; | ||
import combined from '../observables/combined'; | ||
import noop from './noop'; | ||
const getLooks = (look) => { | ||
import * as observableStore from '../observables/util/store'; | ||
const getLooks = (img) => { | ||
const regex = /data-src-look-(\d+)/i; | ||
const looks = []; | ||
for (const key of Object.keys(look)) { | ||
const src = look[key]; | ||
if (src) { | ||
looks.push({ angle: parseFloat(key), src }); | ||
for (let i = 0; i < img.attributes.length; i++) { | ||
const attr = img.attributes[i]; | ||
const match = regex.exec(attr.name); | ||
if (match) { | ||
looks.push({ angle: parseFloat(match[1]), src: attr.value }); | ||
} | ||
} | ||
return looks; | ||
return looks.length ? looks : undefined; | ||
}; | ||
function fromImage(element) { | ||
const { src = {}, fieldofvision, timetodefault, resetoncancel, throttle } = parseDataAttributes(element); | ||
const options = { | ||
src: element.getAttribute('src') || undefined | ||
}; | ||
if (timetodefault) | ||
options.timeToDefault = parseFloat(timetodefault); | ||
if (throttle) | ||
options.throttle = parseFloat(throttle); | ||
if (fieldofvision) | ||
options.fieldOfVision = parseFloat(fieldofvision); | ||
if (resetoncancel) | ||
options.resetOnCancel = resetoncancel === 'true'; | ||
if (src.hover) | ||
options.hover = src.hover; | ||
if (src.look) | ||
options.looks = getLooks(src.look); | ||
return options; | ||
} | ||
const getFloat = (s) => { | ||
const float = s ? parseFloat(s) : NaN; | ||
return isNaN(float) ? undefined : float; | ||
}; | ||
const fromImage = (img) => ({ | ||
hover: img.getAttribute('data-src-hover') || undefined, | ||
looks: getLooks(img), | ||
points: img.getAttribute('data-points') || undefined, | ||
timeToDefault: getFloat(img.getAttribute('data-timetodefault')), | ||
throttle: getFloat(img.getAttribute('data-throttle')), | ||
fieldOfVision: getFloat(img.getAttribute('data-fieldofvision')) | ||
}); | ||
const getPoints = (userOptions) => { | ||
if (typeof userOptions.points === 'function') { | ||
return userOptions.points; | ||
} | ||
return observableStore.retrieve(userOptions.points || 'pointer'); | ||
}; | ||
export default function getOptions(img, options = {}) { | ||
const userOptions = Object.assign({}, fromImage(img), options); | ||
if (!userOptions.src) | ||
const userOptions = { ...fromImage(img), ...options }; | ||
const src = img.getAttribute('src'); | ||
if (!src) | ||
throw new Error('A default URL must be specified'); | ||
return { | ||
fieldOfVision: userOptions.fieldOfVision || 150, | ||
src: userOptions.src, | ||
src, | ||
hover: userOptions.hover || '', | ||
points: userOptions.points || combined([mousePoints, fingerPoints]), | ||
points: getPoints(userOptions), | ||
looks: userOptions.looks || [], | ||
timeToDefault: userOptions.timeToDefault || 1000, | ||
resetOnCancel: !(userOptions.resetOnCancel === false), | ||
throttle: userOptions.throttle || 100, | ||
@@ -48,0 +46,0 @@ onDebug: userOptions.onDebug || noop, |
import { Options } from './options'; | ||
import { CreepyImage, Cancel } from './types'; | ||
export default function preload(img: CreepyImage, options: Options): Promise<Cancel>; | ||
export default function preload(img: CreepyImage, options: Options, callback: (cancel: Cancel) => void): void; |
@@ -1,2 +0,1 @@ | ||
import loadImages from 'image-promise'; | ||
const getSrcs = (options) => { | ||
@@ -10,9 +9,31 @@ const srcs = options.looks.map(({ src }) => src); | ||
}; | ||
export default function preload(img, options) { | ||
return loadImages(getSrcs(options)).then(imgs => { | ||
const loadImage = (src, callback) => { | ||
const img = new Image(); | ||
img.src = src; | ||
img.onload = img.onerror = () => { | ||
if (!img.naturalWidth) { | ||
console.error(`Creepyface was unable to load ${src}`); | ||
} | ||
delete img.onload; | ||
delete img.onerror; | ||
callback(img); | ||
}; | ||
}; | ||
const loadImages = (srcs, callback) => { | ||
const imgs = []; | ||
srcs.forEach(src => { | ||
loadImage(src, img => { | ||
imgs.push(img); | ||
if (imgs.length === srcs.length) | ||
callback(imgs); | ||
}); | ||
}); | ||
}; | ||
export default function preload(img, options, callback) { | ||
loadImages(getSrcs(options), imgs => { | ||
img.creepyfaceReachableImages = imgs; | ||
return () => { | ||
callback(() => { | ||
delete img.creepyfaceReachableImages; | ||
}; | ||
}); | ||
}); | ||
} |
export declare type Cancel = () => void; | ||
export declare type Observer<T> = (t: T) => void; | ||
export declare type Observable<T> = (observer: Observer<T>) => Cancel; | ||
export declare type CreepyImage = HTMLImageElement & { | ||
@@ -3,0 +5,0 @@ creepyfaceCancel?: Cancel; |
@@ -5,7 +5,7 @@ import noop from './noop'; | ||
if (MutationObserver) { | ||
let wasInDOM = document.body && document.body.contains(node); | ||
let wasInDOM = document.body.contains(node); | ||
if (wasInDOM) | ||
isReady(true); | ||
const observer = new MutationObserver(mutations => { | ||
const isInDOM = document.body && document.body.contains(node); | ||
const isInDOM = document.body.contains(node); | ||
if (!isInDOM && wasInDOM) { | ||
@@ -24,3 +24,3 @@ isReady(false); | ||
else { | ||
if (document.body && document.body.contains(node)) | ||
if (document.body.contains(node)) | ||
isReady(true); | ||
@@ -27,0 +27,0 @@ node.addEventListener('DOMNodeInserted', () => isReady(true), false); |
{ | ||
"name": "creepyface", | ||
"version": "5.0.0", | ||
"version": "6.0.0", | ||
"keywords": [ | ||
@@ -23,20 +23,14 @@ "face", | ||
"main": "dist/creepyface.umd.js", | ||
"module": "dist/creepyface.js", | ||
"browser": "dist/creepyface.umd.js", | ||
"types": "dist/creepyface.d.ts", | ||
"devDependencies": { | ||
"@babel/core": "^7.2.2", | ||
"@babel/plugin-proposal-class-properties": "^7.2.3", | ||
"@babel/preset-env": "^7.2.3", | ||
"@types/debounce": "^1.2.0", | ||
"@types/jest": "^23.3.11", | ||
"@types/lodash.throttle": "^4.1.4", | ||
"babel-core": "7.0.0-bridge.0", | ||
"babel-jest": "^23.6.0", | ||
"chromedriver": "^2.45.0", | ||
"babel-jest": "^24.9.0", | ||
"coveralls": "^3.0.2", | ||
"eslint": "^5.11.1", | ||
"geckodriver": "^1.14.1", | ||
"geckodriver": "^1.19.1", | ||
"husky": "^1.3.1", | ||
"jest": "^23.6.0", | ||
"jest": "^24.9.0", | ||
"lint-staged": "^8.1.0", | ||
@@ -46,5 +40,6 @@ "lolex": "^3.0.0", | ||
"prettier": "^1.15.3", | ||
"rimraf": "^2.6.3", | ||
"rollup": "^1.0.0", | ||
"rollup-plugin-babel": "^4.2.0", | ||
"rollup-plugin-browsersync": "^1.0.0", | ||
"rollup-plugin-browsersync": "^1.1.0", | ||
"rollup-plugin-commonjs": "^9.2.0", | ||
@@ -54,17 +49,12 @@ "rollup-plugin-node-resolve": "^4.0.0", | ||
"selenium-webdriver": "^4.0.0-alpha.1", | ||
"semantic-release": "^15.13.2", | ||
"simulate-event": "^1.4.0", | ||
"standard": "^12.0.1", | ||
"ts-jest": "^23.10.5", | ||
"ts-jest": "^24.2.0", | ||
"tslint": "^5.12.0", | ||
"tslint-config-prettier": "^1.17.0", | ||
"tslint-config-standard": "^8.0.1", | ||
"typescript": "^3.2.2" | ||
"typescript": "^3.7.3" | ||
}, | ||
"dependencies": { | ||
"data-attrs-to-js": "^1.1.1", | ||
"debounce": "^1.0.2", | ||
"get-element-center": "^1.1.0", | ||
"image-promise": "^5.0.1", | ||
"lodash.throttle": "^4.1.1" | ||
"get-element-center": "^1.1.0" | ||
}, | ||
@@ -78,6 +68,6 @@ "lint-staged": { | ||
"scripts": { | ||
"all": "run-s lint build test", | ||
"travis": "run-s lint build test:coverage", | ||
"all": "run-s clean lint build test", | ||
"travis": "run-s clean lint build test:coverage", | ||
"prepublishOnly": "npm run all", | ||
"start": "run-p \"build:* -- -w\"", | ||
"start": "npm run build:ts && run-p \"build:* -- -w\"", | ||
"lint": "tslint --project tsconfig.json", | ||
@@ -87,2 +77,3 @@ "build:ts": "tsc", | ||
"build": "NODE_ENV=production run-s build:*", | ||
"clean": "rimraf dist", | ||
"test": "jest", | ||
@@ -89,0 +80,0 @@ "test:coverage": "jest --coverage && cat ./coverage/lcov.info | coveralls" |
# [Creepyface](https://creepyface.io) · [](https://github.com/4lejandrito/creepyface/blob/master/LICENSE) [](https://www.npmjs.com/package/creepyface) [](https://travis-ci.org/4lejandrito/creepyface) [](https://coveralls.io/github/4lejandrito/creepyface?branch=master) | ||
Creepyface is a little JavaScript tool (<4K minified & gzipped) that makes your face look at the mouse (or fingers). It is ideal for resumes, team presentation sites, etc... | ||
Creepyface is a little JavaScript tool that makes your face look at the pointer (or a [firefly](https://github.com/4lejandrito/creepyface-firefly)). It is ideal for resumes, team presentation sites, etc... | ||
@@ -23,3 +23,3 @@ [Demo](https://creepyface.io) | ||
<img | ||
data-creepy | ||
data-creepyface | ||
src="img/face/serious.jpg" | ||
@@ -43,8 +43,8 @@ data-src-hover="img/face/crazy.jpg" | ||
```html | ||
<script type="text/javascript" src="creepyface.umd.js"></script> | ||
<script src="https://creepyface.io/creepyface.js"></script> | ||
``` | ||
Creepyface will automatically detect your image (thanks to the `data-creepy` attribute) and make it look at the mouse or fingers depending on which device you are using. | ||
Creepyface will automatically detect your image (thanks to the `data-creepyface` attribute) and make it look at the mouse or fingers depending on which device you are using. | ||
You can add as many Creepyfaces as you want as long as they all have the `data-creepy` attribute. | ||
You can add as many Creepyfaces as you want as long as they all have the `data-creepyface` attribute. | ||
@@ -90,2 +90,81 @@ If you want to stop Creepyface on a given image: | ||
## Super advanced usage | ||
Creepyface will look at the pointer by default, however custom point sources can be defined (see [firefly](https://github.com/4lejandrito/creepyface-firefly) for a real world implementation). | ||
For example, to make your face look at a random point every half a second you need to register an observable: | ||
```js | ||
import creepyface from 'creepyface' | ||
creepyface.registerObservable('random', observer => { | ||
const interval = setInterval( | ||
() => | ||
observer([ | ||
Math.random() * window.innerWidth, | ||
Math.random() * window.innerHeight | ||
]), | ||
500 | ||
) | ||
return () => { | ||
clearInterval(interval) | ||
} | ||
}) | ||
``` | ||
and consume it from the data-attribute API using the `data-points` attribute: | ||
```html | ||
<img | ||
data-creepyface | ||
data-points="random" | ||
src="img/face/serious.jpg" | ||
data-src-hover="img/face/crazy.jpg" | ||
data-src-look-0="img/face/north.jpg" | ||
data-src-look-45="img/face/north-east.jpg" | ||
data-src-look-90="img/face/east.jpg" | ||
data-src-look-135="img/face/south-east.jpg" | ||
data-src-look-180="img/face/south.jpg" | ||
data-src-look-225="img/face/south-west.jpg" | ||
data-src-look-270="img/face/west.jpg" | ||
data-src-look-315="img/face/north-west.jpg" | ||
/> | ||
``` | ||
or pass it programmatically: | ||
```js | ||
const img = document.querySelector('img#face') | ||
creepyface(img, { | ||
// An observable that provides the points to look at | ||
points: observer => { | ||
const interval = setInterval( | ||
() => | ||
observer([ | ||
Math.random() * window.innerWidth, | ||
Math.random() * window.innerHeight | ||
]), | ||
500 | ||
) | ||
return () => { | ||
clearInterval(interval) | ||
} | ||
}, | ||
// Image URL to display on hover | ||
hover: 'img/face/crazy.jpg', | ||
// Each of the images looking at a given direction | ||
looks: [ | ||
{ angle: 0, src: 'img/face/north.jpg' }, | ||
{ angle: 45, src: 'img/face/north-east.jpg' }, | ||
{ angle: 90, src: 'img/face/east.jpg' }, | ||
{ angle: 135, src: 'img/face/south-east.jpg' }, | ||
{ angle: 180, src: 'img/face/south.jpg' }, | ||
{ angle: 225, src: 'img/face/south-west.jpg' }, | ||
{ angle: 270, src: 'img/face/west.jpg' }, | ||
{ angle: 315, src: 'img/face/north-west.jpg' } | ||
] | ||
}) | ||
``` | ||
## Developing | ||
@@ -104,1 +183,7 @@ | ||
MIT, see [LICENSE](https://github.com/4lejandrito/creepyface/blob/master/LICENSE) for details. | ||
## Big Thanks | ||
Cross-browser Testing Platform and Open Source ❤️ provided by [Sauce Labs][homepage]. | ||
[homepage]: https://saucelabs.com |
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
31166
0.7%1
-80%29
-12.12%38
11.76%489
18.69%186
84.16%- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed