├── .babelrc ├── .gitignore ├── Gruntfile.js ├── LICENSE.txt ├── README.md ├── basic-demo.html ├── bower.json ├── dist ├── touchpoint-es5.js ├── touchpoint-es5.js.map ├── touchpoint-es5.min.js └── touchpoint.js ├── index.html ├── package.json └── src └── touchpoint.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["es2015"] 3 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | *.log 3 | node_modules 4 | build 5 | *.node 6 | gh-pages 7 | components 8 | coverage 9 | *.orig 10 | .idea -------------------------------------------------------------------------------- /Gruntfile.js: -------------------------------------------------------------------------------- 1 | module.exports = function(grunt) { 2 | var bannerContent = '/* \n' + 3 | ' * <%= pkg.name %> v<%= pkg.version %> - <%= grunt.template.today("yyyy-mm-dd") %> \n' + 4 | ' * <%= pkg.description %> \n' + 5 | ' * <%= pkg.repository.url %> \n' + 6 | ' * \n' + 7 | ' * Copyright (c) <%= grunt.template.today("yyyy") %> <%= pkg.author %> \n' + 8 | ' * \n' + 9 | ' * Released under the <%= pkg.license %> license \n' + 10 | '*/ \n', 11 | name = '<%= pkg.name %>'; 12 | 13 | grunt.initConfig({ 14 | pkg: grunt.file.readJSON('package.json'), 15 | 16 | // Copy 17 | copy: { 18 | src: { 19 | nonull: true, 20 | src: 'src/touchpoint.js', 21 | dest: 'dist/touchpoint.js', 22 | }, 23 | }, 24 | // Concat 25 | concat: { 26 | options: { 27 | separator: ';', 28 | banner: bannerContent 29 | }, 30 | es5: { 31 | src: ['dist/touchpoint.js'], 32 | dest: 'dist/touchpoint-es5.js' 33 | }, 34 | es6: { 35 | src: ['src/touchpoint.js'], 36 | dest: 'dist/touchpoint.js' 37 | } 38 | }, 39 | // Babel 40 | babel: { 41 | options: { 42 | sourceMap: true, 43 | presets: ['env'] 44 | }, 45 | convert: { 46 | files: { 47 | 'dist/touchpoint-es5.js': 'dist/touchpoint.js' 48 | } 49 | } 50 | }, 51 | // Uglify 52 | uglify: { 53 | options: { 54 | banner: bannerContent 55 | }, 56 | es5: { 57 | files: { 58 | 'dist/touchpoint-es5.min.js': ['dist/touchpoint-es5.js'] 59 | } 60 | } 61 | }, 62 | // JS Hint 63 | jshint: { 64 | files: ['Gruntfile.js', 'touchpoint.js', 'test/touchpoint.js'], 65 | options: { 66 | // options here to override JSHint defaults 67 | globals: { 68 | console: true, 69 | module: true, 70 | document: true 71 | } 72 | } 73 | }, 74 | // Watch 75 | watch: { 76 | options: { 77 | livereload: true 78 | }, 79 | js: { 80 | files: [ 81 | 'dist/*.js', 82 | '*.html' 83 | ] 84 | } 85 | }, 86 | // Connect 87 | connect: { 88 | server: { 89 | options: { 90 | port: 9000, 91 | base: '.', 92 | hostname: '0.0.0.0', 93 | protocol: 'http', 94 | livereload: true, 95 | open: true 96 | } 97 | } 98 | } 99 | }); 100 | // Load Tasks 101 | grunt.loadNpmTasks('grunt-contrib-copy'); 102 | grunt.loadNpmTasks('grunt-contrib-uglify'); 103 | grunt.loadNpmTasks('grunt-contrib-jshint'); 104 | grunt.loadNpmTasks('grunt-contrib-concat'); 105 | grunt.loadNpmTasks('grunt-babel'); 106 | grunt.loadNpmTasks('grunt-contrib-connect'); 107 | grunt.loadNpmTasks('grunt-contrib-watch'); 108 | // Register Tasks 109 | grunt.registerTask('es6', ['babel']); 110 | grunt.registerTask('jsErrors', ['jshint']); 111 | grunt.registerTask('server', ['connect', 'watch']); 112 | grunt.registerTask( 113 | 'build', 114 | 'Compiles all of the assets and copies the files to the build directory.', 115 | ['copy', 'concat', 'babel', 'uglify'] 116 | ); 117 | }; -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Jonah Bitautas 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # TouchPoint.js 2 | 3 | A tiny (3.86 kb minified) vanilla JavaScript library made for in-browser HTML prototyping (as part of the UX process) that visually shows where the user clicks/taps on-screen using CSS3 transforms and transitions. TouchPoint is highly customizable, mobile ready and great for screencasting, screen recording, user testing and presentations. Library now built for ES6 and ES5. 4 | 5 | ![TouchPoint.js in action](http://lighthouseux.com/in-the-lab/lib/touchpoint-js/touchpoint-js-intro.gif "TouchPoint.js in action") 6 | 7 | **Live Demo: [View](http://lighthouseux.com/in-the-lab/lib/touchpoint-js/demo.html)** 8 | 9 | ## Installation 10 | 11 | ### ES5 12 | 13 | Download and include `touchpoint.js` or `touchpoint.min.js` in the `` or at the end of the `` (recommended) in your HTML document. There are no dependencies: 14 | 15 | ```html 16 | 17 | ``` 18 | 19 | ### ES6 20 | 21 | Same instructions as above, but you will need a polyfill to run in-browser. I use [`babel-polyfill`](https://www.npmjs.com/package/babel-polyfill) which can be installed on your machine by using NPM from your command line. 22 | 23 | ```html 24 | 25 | 26 | ``` 27 | 28 | ## Quick Start/How to Use 29 | After you load the script you simply initialize TouchPoint and add an event listener to whichever DOM element you want TouchPoint to show over: 30 | 31 | ```html 32 | 35 | ``` 36 | 37 | **That's it!** 38 | 39 | [Start clicking away on the page to see it in action.](http://lighthouseux.com/in-the-lab/lib/touchpoint-js/basic-demo.html) 40 | 41 | ## Options 42 | TouchPoint is customizable. There are a number of options that you have access to to customize the look for your needs. It's important that these options be defined before you initialize TouchPoint with `TouchPoint.init()`. Otherwise, your updates won't show. 43 | 44 | #### Color 45 | Change the default color of TouchPoint. Any valid CSS color can be used. 46 | 47 | Default value: `'#FFF'` 48 | ```html 49 | TouchPoint.color = 'red'; 50 | ``` 51 | 52 | #### DOM 53 | Change the default DOM element that TouchPoint will be active over. Any valid selector can be used: element name, CSS class or ID. If you want TouchPoint to only show on a specific element, make sure that that element is set to `overflow: visible`, otherwise TouchPoint will get clipped. 54 | 55 | Default value: `window` 56 | ```html 57 | TouchPoint.dom = elementVarId; 58 | ``` 59 | 60 | or (recommended) 61 | 62 | ```html 63 | TouchPoint.init(elementVarId); 64 | ``` 65 | 66 | #### Opacity 67 | Change the opacity of the TouchPoint. You can use any value between `0` and `1`. 68 | 69 | Default value: `0.8` 70 | ```html 71 | TouchPoint.opacity = 0.5; 72 | ``` 73 | 74 | #### Scale 75 | Change the final scale of the TouchPoint. This value can range from `0` and beyond. 76 | 77 | Default value: `8` 78 | ```html 79 | TouchPoint.scale = 14; 80 | ``` 81 | 82 | #### Size 83 | Change the initial size of the TouchPoint. This value is `px`. 84 | 85 | Default value: `20` 86 | ```html 87 | TouchPoint.size = 5; 88 | ``` 89 | 90 | ## Performance 91 | Performance should not be an issue because each individual TouchPoint element is dynamically created and then automatically removed from the DOM after being used. 92 | 93 | The animation is handled via the `requestAnimationFrame` function that is available in all current browsers. This has better overall performance than using `setTimeout`, which doesn't redraw consistently. 94 | 95 | ## Release Notes 96 | **TouchPoint.js 1.0.1** 97 | – Updated to ES6 and reintroduced quick-clicking. 98 | 99 | **TouchPoint.js 1.0** 100 | – Initial Release 101 | 102 | *This is in active development.* 103 | 104 | ## Roadmap 105 | - [X] Reintroduce quick clicking. 106 | - [ ] Add keyboard shortcut to enable/disable script. 107 | 108 | ## Feedback 109 | If you discover any issues please first check [open/past issues](https://github.com/jonahvsweb/touchpoint-js/issues) or [open a new issue](https://github.com/jonahvsweb/touchpoint-js/issues/new) if one does not already exist. 110 | 111 | If you have any questions regarding usage, please send a message me here on GitHub, [@jonahvsweb](https://twitter.com/jonahvsweb) on Twitter or from my website, [jonahvsweb.com](http://jonahvsweb.com). -------------------------------------------------------------------------------- /basic-demo.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | TouchPoint.js Basic Demo 4 | 12 | 13 | 14 | 15 | 16 | 21 | 22 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "touchpoint", 3 | "description": "Visually show taps/cicks on HTML prototypes.", 4 | "main": "touchpoint.js", 5 | "homepage": "https://github.com/jonahvsweb/touchpoint-js", 6 | "license": "MIT", 7 | "authors": [ 8 | "Jonah Bitautas " 9 | ], 10 | "keywords": [ 11 | "touchpoint", 12 | "touchpointjs", 13 | "tap", 14 | "mobile", 15 | "prototype", 16 | "prototyping" 17 | ], 18 | "ignore": [ 19 | "index.html", 20 | "AUTHORS.txt", 21 | "Gruntfile.js", 22 | "package.json" 23 | ] 24 | } -------------------------------------------------------------------------------- /dist/touchpoint-es5.js: -------------------------------------------------------------------------------- 1 | /* 2 | * TouchPoint.js v1.0.1 - 2017-09-30 3 | * A JavaScript library that visually shows taps/cicks on HTML prototypes 4 | * https://github.com/jonahvsweb/touchpoint-js 5 | * 6 | * Copyright (c) 2017 Jonah Bitautas 7 | * 8 | * Released under the MIT license 9 | */ 10 | 'use strict'; 11 | 12 | var TouchPoint; 13 | 14 | (function () { 15 | 16 | TouchPoint = { 17 | 18 | clickTap: 'ontouchstart' in window ? 'touchstart' : 'click', 19 | dom: '', 20 | styleEl: '', 21 | color: '#FFF', 22 | opacity: 0.8, 23 | size: 20, 24 | scale: 8, 25 | tp: '', 26 | animIds: {}, 27 | 28 | init: function init() { 29 | var dom = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 'body'; 30 | 31 | 32 | window.addEventListener('load', this.setupAnimation, false); 33 | this.dom = document.querySelector(dom); 34 | 35 | this.createCss('.tp-init', 'position: absolute; width: ' + this.size + 'px; height: ' + this.size + 'px; background-color: ' + this.color + '; opacity: ' + this.opacity + '; border-radius: 20px; -ms-transform: scale(0.5); -webkit-transform: scale(0.5); -moz-transform: scale(0.5); -o-transform: scale(0.5); transform: scale(0.5); -ms-transition: all 0.5s ease-out; -webkit-transition: all 0.5s ease-out; -moz-transition: all 0.5s ease-out; -o-transition: all 0.5s ease-out; transition: all 0.5s ease-out; z-index: 9999;').createCss('.tp-anim', '-ms-transform: scale(' + this.scale + '); -webkit-transform: scale(' + this.scale + '); -moz-transform: scale(' + this.scale + '); -o-transform: scale(' + this.scale + '); transform: scale(' + this.scale + '); opacity: 0;'); 36 | this.dom.addEventListener(this.clickTap, this.create, false); 37 | }, 38 | create: function create(e) { 39 | 40 | TouchPoint.tp = document.createElement('div'); 41 | TouchPoint.tp.setAttribute('id', 'touchpoint'); 42 | 43 | if (TouchPoint.getMobileOS() === 'iOS') { 44 | TouchPoint.tp.style.left = e.pageX - TouchPoint.size * 0.5 + 'px'; 45 | TouchPoint.tp.style.top = e.pageY - TouchPoint.size * 0.5 + 'px'; 46 | } else if (TouchPoint.getMobileOS() === 'Android') { 47 | TouchPoint.tp.style.left = e.touches[0].pageX - TouchPoint.size * 0.5 + 'px'; 48 | TouchPoint.tp.style.top = e.touches[0].pageY - TouchPoint.size * 0.5 + 'px'; 49 | } else if (e.touches && e.touches.length > 0) { 50 | TouchPoint.tp.style.left = e.touches[0].pageX - TouchPoint.size * 0.5 + 'px'; 51 | TouchPoint.tp.style.top = e.touches[0].pageY - TouchPoint.size * 0.5 + 'px'; 52 | } else { 53 | TouchPoint.tp.style.left = e.clientX - TouchPoint.size * 0.5 + 'px'; 54 | TouchPoint.tp.style.top = e.clientY - TouchPoint.size * 0.5 + 'px'; 55 | } 56 | TouchPoint.tp.className = 'tp-init'; 57 | 58 | document.body.appendChild(TouchPoint.tp); 59 | 60 | window.requestNextAnimationFrame(function () { 61 | TouchPoint.tp.className += ' tp-anim'; 62 | }); 63 | TouchPoint.tp.addEventListener('transitionend', TouchPoint.gc, false); 64 | }, 65 | gc: function gc(e) { 66 | 67 | var currTP = document.querySelector('#touchpoint'); 68 | 69 | TouchPoint.dom.removeEventListener(TouchPoint.clickTap, TouchPoint.create, false); 70 | 71 | if (currTP) { 72 | 73 | e.target.removeEventListener('transitionend', TouchPoint.gc, false); 74 | document.body.removeChild(currTP); 75 | TouchPoint.dom.addEventListener(TouchPoint.clickTap, TouchPoint.create, false); 76 | } 77 | }, 78 | createCss: function createCss(name, rules) { 79 | 80 | var head = document.head || document.getElementsByTagName('head')[0]; 81 | 82 | for (var i = 0; i < head.childNodes.length; i = i + 1) { 83 | if (head.getElementsByTagName('style')[i].tagName.toLowerCase() === 'style') { 84 | head.getElementsByTagName('style')[i].innerHTML += name + ' { ' + rules + ' }'; 85 | TouchPoint.styleEl = head.getElementsByTagName('style')[i]; 86 | break; 87 | } else { 88 | TouchPoint.styleEl = document.createElement('style'); 89 | TouchPoint.styleEl.type = 'text/css'; 90 | TouchPoint.styleEl.innerHTML = name + ' { ' + rules + ' }'; 91 | head.appendChild(TouchPoint.styleEl); 92 | break; 93 | } 94 | } 95 | return TouchPoint; 96 | }, 97 | getMobileOS: function getMobileOS() { 98 | 99 | var userAgent = navigator.userAgent || navigator.vendor || window.opera; 100 | 101 | if (userAgent.match(/iPad/i) || userAgent.match(/iPhone/i) || userAgent.match(/iPod/i)) { 102 | return 'iOS'; 103 | } else if (userAgent.match(/Android/i)) { 104 | return 'Android'; 105 | } else { 106 | return 'unknown'; 107 | } 108 | }, 109 | requestId: function requestId() { 110 | 111 | var id = void 0; 112 | 113 | do { 114 | id = Math.floor(Math.random() * 1E9); 115 | } while (id in TouchPoint.animIds); 116 | 117 | return id; 118 | }, 119 | setupAnimation: function setupAnimation(e) { 120 | 121 | if (!window.requestNextAnimationFrame) { 122 | 123 | window.requestNextAnimationFrame = function (callback, element) { 124 | 125 | var id = TouchPoint.requestId(); 126 | 127 | TouchPoint.animIds[id] = requestAnimationFrame(function () { 128 | 129 | TouchPoint.animIds[id] = requestAnimationFrame(function (ts) { 130 | 131 | delete TouchPoint.animIds[id]; 132 | callback(ts); 133 | }, element); 134 | }, element); 135 | 136 | return id; 137 | }; 138 | } 139 | 140 | if (!window.cancelNextAnimationFrame) { 141 | 142 | window.cancelNextAnimationFrame = function (id) { 143 | 144 | if (TouchPoint.animIds[id]) { 145 | 146 | cancelAnimationFrame(TouchPoint.animIds[id]); 147 | delete TouchPoint.animIds[id]; 148 | } 149 | }; 150 | } 151 | } 152 | }; 153 | })(); 154 | //# sourceMappingURL=touchpoint-es5.js.map 155 | -------------------------------------------------------------------------------- /dist/touchpoint-es5.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":["touchpoint.js"],"names":["TouchPoint","clickTap","window","dom","styleEl","color","opacity","size","scale","tp","animIds","init","addEventListener","setupAnimation","document","querySelector","createCss","create","e","createElement","setAttribute","getMobileOS","style","left","pageX","top","pageY","touches","length","clientX","clientY","className","body","appendChild","requestNextAnimationFrame","gc","currTP","removeEventListener","target","removeChild","name","rules","head","getElementsByTagName","i","childNodes","tagName","toLowerCase","innerHTML","type","userAgent","navigator","vendor","opera","match","requestId","id","Math","floor","random","callback","element","requestAnimationFrame","ts","cancelNextAnimationFrame","cancelAnimationFrame"],"mappings":"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","file":"touchpoint-es5.js","sourcesContent":["/* \n * TouchPoint.js v1.0.1 - 2017-09-30 \n * A JavaScript library that visually shows taps/cicks on HTML prototypes \n * https://github.com/jonahvsweb/touchpoint-js \n * \n * Copyright (c) 2017 Jonah Bitautas \n * \n * Released under the MIT license \n*/ \n'use strict';\n\nvar TouchPoint;\n\n(() => {\n\n TouchPoint = {\n\n clickTap: ('ontouchstart' in window ? 'touchstart' : 'click'),\n dom: '', \n styleEl: '',\n color: '#FFF', \n opacity: 0.8, \n size: 20, \n scale: 8,\n tp: '', \n animIds: {}, \n\n init(dom = 'body') {\n\n window.addEventListener('load', this.setupAnimation, false);\n this.dom = document.querySelector(dom);\n \n this\n .createCss('.tp-init', \n `position: absolute; width: ${ this.size }px; height: ${ this.size }px; background-color: ${ this.color }; opacity: ${ this.opacity }; border-radius: 20px; -ms-transform: scale(0.5); -webkit-transform: scale(0.5); -moz-transform: scale(0.5); -o-transform: scale(0.5); transform: scale(0.5); -ms-transition: all 0.5s ease-out; -webkit-transition: all 0.5s ease-out; -moz-transition: all 0.5s ease-out; -o-transition: all 0.5s ease-out; transition: all 0.5s ease-out; z-index: 9999;`)\n .createCss('.tp-anim', \n `-ms-transform: scale(${ this.scale }); -webkit-transform: scale(${ this.scale }); -moz-transform: scale(${ this.scale }); -o-transform: scale(${ this.scale }); transform: scale(${ this.scale }); opacity: 0;`\n );\n this.dom.addEventListener(this.clickTap, this.create, false);\n },\n\n create(e) {\n \n TouchPoint.tp = document.createElement('div');\n TouchPoint.tp.setAttribute('id', 'touchpoint');\n\n if(TouchPoint.getMobileOS() === 'iOS') {\n TouchPoint.tp.style.left = `${ e.pageX - (TouchPoint.size * 0.5) }px`;\n TouchPoint.tp.style.top = `${ e.pageY - (TouchPoint.size * 0.5) }px`;\n } else if (TouchPoint.getMobileOS() === 'Android') {\n TouchPoint.tp.style.left = `${ e.touches[0].pageX - (TouchPoint.size * 0.5) }px`;\n TouchPoint.tp.style.top = `${ e.touches[0].pageY - (TouchPoint.size * 0.5) }px`;\n } else if (e.touches && e.touches.length > 0) { \n TouchPoint.tp.style.left = `${ e.touches[0].pageX - (TouchPoint.size * 0.5) }px`;\n TouchPoint.tp.style.top = `${ e.touches[0].pageY - (TouchPoint.size * 0.5) }px`;\n } else { \n TouchPoint.tp.style.left = `${ e.clientX - (TouchPoint.size * 0.5) }px`;\n TouchPoint.tp.style.top = `${ e.clientY - (TouchPoint.size * 0.5) }px`;\n }\n TouchPoint.tp.className = 'tp-init';\n\n document.body.appendChild(TouchPoint.tp);\n\n window.requestNextAnimationFrame(() => {\n TouchPoint.tp.className += ' tp-anim';\n });\n TouchPoint.tp.addEventListener('transitionend', TouchPoint.gc, false);\n },\n\n gc(e) {\n\n let currTP = document.querySelector('#touchpoint');\n\n TouchPoint.dom.removeEventListener(TouchPoint.clickTap, TouchPoint.create, false);\n\n if(currTP) {\n\n e.target.removeEventListener('transitionend', TouchPoint.gc, false);\n document.body.removeChild(currTP);\n TouchPoint.dom.addEventListener(TouchPoint.clickTap, TouchPoint.create, false);\n }\n },\n\n createCss(name, rules) {\n\n let head = document.head || document.getElementsByTagName('head')[0];\n\n for(let i = 0; i < head.childNodes.length; i = i + 1) {\n if(head.getElementsByTagName('style')[i].tagName.toLowerCase() === 'style') {\n head.getElementsByTagName('style')[i].innerHTML += name + ' { ' + rules + ' }';\n TouchPoint.styleEl = head.getElementsByTagName('style')[i];\n break;\n } else {\n TouchPoint.styleEl = document.createElement('style');\n TouchPoint.styleEl.type = 'text/css';\n TouchPoint.styleEl.innerHTML = name + ' { ' + rules + ' }';\n head.appendChild(TouchPoint.styleEl);\n break;\n }\n }\n return TouchPoint;\n },\n\n getMobileOS() {\n \n let userAgent = navigator.userAgent || navigator.vendor || window.opera;\n\n if(userAgent.match( /iPad/i ) || userAgent.match( /iPhone/i ) || userAgent.match( /iPod/i )) {\n return 'iOS';\n } else if(userAgent.match( /Android/i )) {\n return 'Android';\n } else {\n return 'unknown';\n }\n }, \n\n requestId() {\n\n let id;\n \n do {\n id = Math.floor(Math.random() * 1E9);\n } while (id in TouchPoint.animIds);\n\n return id;\n }, \n\n setupAnimation(e) {\n\n if (!window.requestNextAnimationFrame) {\n \n window.requestNextAnimationFrame = (callback, element) => {\n \n let id = TouchPoint.requestId();\n\n TouchPoint.animIds[id] = requestAnimationFrame(() => {\n \n TouchPoint.animIds[id] = requestAnimationFrame((ts) => {\n \n delete TouchPoint.animIds[id];\n callback(ts);\n \n }, element);\n \n }, element);\n\n return id;\n };\n }\n\n if (!window.cancelNextAnimationFrame) {\n \n window.cancelNextAnimationFrame = (id) => {\n \n if (TouchPoint.animIds[id]) {\n \n cancelAnimationFrame(TouchPoint.animIds[id]);\n delete TouchPoint.animIds[id];\n }\n }\n }\n }\n }\n})();"]} -------------------------------------------------------------------------------- /dist/touchpoint-es5.min.js: -------------------------------------------------------------------------------- 1 | /* 2 | * TouchPoint.js v1.0.1 - 2017-09-30 3 | * A JavaScript library that visually shows taps/cicks on HTML prototypes 4 | * https://github.com/jonahvsweb/touchpoint-js 5 | * 6 | * Copyright (c) 2017 Jonah Bitautas 7 | * 8 | * Released under the MIT license 9 | */ 10 | "use strict";var TouchPoint;!function(){TouchPoint={clickTap:"ontouchstart"in window?"touchstart":"click",dom:"",styleEl:"",color:"#FFF",opacity:.8,size:20,scale:8,tp:"",animIds:{},init:function(){var a=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"body";window.addEventListener("load",this.setupAnimation,!1),this.dom=document.querySelector(a),this.createCss(".tp-init","position: absolute; width: "+this.size+"px; height: "+this.size+"px; background-color: "+this.color+"; opacity: "+this.opacity+"; border-radius: 20px; -ms-transform: scale(0.5); -webkit-transform: scale(0.5); -moz-transform: scale(0.5); -o-transform: scale(0.5); transform: scale(0.5); -ms-transition: all 0.5s ease-out; -webkit-transition: all 0.5s ease-out; -moz-transition: all 0.5s ease-out; -o-transition: all 0.5s ease-out; transition: all 0.5s ease-out; z-index: 9999;").createCss(".tp-anim","-ms-transform: scale("+this.scale+"); -webkit-transform: scale("+this.scale+"); -moz-transform: scale("+this.scale+"); -o-transform: scale("+this.scale+"); transform: scale("+this.scale+"); opacity: 0;"),this.dom.addEventListener(this.clickTap,this.create,!1)},create:function(a){TouchPoint.tp=document.createElement("div"),TouchPoint.tp.setAttribute("id","touchpoint"),"iOS"===TouchPoint.getMobileOS()?(TouchPoint.tp.style.left=a.pageX-.5*TouchPoint.size+"px",TouchPoint.tp.style.top=a.pageY-.5*TouchPoint.size+"px"):"Android"===TouchPoint.getMobileOS()?(TouchPoint.tp.style.left=a.touches[0].pageX-.5*TouchPoint.size+"px",TouchPoint.tp.style.top=a.touches[0].pageY-.5*TouchPoint.size+"px"):a.touches&&a.touches.length>0?(TouchPoint.tp.style.left=a.touches[0].pageX-.5*TouchPoint.size+"px",TouchPoint.tp.style.top=a.touches[0].pageY-.5*TouchPoint.size+"px"):(TouchPoint.tp.style.left=a.clientX-.5*TouchPoint.size+"px",TouchPoint.tp.style.top=a.clientY-.5*TouchPoint.size+"px"),TouchPoint.tp.className="tp-init",document.body.appendChild(TouchPoint.tp),window.requestNextAnimationFrame(function(){TouchPoint.tp.className+=" tp-anim"}),TouchPoint.tp.addEventListener("transitionend",TouchPoint.gc,!1)},gc:function(a){var b=document.querySelector("#touchpoint");TouchPoint.dom.removeEventListener(TouchPoint.clickTap,TouchPoint.create,!1),b&&(a.target.removeEventListener("transitionend",TouchPoint.gc,!1),document.body.removeChild(b),TouchPoint.dom.addEventListener(TouchPoint.clickTap,TouchPoint.create,!1))},createCss:function(a,b){for(var c=document.head||document.getElementsByTagName("head")[0],d=0;d 7 | * 8 | * Released under the MIT license 9 | */ 10 | 'use strict'; 11 | 12 | var TouchPoint; 13 | 14 | (() => { 15 | 16 | TouchPoint = { 17 | 18 | clickTap: ('ontouchstart' in window ? 'touchstart' : 'click'), 19 | dom: '', 20 | styleEl: '', 21 | color: '#FFF', 22 | opacity: 0.8, 23 | size: 20, 24 | scale: 8, 25 | tp: '', 26 | animIds: {}, 27 | 28 | init(dom = 'body') { 29 | 30 | window.addEventListener('load', this.setupAnimation, false); 31 | this.dom = document.querySelector(dom); 32 | 33 | this 34 | .createCss('.tp-init', 35 | `position: absolute; width: ${ this.size }px; height: ${ this.size }px; background-color: ${ this.color }; opacity: ${ this.opacity }; border-radius: 20px; -ms-transform: scale(0.5); -webkit-transform: scale(0.5); -moz-transform: scale(0.5); -o-transform: scale(0.5); transform: scale(0.5); -ms-transition: all 0.5s ease-out; -webkit-transition: all 0.5s ease-out; -moz-transition: all 0.5s ease-out; -o-transition: all 0.5s ease-out; transition: all 0.5s ease-out; z-index: 9999;`) 36 | .createCss('.tp-anim', 37 | `-ms-transform: scale(${ this.scale }); -webkit-transform: scale(${ this.scale }); -moz-transform: scale(${ this.scale }); -o-transform: scale(${ this.scale }); transform: scale(${ this.scale }); opacity: 0;` 38 | ); 39 | this.dom.addEventListener(this.clickTap, this.create, false); 40 | }, 41 | 42 | create(e) { 43 | 44 | TouchPoint.tp = document.createElement('div'); 45 | TouchPoint.tp.setAttribute('id', 'touchpoint'); 46 | 47 | if(TouchPoint.getMobileOS() === 'iOS') { 48 | TouchPoint.tp.style.left = `${ e.pageX - (TouchPoint.size * 0.5) }px`; 49 | TouchPoint.tp.style.top = `${ e.pageY - (TouchPoint.size * 0.5) }px`; 50 | } else if (TouchPoint.getMobileOS() === 'Android') { 51 | TouchPoint.tp.style.left = `${ e.touches[0].pageX - (TouchPoint.size * 0.5) }px`; 52 | TouchPoint.tp.style.top = `${ e.touches[0].pageY - (TouchPoint.size * 0.5) }px`; 53 | } else if (e.touches && e.touches.length > 0) { 54 | TouchPoint.tp.style.left = `${ e.touches[0].pageX - (TouchPoint.size * 0.5) }px`; 55 | TouchPoint.tp.style.top = `${ e.touches[0].pageY - (TouchPoint.size * 0.5) }px`; 56 | } else { 57 | TouchPoint.tp.style.left = `${ e.clientX - (TouchPoint.size * 0.5) }px`; 58 | TouchPoint.tp.style.top = `${ e.clientY - (TouchPoint.size * 0.5) }px`; 59 | } 60 | TouchPoint.tp.className = 'tp-init'; 61 | 62 | document.body.appendChild(TouchPoint.tp); 63 | 64 | window.requestNextAnimationFrame(() => { 65 | TouchPoint.tp.className += ' tp-anim'; 66 | }); 67 | TouchPoint.tp.addEventListener('transitionend', TouchPoint.gc, false); 68 | }, 69 | 70 | gc(e) { 71 | 72 | let currTP = document.querySelector('#touchpoint'); 73 | 74 | TouchPoint.dom.removeEventListener(TouchPoint.clickTap, TouchPoint.create, false); 75 | 76 | if(currTP) { 77 | 78 | e.target.removeEventListener('transitionend', TouchPoint.gc, false); 79 | document.body.removeChild(currTP); 80 | TouchPoint.dom.addEventListener(TouchPoint.clickTap, TouchPoint.create, false); 81 | } 82 | }, 83 | 84 | createCss(name, rules) { 85 | 86 | let head = document.head || document.getElementsByTagName('head')[0]; 87 | 88 | for(let i = 0; i < head.childNodes.length; i = i + 1) { 89 | if(head.getElementsByTagName('style')[i].tagName.toLowerCase() === 'style') { 90 | head.getElementsByTagName('style')[i].innerHTML += name + ' { ' + rules + ' }'; 91 | TouchPoint.styleEl = head.getElementsByTagName('style')[i]; 92 | break; 93 | } else { 94 | TouchPoint.styleEl = document.createElement('style'); 95 | TouchPoint.styleEl.type = 'text/css'; 96 | TouchPoint.styleEl.innerHTML = name + ' { ' + rules + ' }'; 97 | head.appendChild(TouchPoint.styleEl); 98 | break; 99 | } 100 | } 101 | return TouchPoint; 102 | }, 103 | 104 | getMobileOS() { 105 | 106 | let userAgent = navigator.userAgent || navigator.vendor || window.opera; 107 | 108 | if(userAgent.match( /iPad/i ) || userAgent.match( /iPhone/i ) || userAgent.match( /iPod/i )) { 109 | return 'iOS'; 110 | } else if(userAgent.match( /Android/i )) { 111 | return 'Android'; 112 | } else { 113 | return 'unknown'; 114 | } 115 | }, 116 | 117 | requestId() { 118 | 119 | let id; 120 | 121 | do { 122 | id = Math.floor(Math.random() * 1E9); 123 | } while (id in TouchPoint.animIds); 124 | 125 | return id; 126 | }, 127 | 128 | setupAnimation(e) { 129 | 130 | if (!window.requestNextAnimationFrame) { 131 | 132 | window.requestNextAnimationFrame = (callback, element) => { 133 | 134 | let id = TouchPoint.requestId(); 135 | 136 | TouchPoint.animIds[id] = requestAnimationFrame(() => { 137 | 138 | TouchPoint.animIds[id] = requestAnimationFrame((ts) => { 139 | 140 | delete TouchPoint.animIds[id]; 141 | callback(ts); 142 | 143 | }, element); 144 | 145 | }, element); 146 | 147 | return id; 148 | }; 149 | } 150 | 151 | if (!window.cancelNextAnimationFrame) { 152 | 153 | window.cancelNextAnimationFrame = (id) => { 154 | 155 | if (TouchPoint.animIds[id]) { 156 | 157 | cancelAnimationFrame(TouchPoint.animIds[id]); 158 | delete TouchPoint.animIds[id]; 159 | } 160 | } 161 | } 162 | } 163 | } 164 | })(); -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | TouchPoint.js - A Javascript library that shows taps/clicks for HTML prototypes on desktop and mobile 4 | 5 | 6 | 21 | 22 | 23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 | 48 | 49 | 55 | 63 | 64 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "TouchPoint.js", 3 | "version": "1.0.1", 4 | "description": "A JavaScript library that visually shows taps/cicks on HTML prototypes", 5 | "devDependencies": { 6 | "babel-core": "6.26.0", 7 | "babel-polyfill": "6.26.0", 8 | "babel-preset-env": "1.6.0", 9 | "babel-preset-es2015": "6.24.1", 10 | "connect-livereload": "0.6.0", 11 | "grunt": ">=1.3.0", 12 | "grunt-babel": "7.0.0", 13 | "grunt-contrib-clean": "^0.6.0", 14 | "grunt-contrib-concat": "^0.5.1", 15 | "grunt-contrib-connect": "1.0.2", 16 | "grunt-contrib-copy": "1.0.0", 17 | "grunt-contrib-jshint": "^0.11.2", 18 | "grunt-contrib-uglify": "^0.9.1", 19 | "grunt-contrib-watch": "1.0.0", 20 | "grunt-serve": "0.1.6" 21 | }, 22 | "repository": { 23 | "type": "git", 24 | "url": "https://github.com/jonahvsweb/touchpoint-js" 25 | }, 26 | "author": "Jonah Bitautas", 27 | "license": "MIT", 28 | "bugs": { 29 | "url": "" 30 | }, 31 | "homepage": "https://github.com/jonahvsweb/touchpoint-js" 32 | } 33 | -------------------------------------------------------------------------------- /src/touchpoint.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var TouchPoint; 4 | 5 | (() => { 6 | 7 | TouchPoint = { 8 | 9 | clickTap: ('ontouchstart' in window ? 'touchstart' : 'click'), 10 | dom: '', 11 | styleEl: '', 12 | color: '#FFF', 13 | opacity: 0.8, 14 | size: 20, 15 | scale: 8, 16 | tp: '', 17 | animIds: {}, 18 | 19 | init(dom = 'body') { 20 | 21 | window.addEventListener('load', this.setupAnimation, false); 22 | this.dom = document.querySelector(dom); 23 | 24 | this 25 | .createCss('.tp-init', 26 | `position: absolute; width: ${ this.size }px; height: ${ this.size }px; background-color: ${ this.color }; opacity: ${ this.opacity }; border-radius: 20px; -ms-transform: scale(0.5); -webkit-transform: scale(0.5); -moz-transform: scale(0.5); -o-transform: scale(0.5); transform: scale(0.5); -ms-transition: all 0.5s ease-out; -webkit-transition: all 0.5s ease-out; -moz-transition: all 0.5s ease-out; -o-transition: all 0.5s ease-out; transition: all 0.5s ease-out; z-index: 9999;`) 27 | .createCss('.tp-anim', 28 | `-ms-transform: scale(${ this.scale }); -webkit-transform: scale(${ this.scale }); -moz-transform: scale(${ this.scale }); -o-transform: scale(${ this.scale }); transform: scale(${ this.scale }); opacity: 0;` 29 | ); 30 | this.dom.addEventListener(this.clickTap, this.create, false); 31 | }, 32 | 33 | create(e) { 34 | 35 | TouchPoint.tp = document.createElement('div'); 36 | TouchPoint.tp.setAttribute('id', 'touchpoint'); 37 | 38 | if(TouchPoint.getMobileOS() === 'iOS') { 39 | TouchPoint.tp.style.left = `${ e.pageX - (TouchPoint.size * 0.5) }px`; 40 | TouchPoint.tp.style.top = `${ e.pageY - (TouchPoint.size * 0.5) }px`; 41 | } else if (TouchPoint.getMobileOS() === 'Android') { 42 | TouchPoint.tp.style.left = `${ e.touches[0].pageX - (TouchPoint.size * 0.5) }px`; 43 | TouchPoint.tp.style.top = `${ e.touches[0].pageY - (TouchPoint.size * 0.5) }px`; 44 | } else if (e.touches && e.touches.length > 0) { 45 | TouchPoint.tp.style.left = `${ e.touches[0].pageX - (TouchPoint.size * 0.5) }px`; 46 | TouchPoint.tp.style.top = `${ e.touches[0].pageY - (TouchPoint.size * 0.5) }px`; 47 | } else { 48 | TouchPoint.tp.style.left = `${ e.clientX - (TouchPoint.size * 0.5) }px`; 49 | TouchPoint.tp.style.top = `${ e.clientY - (TouchPoint.size * 0.5) }px`; 50 | } 51 | TouchPoint.tp.className = 'tp-init'; 52 | 53 | document.body.appendChild(TouchPoint.tp); 54 | 55 | window.requestNextAnimationFrame(() => { 56 | TouchPoint.tp.className += ' tp-anim'; 57 | }); 58 | TouchPoint.tp.addEventListener('transitionend', TouchPoint.gc, false); 59 | }, 60 | 61 | gc(e) { 62 | 63 | let currTP = document.querySelector('#touchpoint'); 64 | 65 | TouchPoint.dom.removeEventListener(TouchPoint.clickTap, TouchPoint.create, false); 66 | 67 | if(currTP) { 68 | 69 | e.target.removeEventListener('transitionend', TouchPoint.gc, false); 70 | document.body.removeChild(currTP); 71 | TouchPoint.dom.addEventListener(TouchPoint.clickTap, TouchPoint.create, false); 72 | } 73 | }, 74 | 75 | createCss(name, rules) { 76 | 77 | let head = document.head || document.getElementsByTagName('head')[0]; 78 | 79 | for(let i = 0; i < head.childNodes.length; i = i + 1) { 80 | if(head.getElementsByTagName('style')[i].tagName.toLowerCase() === 'style') { 81 | head.getElementsByTagName('style')[i].innerHTML += name + ' { ' + rules + ' }'; 82 | TouchPoint.styleEl = head.getElementsByTagName('style')[i]; 83 | break; 84 | } else { 85 | TouchPoint.styleEl = document.createElement('style'); 86 | TouchPoint.styleEl.type = 'text/css'; 87 | TouchPoint.styleEl.innerHTML = name + ' { ' + rules + ' }'; 88 | head.appendChild(TouchPoint.styleEl); 89 | break; 90 | } 91 | } 92 | return TouchPoint; 93 | }, 94 | 95 | getMobileOS() { 96 | 97 | let userAgent = navigator.userAgent || navigator.vendor || window.opera; 98 | 99 | if(userAgent.match( /iPad/i ) || userAgent.match( /iPhone/i ) || userAgent.match( /iPod/i )) { 100 | return 'iOS'; 101 | } else if(userAgent.match( /Android/i )) { 102 | return 'Android'; 103 | } else { 104 | return 'unknown'; 105 | } 106 | }, 107 | 108 | requestId() { 109 | 110 | let id; 111 | 112 | do { 113 | id = Math.floor(Math.random() * 1E9); 114 | } while (id in TouchPoint.animIds); 115 | 116 | return id; 117 | }, 118 | 119 | setupAnimation(e) { 120 | 121 | if (!window.requestNextAnimationFrame) { 122 | 123 | window.requestNextAnimationFrame = (callback, element) => { 124 | 125 | let id = TouchPoint.requestId(); 126 | 127 | TouchPoint.animIds[id] = requestAnimationFrame(() => { 128 | 129 | TouchPoint.animIds[id] = requestAnimationFrame((ts) => { 130 | 131 | delete TouchPoint.animIds[id]; 132 | callback(ts); 133 | 134 | }, element); 135 | 136 | }, element); 137 | 138 | return id; 139 | }; 140 | } 141 | 142 | if (!window.cancelNextAnimationFrame) { 143 | 144 | window.cancelNextAnimationFrame = (id) => { 145 | 146 | if (TouchPoint.animIds[id]) { 147 | 148 | cancelAnimationFrame(TouchPoint.animIds[id]); 149 | delete TouchPoint.animIds[id]; 150 | } 151 | } 152 | } 153 | } 154 | } 155 | })(); --------------------------------------------------------------------------------