├── .babelrc ├── .github └── ISSUE_TEMPLATE.md ├── .gitignore ├── CONTRIBUTING.md ├── License ├── README.md ├── bower.json ├── dist ├── jquery.pressure.js ├── jquery.pressure.min.js ├── pressure.js └── pressure.min.js ├── docs ├── CNAME ├── License.md ├── css │ ├── cssdevices.css │ ├── poet.css │ ├── prism.css │ └── style.css ├── documentation.html ├── favicon.png ├── img │ ├── icons │ │ ├── devices.svg │ │ ├── drag.svg │ │ ├── flask.svg │ │ └── stopwatch.svg │ ├── pickle.jpg │ ├── pressure-heart.gif │ ├── pressure.gif │ └── sierra.jpg ├── index.html ├── js │ ├── app.js │ ├── cssdevices.js │ ├── pressure.js │ └── prism.js └── press.html ├── examples ├── example.js └── index.html ├── gulpfile.js ├── package-lock.json ├── package.json └── src ├── adapters ├── adapter.js ├── adapter_3d_touch.js ├── adapter_force_touch.js └── adapter_pointer.js ├── config.js ├── element.js ├── helpers.js ├── jquery_pressure.js └── pressure.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["@babel/preset-env"] 3 | } 4 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | _add issue description here_ 2 | 3 | ______________ 4 | **Please provide information about the device you are seeing the issue on** 5 | Device is a [year] [device] [model] 6 | Operating system is [OS] [version] 7 | Browser is [browser] [version] 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | node_modules/ 3 | .DS_Store 4 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # How to Contribute 2 | 3 | Yay, you're interested in helping this thing suck less. Thank you! 4 | 5 | ## Project Layout 6 | 7 | - `src/` - JavaScript Source 8 | - `dist/` - Compiled and Minified 9 | - `examples/` - Example test cases in browser 10 | 11 | ## Having a problem? 12 | 13 | A **great** way to start a discussion about a potential issue is to 14 | submit an issue with the device, OS, and browser info. 15 | 16 | ## Have an idea to make it better? 17 | 18 | Again, guard your time and effort. Make sure that you don't spend a lot 19 | of time on an improvement without talking through it first. 20 | 21 | ## Getting to work 22 | 23 | ```sh 24 | npm install --dev 25 | gulp watch 26 | ``` 27 | When you edit and save the files in the `src/` directory it will recompile the 28 | pressure.js and jquery.pressure.js libraries (including the minified versions) 29 | and drop them into the `dist/` directory. 30 | 31 | ## Pull Requests 32 | 33 | Good Pull Requests include: 34 | 35 | - A clear explaination of the problem (or enhancement) 36 | - Clean commit history (squash where it makes sense) 37 | - Relevant Explination of how to test 38 | 39 | Thanks to [Payform](https://github.com/jondavidjohn/payform) for the contribution template. 40 | -------------------------------------------------------------------------------- /License: -------------------------------------------------------------------------------- 1 | The MIT License 2 | 3 | Copyright (c) 2015 - 2017 Stuart Yamartino. 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. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Pressure.js 2 | 3 | [![Join the chat at https://gitter.im/yamartino/pressure](https://badges.gitter.im/yamartino/pressure.svg)](https://gitter.im/stuyam/pressure?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) 4 | [![npm https://www.npmjs.com/package/pressure](https://img.shields.io/npm/v/pressure.svg)](https://www.npmjs.com/package/pressure) 5 | [![npm https://www.npmjs.com/package/pressure](https://img.shields.io/npm/dt/pressure.svg)](https://www.npmjs.com/package/pressure) 6 | 7 | ![Pressure Example](http://pressurejs.com/img/pressure.gif) 8 | 9 | Pressure is a JavaScript library for handling both Force Touch and 3D Touch on the web, bundled under one library with a simple API that makes working with them painless. 10 | 11 | Head over to the [documentation](http://pressurejs.com/documentation.html) for installation instructions, supported devices, and more details on pressure.js. 12 | 13 | ## Install 14 | Download pressure.min.js or pressure.js files from GitHub or install with npm or bower 15 | #### npm 16 | ``` 17 | npm install pressure --save 18 | ``` 19 | #### bower 20 | ``` 21 | bower install pressure --save 22 | ``` 23 | 24 | 25 | ## Setup 26 | Use pressure in the global space: 27 | ```javascript 28 | Pressure.set('#id-name', { 29 | change: function(force){ 30 | this.innerHTML = force; 31 | } 32 | }); 33 | ``` 34 | OR use it with browserify or CommonJS like setups: 35 | ```javascript 36 | var Pressure = require('pressure'); 37 | 38 | Pressure.set('#id-name', { 39 | change: function(force){ 40 | this.innerHTML = force; 41 | } 42 | }); 43 | ``` 44 | 45 | 46 | ## Usage 47 | NOTE: the "this" keyword in each of the callback methods will be the element itself that has force applied to it 48 | ```javascript 49 | Pressure.set('#element', { 50 | start: function(event){ 51 | // this is called on force start 52 | }, 53 | end: function(){ 54 | // this is called on force end 55 | }, 56 | startDeepPress: function(event){ 57 | // this is called on "force click" / "deep press", aka once the force is greater than 0.5 58 | }, 59 | endDeepPress: function(){ 60 | // this is called when the "force click" / "deep press" end 61 | }, 62 | change: function(force, event){ 63 | // this is called every time there is a change in pressure 64 | // force will always be a value from 0 to 1 on mobile and desktop 65 | }, 66 | unsupported: function(){ 67 | // NOTE: this is only called if the polyfill option is disabled! 68 | // this is called once there is a touch on the element and the device or browser does not support Force or 3D touch 69 | } 70 | }); 71 | ``` 72 | 73 | 74 | ## jQuery Usage 75 | NOTE: the "this" keyword in each of the callback methods will be the element itself that has force applied to it 76 | ```javascript 77 | $('#element').pressure({ 78 | start: function(event){ 79 | // this is called on force start 80 | }, 81 | end: function(){ 82 | // this is called on force end 83 | }, 84 | startDeepPress: function(event){ 85 | // this is called on "force click" / "deep press", aka once the force is greater than 0.5 86 | }, 87 | endDeepPress: function(){ 88 | // this is called when the "force click" / "deep press" end 89 | }, 90 | change: function(force, event){ 91 | // this is called every time there is a change in pressure 92 | // force will always be a value from 0 to 1 on mobile and desktop 93 | }, 94 | unsupported: function(){ 95 | // NOTE: this is only called if the polyfill option is disabled! 96 | // this is called once there is a touch on the element and the device or browser does not support Force or 3D touch 97 | } 98 | }); 99 | ``` 100 | 101 | ## Options 102 | With Pressure, the third parameter is an optional object of options that can be passed in. 103 | 104 | ### Polyfill Support 105 | Using the "polyfill" keyword, you can disable polyfill support for the element. The polyfill is enabled by default and is useful if the device or browser does not support pressure, it will fall back to using time. For example instead of force from 0 to 1, it counts up from 0 to 1 over the course of one second, as long as you are holding the element. Try some of the examples on the main page on a device that does not support pressure and see for yourself how it works. 106 | ```javascript 107 | Pressure.set('#example', { 108 | change: function(force, event){ 109 | this.innerHTML = force; 110 | }, 111 | unsupported: function(){ 112 | alert("Oh no, this device does not support pressure."); 113 | } 114 | }, {polyfill: false}); 115 | ``` 116 | 117 | ### Polyfill Speed Up 118 | If you are using the polyfill (on by default), you can see the "polyfillSpeedUp" speed to determine how fast the polyfill takes to go from 0 to 1. The value is an integer in milliseconds and the default is 1000 (1 second). 119 | ```javascript 120 | Pressure.set('#example', { 121 | change: function(force, event){ 122 | this.innerHTML = force; 123 | } 124 | }, {polyfillSpeedUp: 5000}); 125 | // takes 5 seconds to go from a force value of 0 to 1 126 | // only on devices that do not support pressure 127 | ``` 128 | 129 | ### Polyfill Speed Down 130 | If you are using the polyfill (on by default), you can see the "polyfillSpeedDown" speed to determine how fast the polyfill takes to go from 1 to 0 when you let go. The value is an integer in milliseconds and the default is 0 (aka off). 131 | ```javascript 132 | Pressure.set('#example', { 133 | change: function(force, event){ 134 | this.innerHTML = force; 135 | } 136 | }, {polyfillSpeedDown: 2000}); 137 | // takes 2 seconds to go from a force value of 1 to 0 138 | // only on devices that do not support pressure 139 | ``` 140 | 141 | ### Only run on Force Touch trackpads (mouse) 142 | Set the option only to the type you want it to run on 'mouse', 'touch', or 'pointer'. The names are the types of events that pressure will respond to. 143 | ```javascript 144 | Pressure.set('#example',{ 145 | change: function(force, event){ 146 | console.log(force); 147 | }, 148 | }, {only: 'mouse'}); 149 | ``` 150 | ### Only run on 3D Touch (touch) 151 | ```javascript 152 | Pressure.set('#example',{ 153 | change: function(force, event){ 154 | console.log(force); 155 | }, 156 | }, {only: 'touch'}); 157 | ``` 158 | ### Only run on Pointer Supported Devices (pointer) 159 | ```javascript 160 | Pressure.set('#example',{ 161 | change: function(force, event){ 162 | console.log(force); 163 | }, 164 | }, {only: 'pointer'}); 165 | ``` 166 | 167 | ### Change the preventSelect option 168 | The preventDefault option is "true" by default and it prevents the default actions that happen on 3D "peel and pop" actions and the Force "define word" actions as well as other defaults. To allow the defaults to run set preventDefault to "false" 169 | ```javascript 170 | Pressure.set('#example',{ 171 | change: function(force, event){ 172 | console.log(force); 173 | }, 174 | }, {preventSelect: false}); 175 | ``` 176 | 177 | ## Helpers 178 | 179 | ### Config 180 | You can use ```Pressure.config()``` to set default configurations for site wide setup. All of the configurations are the same as the options listed above. 181 | 182 | *Heads Up: If you have a config set, you can always override the config on individual Pressure elements by passing in any of the options listed above to a specific Pressure block.* 183 | 184 | **When using the jQuery Pressure library, use ```$.pressureConfig()``` rather than ```Pressure.map()```** 185 | ```javascript 186 | // These are the default configs set by Pressure 187 | Pressure.config({ 188 | polyfill: true, 189 | polyfillSpeedUp: 1000, 190 | polyfillSpeedDown: 0, 191 | preventDefault: true, 192 | only: null 193 | }); 194 | ``` 195 | 196 | ### Map 197 | You can use ```Pressure.map()``` to map a value from one range of values to another. It takes 5 params: ```Pressure.map(inputValue, inputValueMin, inputValueMax, mapToMin, mapToMax);``` Here is a good write up on how this works in the Processing framework: [Map Function](https://processing.org/reference/map_.html). 198 | 199 | **When using the jQuery Pressure library, use ```$.pressureMap()``` rather than ```Pressure.map()```** 200 | ```javascript 201 | Pressure.set('#element', { 202 | change: function(force, event){ 203 | // this takes the force, given that the force can range from 0 to 1, and maps that force value on a 100 to 200 range 204 | this.style.width = Pressure.map(force, 0, 1, 100, 200); 205 | } 206 | }); 207 | ``` 208 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "pressure", 3 | "version": "2.2.0", 4 | "description": "Pressure is a lightweight JavaScript library for Force Touch, 3D Touch, and Pointer Pressure through a single API.", 5 | "main": "dist/pressure.min.js", 6 | "homepage": "https://github.com/stuyam/pressure", 7 | "license": "MIT", 8 | "authors": [ 9 | "Stuart Yamartino" 10 | ], 11 | "keywords": [ 12 | "forcetouch", 13 | "force-touch", 14 | "3dtouch", 15 | "3d-touch", 16 | "pressure", 17 | "pressurejs", 18 | "iphone6s", 19 | "iphone6splus", 20 | "iphone7", 21 | "iphone7plus", 22 | "magic-trackpad", 23 | "apple-pencil" 24 | ], 25 | "moduleType": [ 26 | "amd", 27 | "globals", 28 | "node" 29 | ], 30 | "ignore": [ 31 | "**/.*", 32 | "node_modules", 33 | "bower_components", 34 | "test", 35 | "tests" 36 | ] 37 | } 38 | -------------------------------------------------------------------------------- /dist/jquery.pressure.js: -------------------------------------------------------------------------------- 1 | // Pressure v2.2.0 | Created By Stuart Yamartino | MIT License | 2015 - 2020 2 | ;(function(root, factory) { 3 | if (typeof define === 'function' && define.amd) { 4 | define(['jquery'], factory); 5 | } else if (typeof exports === 'object') { 6 | module.exports = factory(require('jquery')); 7 | } else { 8 | root.jQuery__pressure = factory(root.jQuery); 9 | } 10 | }(this, function($) { 11 | "use strict"; 12 | 13 | function _typeof(obj) { "@babel/helpers - typeof"; if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); } 14 | 15 | function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); if (superClass) _setPrototypeOf(subClass, superClass); } 16 | 17 | function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); } 18 | 19 | function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; } 20 | 21 | function _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === "object" || typeof call === "function")) { return call; } return _assertThisInitialized(self); } 22 | 23 | function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; } 24 | 25 | function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Date.prototype.toString.call(Reflect.construct(Date, [], function () {})); return true; } catch (e) { return false; } } 26 | 27 | function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); } 28 | 29 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 30 | 31 | function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } 32 | 33 | function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; } 34 | 35 | //--------------------- Public jQuery API Section ---------------------// 36 | if ($) { 37 | $.fn.pressure = function (closure, options) { 38 | loopPressureElements(this, closure, options); 39 | return this; 40 | }; 41 | 42 | $.pressureConfig = function (options) { 43 | Config.set(options); 44 | }; 45 | 46 | $.pressureMap = function (x, in_min, in_max, out_min, out_max) { 47 | return map.apply(null, arguments); 48 | }; 49 | } else { 50 | throw new Error("Pressure jQuery requires jQuery to be loaded."); 51 | } 52 | 53 | var Element = /*#__PURE__*/function () { 54 | function Element(el, block, options) { 55 | _classCallCheck(this, Element); 56 | 57 | this.routeEvents(el, block, options); 58 | this.preventSelect(el, options); 59 | } 60 | 61 | _createClass(Element, [{ 62 | key: "routeEvents", 63 | value: function routeEvents(el, block, options) { 64 | var type = Config.get('only', options); // for devices that support Force Touch 65 | 66 | if (supportsMouse && (type === 'mouse' || type === null)) { 67 | this.adapter = new AdapterForceTouch(el, block, options).bindEvents(); 68 | } // for devices that support pointer events 69 | else if (supportsPointer && (type === 'pointer' || type === null)) { 70 | this.adapter = new AdapterPointer(el, block, options).bindEvents(); 71 | } // for devices that support 3D Touch 72 | else if (supportsTouch && (type === 'touch' || type === null)) { 73 | this.adapter = new Adapter3DTouch(el, block, options).bindEvents(); 74 | } // unsupported if it is requesting a type and your browser is of other type 75 | else { 76 | this.adapter = new Adapter(el, block).bindUnsupportedEvent(); 77 | } 78 | } // prevent the default action of text selection, "peak & pop", and force touch special feature 79 | 80 | }, { 81 | key: "preventSelect", 82 | value: function preventSelect(el, options) { 83 | if (Config.get('preventSelect', options)) { 84 | el.style.webkitTouchCallout = "none"; 85 | el.style.webkitUserSelect = "none"; 86 | el.style.khtmlUserSelect = "none"; 87 | el.style.MozUserSelect = "none"; 88 | el.style.msUserSelect = "none"; 89 | el.style.userSelect = "none"; 90 | } 91 | } 92 | }]); 93 | 94 | return Element; 95 | }(); 96 | /* 97 | This is the base adapter from which all the other adapters extend. 98 | */ 99 | 100 | 101 | var Adapter = /*#__PURE__*/function () { 102 | function Adapter(el, block, options) { 103 | _classCallCheck(this, Adapter); 104 | 105 | this.el = el; 106 | this.block = block; 107 | this.options = options; 108 | this.pressed = false; 109 | this.deepPressed = false; 110 | this.nativeSupport = false; 111 | this.runningPolyfill = false; 112 | this.runKey = Math.random(); 113 | } 114 | 115 | _createClass(Adapter, [{ 116 | key: "setPressed", 117 | value: function setPressed(_boolean) { 118 | this.pressed = _boolean; 119 | } 120 | }, { 121 | key: "setDeepPressed", 122 | value: function setDeepPressed(_boolean2) { 123 | this.deepPressed = _boolean2; 124 | } 125 | }, { 126 | key: "isPressed", 127 | value: function isPressed() { 128 | return this.pressed; 129 | } 130 | }, { 131 | key: "isDeepPressed", 132 | value: function isDeepPressed() { 133 | return this.deepPressed; 134 | } 135 | }, { 136 | key: "add", 137 | value: function add(event, set) { 138 | this.el.addEventListener(event, set, false); 139 | } 140 | }, { 141 | key: "runClosure", 142 | value: function runClosure(method) { 143 | if (method in this.block) { 144 | // call the closure method and apply nth arguments if they exist 145 | this.block[method].apply(this.el, Array.prototype.slice.call(arguments, 1)); 146 | } 147 | } 148 | }, { 149 | key: "fail", 150 | value: function fail(event, runKey) { 151 | if (Config.get('polyfill', this.options)) { 152 | if (this.runKey === runKey) { 153 | this.runPolyfill(event); 154 | } 155 | } else { 156 | this.runClosure('unsupported', event); 157 | } 158 | } 159 | }, { 160 | key: "bindUnsupportedEvent", 161 | value: function bindUnsupportedEvent() { 162 | var _this = this; 163 | 164 | this.add(supportsTouch ? 'touchstart' : 'mousedown', function (event) { 165 | return _this.runClosure('unsupported', event); 166 | }); 167 | } 168 | }, { 169 | key: "_startPress", 170 | value: function _startPress(event) { 171 | if (this.isPressed() === false) { 172 | this.runningPolyfill = false; 173 | this.setPressed(true); 174 | this.runClosure('start', event); 175 | } 176 | } 177 | }, { 178 | key: "_startDeepPress", 179 | value: function _startDeepPress(event) { 180 | if (this.isPressed() && this.isDeepPressed() === false) { 181 | this.setDeepPressed(true); 182 | this.runClosure('startDeepPress', event); 183 | } 184 | } 185 | }, { 186 | key: "_changePress", 187 | value: function _changePress(force, event) { 188 | this.nativeSupport = true; 189 | this.runClosure('change', force, event); 190 | } 191 | }, { 192 | key: "_endDeepPress", 193 | value: function _endDeepPress() { 194 | if (this.isPressed() && this.isDeepPressed()) { 195 | this.setDeepPressed(false); 196 | this.runClosure('endDeepPress'); 197 | } 198 | } 199 | }, { 200 | key: "_endPress", 201 | value: function _endPress() { 202 | if (this.runningPolyfill === false) { 203 | if (this.isPressed()) { 204 | this._endDeepPress(); 205 | 206 | this.setPressed(false); 207 | this.runClosure('end'); 208 | } 209 | 210 | this.runKey = Math.random(); 211 | this.nativeSupport = false; 212 | } else { 213 | this.setPressed(false); 214 | } 215 | } 216 | }, { 217 | key: "deepPress", 218 | value: function deepPress(force, event) { 219 | force >= 0.5 ? this._startDeepPress(event) : this._endDeepPress(); 220 | } 221 | }, { 222 | key: "runPolyfill", 223 | value: function runPolyfill(event) { 224 | this.increment = Config.get('polyfillSpeedUp', this.options) === 0 ? 1 : 10 / Config.get('polyfillSpeedUp', this.options); 225 | this.decrement = Config.get('polyfillSpeedDown', this.options) === 0 ? 1 : 10 / Config.get('polyfillSpeedDown', this.options); 226 | this.setPressed(true); 227 | this.runClosure('start', event); 228 | 229 | if (this.runningPolyfill === false) { 230 | this.loopPolyfillForce(0, event); 231 | } 232 | } 233 | }, { 234 | key: "loopPolyfillForce", 235 | value: function loopPolyfillForce(force, event) { 236 | if (this.nativeSupport === false) { 237 | if (this.isPressed()) { 238 | this.runningPolyfill = true; 239 | force = force + this.increment > 1 ? 1 : force + this.increment; 240 | this.runClosure('change', force, event); 241 | this.deepPress(force, event); 242 | setTimeout(this.loopPolyfillForce.bind(this, force, event), 10); 243 | } else { 244 | force = force - this.decrement < 0 ? 0 : force - this.decrement; 245 | 246 | if (force < 0.5 && this.isDeepPressed()) { 247 | this.setDeepPressed(false); 248 | this.runClosure('endDeepPress'); 249 | } 250 | 251 | if (force === 0) { 252 | this.runningPolyfill = false; 253 | this.setPressed(true); 254 | 255 | this._endPress(); 256 | } else { 257 | this.runClosure('change', force, event); 258 | this.deepPress(force, event); 259 | setTimeout(this.loopPolyfillForce.bind(this, force, event), 10); 260 | } 261 | } 262 | } 263 | } 264 | }]); 265 | 266 | return Adapter; 267 | }(); 268 | /* 269 | This adapter is for Macs with Force Touch trackpads. 270 | */ 271 | 272 | 273 | var AdapterForceTouch = /*#__PURE__*/function (_Adapter) { 274 | _inherits(AdapterForceTouch, _Adapter); 275 | 276 | var _super = _createSuper(AdapterForceTouch); 277 | 278 | function AdapterForceTouch(el, block, options) { 279 | _classCallCheck(this, AdapterForceTouch); 280 | 281 | return _super.call(this, el, block, options); 282 | } 283 | 284 | _createClass(AdapterForceTouch, [{ 285 | key: "bindEvents", 286 | value: function bindEvents() { 287 | this.add('webkitmouseforcewillbegin', this._startPress.bind(this)); 288 | this.add('mousedown', this.support.bind(this)); 289 | this.add('webkitmouseforcechanged', this.change.bind(this)); 290 | this.add('webkitmouseforcedown', this._startDeepPress.bind(this)); 291 | this.add('webkitmouseforceup', this._endDeepPress.bind(this)); 292 | this.add('mouseleave', this._endPress.bind(this)); 293 | this.add('mouseup', this._endPress.bind(this)); 294 | } 295 | }, { 296 | key: "support", 297 | value: function support(event) { 298 | if (this.isPressed() === false) { 299 | this.fail(event, this.runKey); 300 | } 301 | } 302 | }, { 303 | key: "change", 304 | value: function change(event) { 305 | if (this.isPressed() && event.webkitForce > 0) { 306 | this._changePress(this.normalizeForce(event.webkitForce), event); 307 | } 308 | } // make the force the standard 0 to 1 scale and not the 1 to 3 scale 309 | 310 | }, { 311 | key: "normalizeForce", 312 | value: function normalizeForce(force) { 313 | return this.reachOne(map(force, 1, 3, 0, 1)); 314 | } // if the force value is above 0.995 set the force to 1 315 | 316 | }, { 317 | key: "reachOne", 318 | value: function reachOne(force) { 319 | return force > 0.995 ? 1 : force; 320 | } 321 | }]); 322 | 323 | return AdapterForceTouch; 324 | }(Adapter); 325 | /* 326 | This adapter is more mobile devices that support 3D Touch. 327 | */ 328 | 329 | 330 | var Adapter3DTouch = /*#__PURE__*/function (_Adapter2) { 331 | _inherits(Adapter3DTouch, _Adapter2); 332 | 333 | var _super2 = _createSuper(Adapter3DTouch); 334 | 335 | function Adapter3DTouch(el, block, options) { 336 | _classCallCheck(this, Adapter3DTouch); 337 | 338 | return _super2.call(this, el, block, options); 339 | } 340 | 341 | _createClass(Adapter3DTouch, [{ 342 | key: "bindEvents", 343 | value: function bindEvents() { 344 | if (supportsTouchForceChange) { 345 | this.add('touchforcechange', this.start.bind(this)); 346 | this.add('touchstart', this.support.bind(this, 0)); 347 | this.add('touchend', this._endPress.bind(this)); 348 | } else { 349 | this.add('touchstart', this.startLegacy.bind(this)); 350 | this.add('touchend', this._endPress.bind(this)); 351 | } 352 | } 353 | }, { 354 | key: "start", 355 | value: function start(event) { 356 | if (event.touches.length > 0) { 357 | this._startPress(event); 358 | 359 | this.touch = this.selectTouch(event); 360 | 361 | if (this.touch) { 362 | this._changePress(this.touch.force, event); 363 | } 364 | } 365 | } 366 | }, { 367 | key: "support", 368 | value: function support(iter, event) { 369 | var runKey = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : this.runKey; 370 | 371 | if (this.isPressed() === false) { 372 | if (iter <= 6) { 373 | iter++; 374 | setTimeout(this.support.bind(this, iter, event, runKey), 10); 375 | } else { 376 | this.fail(event, runKey); 377 | } 378 | } 379 | } 380 | }, { 381 | key: "startLegacy", 382 | value: function startLegacy(event) { 383 | this.initialForce = event.touches[0].force; 384 | this.supportLegacy(0, event, this.runKey, this.initialForce); 385 | } // this checks up to 6 times on a touch to see if the touch can read a force value 386 | // if the force value has changed it means the device supports pressure 387 | // more info from this issue https://github.com/yamartino/pressure/issues/15 388 | 389 | }, { 390 | key: "supportLegacy", 391 | value: function supportLegacy(iter, event, runKey, force) { 392 | if (force !== this.initialForce) { 393 | this._startPress(event); 394 | 395 | this.loopForce(event); 396 | } else if (iter <= 6) { 397 | iter++; 398 | setTimeout(this.supportLegacy.bind(this, iter, event, runKey, force), 10); 399 | } else { 400 | this.fail(event, runKey); 401 | } 402 | } 403 | }, { 404 | key: "loopForce", 405 | value: function loopForce(event) { 406 | if (this.isPressed()) { 407 | this.touch = this.selectTouch(event); 408 | setTimeout(this.loopForce.bind(this, event), 10); 409 | 410 | this._changePress(this.touch.force, event); 411 | } 412 | } // link up the touch point to the correct element, this is to support multitouch 413 | 414 | }, { 415 | key: "selectTouch", 416 | value: function selectTouch(event) { 417 | if (event.touches.length === 1) { 418 | return this.returnTouch(event.touches[0], event); 419 | } else { 420 | for (var i = 0; i < event.touches.length; i++) { 421 | // if the target press is on this element 422 | if (event.touches[i].target === this.el || this.el.contains(event.touches[i].target)) { 423 | return this.returnTouch(event.touches[i], event); 424 | } 425 | } 426 | } 427 | } // return the touch and run a start or end for deep press 428 | 429 | }, { 430 | key: "returnTouch", 431 | value: function returnTouch(touch, event) { 432 | this.deepPress(touch.force, event); 433 | return touch; 434 | } 435 | }]); 436 | 437 | return Adapter3DTouch; 438 | }(Adapter); 439 | /* 440 | This adapter is for devices that support pointer events. 441 | */ 442 | 443 | 444 | var AdapterPointer = /*#__PURE__*/function (_Adapter3) { 445 | _inherits(AdapterPointer, _Adapter3); 446 | 447 | var _super3 = _createSuper(AdapterPointer); 448 | 449 | function AdapterPointer(el, block, options) { 450 | _classCallCheck(this, AdapterPointer); 451 | 452 | return _super3.call(this, el, block, options); 453 | } 454 | 455 | _createClass(AdapterPointer, [{ 456 | key: "bindEvents", 457 | value: function bindEvents() { 458 | this.add('pointerdown', this.support.bind(this)); 459 | this.add('pointermove', this.change.bind(this)); 460 | this.add('pointerup', this._endPress.bind(this)); 461 | this.add('pointerleave', this._endPress.bind(this)); 462 | } 463 | }, { 464 | key: "support", 465 | value: function support(event) { 466 | if (this.isPressed() === false) { 467 | if (event.pressure === 0 || event.pressure === 0.5 || event.pressure > 1) { 468 | this.fail(event, this.runKey); 469 | } else { 470 | this._startPress(event); 471 | 472 | this._changePress(event.pressure, event); 473 | } 474 | } 475 | } 476 | }, { 477 | key: "change", 478 | value: function change(event) { 479 | if (this.isPressed() && event.pressure > 0 && event.pressure !== 0.5) { 480 | this._changePress(event.pressure, event); 481 | 482 | this.deepPress(event.pressure, event); 483 | } 484 | } 485 | }]); 486 | 487 | return AdapterPointer; 488 | }(Adapter); // This class holds the states of the the Pressure config 489 | 490 | 491 | var Config = { 492 | // 'false' will make polyfill not run when pressure is not supported and the 'unsupported' method will be called 493 | polyfill: true, 494 | // milliseconds it takes to go from 0 to 1 for the polyfill 495 | polyfillSpeedUp: 1000, 496 | // milliseconds it takes to go from 1 to 0 for the polyfill 497 | polyfillSpeedDown: 0, 498 | // 'true' prevents the selecting of text and images via css properties 499 | preventSelect: true, 500 | // 'touch', 'mouse', or 'pointer' will make it run only on that type of device 501 | only: null, 502 | // this will get the correct config / option settings for the current pressure check 503 | get: function get(option, options) { 504 | return options.hasOwnProperty(option) ? options[option] : this[option]; 505 | }, 506 | // this will set the global configs 507 | set: function set(options) { 508 | for (var k in options) { 509 | if (options.hasOwnProperty(k) && this.hasOwnProperty(k) && k != 'get' && k != 'set') { 510 | this[k] = options[k]; 511 | } 512 | } 513 | } 514 | }; //------------------- Helpers -------------------// 515 | // accepts jQuery object, node list, string selector, then called a setup for each element 516 | 517 | var loopPressureElements = function loopPressureElements(selector, closure) { 518 | var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; 519 | 520 | // if a string is passed in as an element 521 | if (typeof selector === 'string' || selector instanceof String) { 522 | var elements = document.querySelectorAll(selector); 523 | 524 | for (var i = 0; i < elements.length; i++) { 525 | new Element(elements[i], closure, options); 526 | } // if a single element object is passed in 527 | 528 | } else if (isElement(selector)) { 529 | new Element(selector, closure, options); // if a node list is passed in ex. jQuery $() object 530 | } else { 531 | for (var i = 0; i < selector.length; i++) { 532 | new Element(selector[i], closure, options); 533 | } 534 | } 535 | }; //Returns true if it is a DOM element 536 | 537 | 538 | var isElement = function isElement(o) { 539 | return (typeof HTMLElement === "undefined" ? "undefined" : _typeof(HTMLElement)) === "object" ? o instanceof HTMLElement : //DOM2 540 | o && _typeof(o) === "object" && o !== null && o.nodeType === 1 && typeof o.nodeName === "string"; 541 | }; // the map method allows for interpolating a value from one range of values to another 542 | // example from the Arduino documentation: https://www.arduino.cc/en/Reference/Map 543 | 544 | 545 | var map = function map(x, in_min, in_max, out_min, out_max) { 546 | return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min; 547 | }; 548 | 549 | var supportsMouse = false; 550 | var supportsTouch = false; 551 | var supportsPointer = false; 552 | var supportsTouchForce = false; 553 | var supportsTouchForceChange = false; 554 | 555 | if (typeof window !== 'undefined') { 556 | // only attempt to assign these in a browser environment. 557 | // on the server, this is a no-op, like the rest of the library 558 | if (typeof Touch !== 'undefined') { 559 | // In Android, new Touch requires arguments. 560 | try { 561 | if (Touch.prototype.hasOwnProperty('force') || 'force' in new Touch()) { 562 | supportsTouchForce = true; 563 | } 564 | } catch (e) {} 565 | } 566 | 567 | supportsTouch = 'ontouchstart' in window.document && supportsTouchForce; 568 | supportsMouse = 'onmousemove' in window.document && 'onwebkitmouseforcechanged' in window.document && !supportsTouch; 569 | supportsPointer = 'onpointermove' in window.document; 570 | supportsTouchForceChange = 'ontouchforcechange' in window.document; 571 | } 572 | return void 0; 573 | })); 574 | -------------------------------------------------------------------------------- /dist/jquery.pressure.min.js: -------------------------------------------------------------------------------- 1 | // Pressure v2.2.0 | Created By Stuart Yamartino | MIT License | 2015 - 2020 2 | !function(e,t){"function"==typeof define&&define.amd?define(["jquery"],t):"object"==typeof exports?module.exports=t(require("jquery")):e.jQuery__pressure=t(e.jQuery)}(this,function(e){"use strict";function i(e){return(i="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e})(e)}function t(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function");e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,writable:!0,configurable:!0}}),t&&s(e,t)}function s(e,t){return(s=Object.setPrototypeOf||function(e,t){return e.__proto__=t,e})(e,t)}function r(s){var n=function(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Date.prototype.toString.call(Reflect.construct(Date,[],function(){})),!0}catch(e){return!1}}();return function(){var e,t=o(s);return e=n?(e=o(this).constructor,Reflect.construct(t,arguments,e)):t.apply(this,arguments),t=this,!(e=e)||"object"!==i(e)&&"function"!=typeof e?function(e){if(void 0!==e)return e;throw new ReferenceError("this hasn't been initialised - super() hasn't been called")}(t):e}}function o(e){return(o=Object.setPrototypeOf?Object.getPrototypeOf:function(e){return e.__proto__||Object.getPrototypeOf(e)})(e)}function u(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function n(e,t){for(var s=0;s= 0.5 ? this._startDeepPress(event) : this._endDeepPress(); 221 | } 222 | }, { 223 | key: "runPolyfill", 224 | value: function runPolyfill(event) { 225 | this.increment = Config.get('polyfillSpeedUp', this.options) === 0 ? 1 : 10 / Config.get('polyfillSpeedUp', this.options); 226 | this.decrement = Config.get('polyfillSpeedDown', this.options) === 0 ? 1 : 10 / Config.get('polyfillSpeedDown', this.options); 227 | this.setPressed(true); 228 | this.runClosure('start', event); 229 | 230 | if (this.runningPolyfill === false) { 231 | this.loopPolyfillForce(0, event); 232 | } 233 | } 234 | }, { 235 | key: "loopPolyfillForce", 236 | value: function loopPolyfillForce(force, event) { 237 | if (this.nativeSupport === false) { 238 | if (this.isPressed()) { 239 | this.runningPolyfill = true; 240 | force = force + this.increment > 1 ? 1 : force + this.increment; 241 | this.runClosure('change', force, event); 242 | this.deepPress(force, event); 243 | setTimeout(this.loopPolyfillForce.bind(this, force, event), 10); 244 | } else { 245 | force = force - this.decrement < 0 ? 0 : force - this.decrement; 246 | 247 | if (force < 0.5 && this.isDeepPressed()) { 248 | this.setDeepPressed(false); 249 | this.runClosure('endDeepPress'); 250 | } 251 | 252 | if (force === 0) { 253 | this.runningPolyfill = false; 254 | this.setPressed(true); 255 | 256 | this._endPress(); 257 | } else { 258 | this.runClosure('change', force, event); 259 | this.deepPress(force, event); 260 | setTimeout(this.loopPolyfillForce.bind(this, force, event), 10); 261 | } 262 | } 263 | } 264 | } 265 | }]); 266 | 267 | return Adapter; 268 | }(); 269 | /* 270 | This adapter is for Macs with Force Touch trackpads. 271 | */ 272 | 273 | 274 | var AdapterForceTouch = /*#__PURE__*/function (_Adapter) { 275 | _inherits(AdapterForceTouch, _Adapter); 276 | 277 | var _super = _createSuper(AdapterForceTouch); 278 | 279 | function AdapterForceTouch(el, block, options) { 280 | _classCallCheck(this, AdapterForceTouch); 281 | 282 | return _super.call(this, el, block, options); 283 | } 284 | 285 | _createClass(AdapterForceTouch, [{ 286 | key: "bindEvents", 287 | value: function bindEvents() { 288 | this.add('webkitmouseforcewillbegin', this._startPress.bind(this)); 289 | this.add('mousedown', this.support.bind(this)); 290 | this.add('webkitmouseforcechanged', this.change.bind(this)); 291 | this.add('webkitmouseforcedown', this._startDeepPress.bind(this)); 292 | this.add('webkitmouseforceup', this._endDeepPress.bind(this)); 293 | this.add('mouseleave', this._endPress.bind(this)); 294 | this.add('mouseup', this._endPress.bind(this)); 295 | } 296 | }, { 297 | key: "support", 298 | value: function support(event) { 299 | if (this.isPressed() === false) { 300 | this.fail(event, this.runKey); 301 | } 302 | } 303 | }, { 304 | key: "change", 305 | value: function change(event) { 306 | if (this.isPressed() && event.webkitForce > 0) { 307 | this._changePress(this.normalizeForce(event.webkitForce), event); 308 | } 309 | } // make the force the standard 0 to 1 scale and not the 1 to 3 scale 310 | 311 | }, { 312 | key: "normalizeForce", 313 | value: function normalizeForce(force) { 314 | return this.reachOne(_map(force, 1, 3, 0, 1)); 315 | } // if the force value is above 0.995 set the force to 1 316 | 317 | }, { 318 | key: "reachOne", 319 | value: function reachOne(force) { 320 | return force > 0.995 ? 1 : force; 321 | } 322 | }]); 323 | 324 | return AdapterForceTouch; 325 | }(Adapter); 326 | /* 327 | This adapter is more mobile devices that support 3D Touch. 328 | */ 329 | 330 | 331 | var Adapter3DTouch = /*#__PURE__*/function (_Adapter2) { 332 | _inherits(Adapter3DTouch, _Adapter2); 333 | 334 | var _super2 = _createSuper(Adapter3DTouch); 335 | 336 | function Adapter3DTouch(el, block, options) { 337 | _classCallCheck(this, Adapter3DTouch); 338 | 339 | return _super2.call(this, el, block, options); 340 | } 341 | 342 | _createClass(Adapter3DTouch, [{ 343 | key: "bindEvents", 344 | value: function bindEvents() { 345 | if (supportsTouchForceChange) { 346 | this.add('touchforcechange', this.start.bind(this)); 347 | this.add('touchstart', this.support.bind(this, 0)); 348 | this.add('touchend', this._endPress.bind(this)); 349 | } else { 350 | this.add('touchstart', this.startLegacy.bind(this)); 351 | this.add('touchend', this._endPress.bind(this)); 352 | } 353 | } 354 | }, { 355 | key: "start", 356 | value: function start(event) { 357 | if (event.touches.length > 0) { 358 | this._startPress(event); 359 | 360 | this.touch = this.selectTouch(event); 361 | 362 | if (this.touch) { 363 | this._changePress(this.touch.force, event); 364 | } 365 | } 366 | } 367 | }, { 368 | key: "support", 369 | value: function support(iter, event) { 370 | var runKey = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : this.runKey; 371 | 372 | if (this.isPressed() === false) { 373 | if (iter <= 6) { 374 | iter++; 375 | setTimeout(this.support.bind(this, iter, event, runKey), 10); 376 | } else { 377 | this.fail(event, runKey); 378 | } 379 | } 380 | } 381 | }, { 382 | key: "startLegacy", 383 | value: function startLegacy(event) { 384 | this.initialForce = event.touches[0].force; 385 | this.supportLegacy(0, event, this.runKey, this.initialForce); 386 | } // this checks up to 6 times on a touch to see if the touch can read a force value 387 | // if the force value has changed it means the device supports pressure 388 | // more info from this issue https://github.com/yamartino/pressure/issues/15 389 | 390 | }, { 391 | key: "supportLegacy", 392 | value: function supportLegacy(iter, event, runKey, force) { 393 | if (force !== this.initialForce) { 394 | this._startPress(event); 395 | 396 | this.loopForce(event); 397 | } else if (iter <= 6) { 398 | iter++; 399 | setTimeout(this.supportLegacy.bind(this, iter, event, runKey, force), 10); 400 | } else { 401 | this.fail(event, runKey); 402 | } 403 | } 404 | }, { 405 | key: "loopForce", 406 | value: function loopForce(event) { 407 | if (this.isPressed()) { 408 | this.touch = this.selectTouch(event); 409 | setTimeout(this.loopForce.bind(this, event), 10); 410 | 411 | this._changePress(this.touch.force, event); 412 | } 413 | } // link up the touch point to the correct element, this is to support multitouch 414 | 415 | }, { 416 | key: "selectTouch", 417 | value: function selectTouch(event) { 418 | if (event.touches.length === 1) { 419 | return this.returnTouch(event.touches[0], event); 420 | } else { 421 | for (var i = 0; i < event.touches.length; i++) { 422 | // if the target press is on this element 423 | if (event.touches[i].target === this.el || this.el.contains(event.touches[i].target)) { 424 | return this.returnTouch(event.touches[i], event); 425 | } 426 | } 427 | } 428 | } // return the touch and run a start or end for deep press 429 | 430 | }, { 431 | key: "returnTouch", 432 | value: function returnTouch(touch, event) { 433 | this.deepPress(touch.force, event); 434 | return touch; 435 | } 436 | }]); 437 | 438 | return Adapter3DTouch; 439 | }(Adapter); 440 | /* 441 | This adapter is for devices that support pointer events. 442 | */ 443 | 444 | 445 | var AdapterPointer = /*#__PURE__*/function (_Adapter3) { 446 | _inherits(AdapterPointer, _Adapter3); 447 | 448 | var _super3 = _createSuper(AdapterPointer); 449 | 450 | function AdapterPointer(el, block, options) { 451 | _classCallCheck(this, AdapterPointer); 452 | 453 | return _super3.call(this, el, block, options); 454 | } 455 | 456 | _createClass(AdapterPointer, [{ 457 | key: "bindEvents", 458 | value: function bindEvents() { 459 | this.add('pointerdown', this.support.bind(this)); 460 | this.add('pointermove', this.change.bind(this)); 461 | this.add('pointerup', this._endPress.bind(this)); 462 | this.add('pointerleave', this._endPress.bind(this)); 463 | } 464 | }, { 465 | key: "support", 466 | value: function support(event) { 467 | if (this.isPressed() === false) { 468 | if (event.pressure === 0 || event.pressure === 0.5 || event.pressure > 1) { 469 | this.fail(event, this.runKey); 470 | } else { 471 | this._startPress(event); 472 | 473 | this._changePress(event.pressure, event); 474 | } 475 | } 476 | } 477 | }, { 478 | key: "change", 479 | value: function change(event) { 480 | if (this.isPressed() && event.pressure > 0 && event.pressure !== 0.5) { 481 | this._changePress(event.pressure, event); 482 | 483 | this.deepPress(event.pressure, event); 484 | } 485 | } 486 | }]); 487 | 488 | return AdapterPointer; 489 | }(Adapter); // This class holds the states of the the Pressure config 490 | 491 | 492 | var Config = { 493 | // 'false' will make polyfill not run when pressure is not supported and the 'unsupported' method will be called 494 | polyfill: true, 495 | // milliseconds it takes to go from 0 to 1 for the polyfill 496 | polyfillSpeedUp: 1000, 497 | // milliseconds it takes to go from 1 to 0 for the polyfill 498 | polyfillSpeedDown: 0, 499 | // 'true' prevents the selecting of text and images via css properties 500 | preventSelect: true, 501 | // 'touch', 'mouse', or 'pointer' will make it run only on that type of device 502 | only: null, 503 | // this will get the correct config / option settings for the current pressure check 504 | get: function get(option, options) { 505 | return options.hasOwnProperty(option) ? options[option] : this[option]; 506 | }, 507 | // this will set the global configs 508 | set: function set(options) { 509 | for (var k in options) { 510 | if (options.hasOwnProperty(k) && this.hasOwnProperty(k) && k != 'get' && k != 'set') { 511 | this[k] = options[k]; 512 | } 513 | } 514 | } 515 | }; //------------------- Helpers -------------------// 516 | // accepts jQuery object, node list, string selector, then called a setup for each element 517 | 518 | var loopPressureElements = function loopPressureElements(selector, closure) { 519 | var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; 520 | 521 | // if a string is passed in as an element 522 | if (typeof selector === 'string' || selector instanceof String) { 523 | var elements = document.querySelectorAll(selector); 524 | 525 | for (var i = 0; i < elements.length; i++) { 526 | new Element(elements[i], closure, options); 527 | } // if a single element object is passed in 528 | 529 | } else if (isElement(selector)) { 530 | new Element(selector, closure, options); // if a node list is passed in ex. jQuery $() object 531 | } else { 532 | for (var i = 0; i < selector.length; i++) { 533 | new Element(selector[i], closure, options); 534 | } 535 | } 536 | }; //Returns true if it is a DOM element 537 | 538 | 539 | var isElement = function isElement(o) { 540 | return (typeof HTMLElement === "undefined" ? "undefined" : _typeof(HTMLElement)) === "object" ? o instanceof HTMLElement : //DOM2 541 | o && _typeof(o) === "object" && o !== null && o.nodeType === 1 && typeof o.nodeName === "string"; 542 | }; // the map method allows for interpolating a value from one range of values to another 543 | // example from the Arduino documentation: https://www.arduino.cc/en/Reference/Map 544 | 545 | 546 | var _map = function _map(x, in_min, in_max, out_min, out_max) { 547 | return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min; 548 | }; 549 | 550 | var supportsMouse = false; 551 | var supportsTouch = false; 552 | var supportsPointer = false; 553 | var supportsTouchForce = false; 554 | var supportsTouchForceChange = false; 555 | 556 | if (typeof window !== 'undefined') { 557 | // only attempt to assign these in a browser environment. 558 | // on the server, this is a no-op, like the rest of the library 559 | if (typeof Touch !== 'undefined') { 560 | // In Android, new Touch requires arguments. 561 | try { 562 | if (Touch.prototype.hasOwnProperty('force') || 'force' in new Touch()) { 563 | supportsTouchForce = true; 564 | } 565 | } catch (e) {} 566 | } 567 | 568 | supportsTouch = 'ontouchstart' in window.document && supportsTouchForce; 569 | supportsMouse = 'onmousemove' in window.document && 'onwebkitmouseforcechanged' in window.document && !supportsTouch; 570 | supportsPointer = 'onpointermove' in window.document; 571 | supportsTouchForceChange = 'ontouchforcechange' in window.document; 572 | } 573 | return Pressure; 574 | })); 575 | -------------------------------------------------------------------------------- /dist/pressure.min.js: -------------------------------------------------------------------------------- 1 | // Pressure v2.2.0 | Created By Stuart Yamartino | MIT License | 2015 - 2020 2 | !function(e,t){"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?module.exports=t():e.Pressure=t()}(this,function(){"use strict";function i(e){return(i="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e})(e)}function e(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function");e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,writable:!0,configurable:!0}}),t&&s(e,t)}function s(e,t){return(s=Object.setPrototypeOf||function(e,t){return e.__proto__=t,e})(e,t)}function t(s){var n=function(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Date.prototype.toString.call(Reflect.construct(Date,[],function(){})),!0}catch(e){return!1}}();return function(){var e,t=r(s);return e=n?(e=r(this).constructor,Reflect.construct(t,arguments,e)):t.apply(this,arguments),t=this,!(e=e)||"object"!==i(e)&&"function"!=typeof e?function(e){if(void 0!==e)return e;throw new ReferenceError("this hasn't been initialised - super() hasn't been called")}(t):e}}function r(e){return(r=Object.setPrototypeOf?Object.getPrototypeOf:function(e){return e.__proto__||Object.getPrototypeOf(e)})(e)}function o(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function n(e,t){for(var s=0;s.cd-body,.cd-iphone-5c>.cd-body{width:19.8em;height:42em}.cd-iphone-6,.cd-iphone-6>.cd-body{width:23em;height:48em}.cd-iphone-6-plus,.cd-iphone-6-plus>.cd-body{width:25.5em;height:53em}[class^='cd-iphone'] .cd-body{position:relative;border-style:solid;background:#1e1e1e}[class^='cd-iphone-5'] .cd-body{border-radius:2.5em;border-width:.25em}[class^='cd-iphone-5'] .cd-camera{background:#3c3d3d;top:1.7em;left:50%;margin-left:-.25em;width:.5em;height:.5em;border-radius:.25em}[class^='cd-iphone-5'] .cd-ear{background:#292728;top:3em;left:50%;margin-left:-1.75em;width:3.5em;height:.6em;border-radius:.3em}[class^='cd-iphone-5'] .cd-screen{background:black;top:5em;left:50%;margin-left:-8.9em;width:17.8em;height:31em;border:solid .2em black;border-radius:.1em}[class^='cd-iphone-5'] .cd-home{bottom:1em;left:50%;margin-left:-1.75em;width:3.5em;height:3.5em;border-radius:1.75em;border:.2em solid black}[class^='cd-iphone-5'] .cd-sound{top:5.1em;left:-.35em;height:1.8em;width:.2em}[class^='cd-iphone-5'] .cd-sound::before{content:"";top:4em;height:1.4em;width:.2em}[class^='cd-iphone-5'] .cd-sound::after{content:"";top:7.2em;height:1.4em;width:.2em}[class^='cd-iphone-5'] .cd-sleep{top:-.35em;left:13.2em;height:.2em;width:3em}.cd-iphone-5c .cd-home{background:#242324;border-width:0 !important}.cd-iphone-5c .cd-home::after{content:"";display:block;width:1.2em;height:1.2em;top:1.1em;left:50%;margin-left:-.6em;border-radius:.3em;border:1px solid #393839}.cd-iphone-5c .cd-body{border-color:#7ec4fc}.cd-iphone-5c.cd-green .cd-body{border-color:#b3f390}.cd-iphone-5c.cd-red .cd-body{border-color:#fc828d}.cd-iphone-5c.cd-yellow .cd-body{border-color:#fff38a}.cd-iphone-5c.cd-white .cd-body{border-color:#efefee}.cd-iphone-5c .cd-sound,.cd-iphone-5c .cd-sound::before,.cd-iphone-5c .cd-sound::after,.cd-iphone-5c .cd-sleep{background:#7ec4fc}.cd-iphone-5c.cd-green .cd-sound,.cd-iphone-5c.cd-green .cd-sound::before,.cd-iphone-5c.cd-green .cd-sound::after,.cd-iphone-5c.cd-green .cd-sleep{background:#b3f390}.cd-iphone-5c.cd-red .cd-sound,.cd-iphone-5c.cd-red .cd-sound::before,.cd-iphone-5c.cd-red .cd-sound::after,.cd-iphone-5c.cd-red .cd-sleep{background:#fc828d}.cd-iphone-5c.cd-yellow .cd-sound,.cd-iphone-5c.cd-yellow .cd-sound::before,.cd-iphone-5c.cd-yellow .cd-sound::after,.cd-iphone-5c.cd-yellow .cd-sleep{background:#fff38a}.cd-iphone-5c.cd-white .cd-sound,.cd-iphone-5c.cd-white .cd-sound::before,.cd-iphone-5c.cd-white .cd-sound::after,.cd-iphone-5c.cd-white .cd-sleep{background:#efefee}.cd-iphone-5s .cd-body,.cd-iphone-6 .cd-body,.cd-iphone-6-plus .cd-body{border-color:#656565}.cd-iphone-5s.cd-gold .cd-body,.cd-iphone-5s.cd-gold .cd-body .cd-home,.cd-iphone-6.cd-gold .cd-body,.cd-iphone-6.cd-gold .cd-body .cd-home,.cd-iphone-6-plus.cd-gold .cd-body,.cd-iphone-6-plus.cd-gold .cd-body .cd-home{background-color:#fafafa;border-color:#ecdcc8}.cd-iphone-5s.cd-rosegold .cd-body,.cd-iphone-5s.cd-rosegold .cd-body .cd-home,.cd-iphone-6.cd-rosegold .cd-body,.cd-iphone-6.cd-rosegold .cd-body .cd-home,.cd-iphone-6-plus.cd-rosegold .cd-body,.cd-iphone-6-plus.cd-rosegold .cd-body .cd-home{background-color:#fafafa;border-color:#E9C9C5}.cd-iphone-5s.cd-silver .cd-body,.cd-iphone-5s.cd-silver .cd-body .cd-home,.cd-iphone-6.cd-silver .cd-body,.cd-iphone-6.cd-silver .cd-body .cd-home,.cd-iphone-6-plus.cd-silver .cd-body,.cd-iphone-6-plus.cd-silver .cd-body .cd-home{background-color:#fafafa;border-color:#bdbfbe}.cd-iphone-5s .cd-body .cd-home,.cd-iphone-6 .cd-body .cd-home,.cd-iphone-6-plus .cd-body .cd-home{border-color:#2c2b2c}.cd-iphone-5s .cd-sound,.cd-iphone-5s .cd-sound::before,.cd-iphone-5s .cd-sound::after,.cd-iphone-5s .cd-sleep,.cd-iphone-6 .cd-sound,.cd-iphone-6 .cd-sound::before,.cd-iphone-6 .cd-sound::after,.cd-iphone-6 .cd-sleep,.cd-iphone-6-plus .cd-sound,.cd-iphone-6-plus .cd-sound::before,.cd-iphone-6-plus .cd-sound::after,.cd-iphone-6-plus .cd-sleep{background:#656565}.cd-iphone-5s.cd-gold .cd-sound,.cd-iphone-5s.cd-gold .cd-sound::before,.cd-iphone-5s.cd-gold .cd-sound::after,.cd-iphone-5s.cd-gold .cd-sleep,.cd-iphone-6.cd-gold .cd-sound,.cd-iphone-6.cd-gold .cd-sound::before,.cd-iphone-6.cd-gold .cd-sound::after,.cd-iphone-6.cd-gold .cd-sleep,.cd-iphone-6-plus.cd-gold .cd-sound,.cd-iphone-6-plus.cd-gold .cd-sound::before,.cd-iphone-6-plus.cd-gold .cd-sound::after,.cd-iphone-6-plus.cd-gold .cd-sleep{background:#ecdcc8}.cd-iphone-5s.cd-rosegold .cd-sound,.cd-iphone-5s.cd-rosegold .cd-sound::before,.cd-iphone-5s.cd-rosegold .cd-sound::after,.cd-iphone-5s.cd-rosegold .cd-sleep,.cd-iphone-6.cd-rosegold .cd-sound,.cd-iphone-6.cd-rosegold .cd-sound::before,.cd-iphone-6.cd-rosegold .cd-sound::after,.cd-iphone-6.cd-rosegold .cd-sleep,.cd-iphone-6-plus.cd-rosegold .cd-sound,.cd-iphone-6-plus.cd-rosegold .cd-sound::before,.cd-iphone-6-plus.cd-rosegold .cd-sound::after,.cd-iphone-6-plus.cd-rosegold .cd-sleep{background:#E9C9C5}.cd-iphone-5s.cd-silver .cd-sound,.cd-iphone-5s.cd-silver .cd-sound::before,.cd-iphone-5s.cd-silver .cd-sound::after,.cd-iphone-5s.cd-silver .cd-sleep,.cd-iphone-6.cd-silver .cd-sound,.cd-iphone-6.cd-silver .cd-sound::before,.cd-iphone-6.cd-silver .cd-sound::after,.cd-iphone-6.cd-silver .cd-sleep,.cd-iphone-6-plus.cd-silver .cd-sound,.cd-iphone-6-plus.cd-silver .cd-sound::before,.cd-iphone-6-plus.cd-silver .cd-sound::after,.cd-iphone-6-plus.cd-silver .cd-sleep{background:#bdbfbe}[class^='cd-iphone-6'] .cd-body{border-radius:3em;border-width:.4em}[class^='cd-iphone-6'] .cd-camera{background:#3c3d3d;top:2.4em;left:50%;margin-left:-4em;width:.7em;height:.7em;border-radius:.35em}[class^='cd-iphone-6'].cd-gold .cd-camera::after,[class^='cd-iphone-6'].cd-rosegold .cd-camera::after,[class^='cd-iphone-6'].cd-silver .cd-camera::after{content:"";background:#3c3d3d;top:-1.4em;right:-3.5em;width:.5em;height:.5em;border-radius:.25em}[class^='cd-iphone-6'] .cd-ear{background:#292728;top:2.5em;left:50%;margin-left:-2em;width:4em;height:.5em;border-radius:.3em}[class^='cd-iphone-6'] .cd-screen{background:black;top:5em;left:50%;margin-left:-10.5em;width:21em;height:37em;border:solid .2em black;border-radius:.1em}[class^='cd-iphone-6'] .cd-home{bottom:.9em;left:50%;margin-left:-1.75em;width:3.5em;height:3.5em;border-radius:1.75em;border:.2em solid black}[class^='cd-iphone-6'] .cd-sound{top:5em;left:-.5em;height:1.8em;width:.2em}[class^='cd-iphone-6'] .cd-sound::before{content:"";top:4.4em;height:3.4em;width:.2em}[class^='cd-iphone-6'] .cd-sound::after{content:"";top:8.8em;height:3.4em;width:.2em}[class^='cd-iphone-6'] .cd-sleep{top:9.3em;right:-.5em;height:3.4em;width:.2em}.cd-iphone-6-plus .cd-body .cd-screen{top:5em;margin-left:-11.8em;width:23.6em;height:42em;border:solid .2em black;border-radius:.1em}.cd-blueprint .cd-sound{left:-2px !important}.cd-blueprint[class^='cd-iphone-6'] .cd-sleep{background:#000 !important;width:1px !important;border:none !important;right:-2px}.cd-blueprint[class^='cd-iphone-5'] .cd-sleep{background:#000 !important;height:1px !important;border:none !important;top:-2px}.cd-ipad,.cd-ipad>.cd-body{width:33.6em;height:48em}.cd-ipad .cd-body{background:#1e1e1e;border-radius:1.6em;border:0.25em solid #656565}.cd-ipad .cd-camera{background:#3c3d3d;width:.5em;height:.5em;top:1.6em;left:50%;margin-left:-.25em;border-radius:.3em}.cd-ipad .cd-screen{width:30em;height:40em;background:black;top:3.7em;left:50%;margin-left:-15em;border:.2em solid black}.cd-ipad .cd-home{width:2.2em;height:2.2em;border:0.2em solid #2c2b2c;bottom:.8em;border-radius:1.1em;left:50%;margin-left:-1em}.cd-ipad.cd-gold .cd-body{background:#fafafa;border-color:#ecdcc8}.cd-ipad.cd-gold .cd-home{border-color:#ecdcc8}.cd-ipad.cd-silver .cd-body{background:#fafafa;border-color:#bdbfbe}.cd-ipad.cd-silver .cd-home{border-color:#bdbfbe}.cd-mac{width:60em;height:34.8em}.cd-mac .cd-top{width:52em;height:34em;left:50%;margin-left:-26em;background:#d6d5da;border-radius:1.5em 1.5em .6em .6em}.cd-mac .cd-camera{width:.4em;height:.4em;background:#3c3d3d;left:50%;margin-left:-.2em;top:.8em;border-radius:.2em}.cd-mac .cd-screen{width:48em;height:30em;background:#3c3d3d;overflow:hidden;border:1px solid #3c3d3d;top:2em;left:50%;margin-left:-24em}.cd-mac .cd-bottom{width:100%;height:1em;bottom:0;background:#BDBDBD;border-radius:10em/1.2em;border-top-left-radius:0;border-top-right-radius:0}.cd-mac .cd-notch{width:10em;height:.5em;background:#d6d5da;left:50%;margin-left:-5em;bottom:.5em;border-radius:0 0 1em 1em;border-top:1px solid #BDBDBD}.cd-mac.cd-pro .cd-top{background:#1C1C1C}.cd-mac.cd-pro .cd-bottom{border-bottom-left-radius:2em;border-bottom-right-radius:2em}.cd-watch{width:16.6em;height:28em}.cd-watch .cd-bracket{height:19.4em;width:11.2em;left:50%;margin-left:-5.6em;border:0.5em solid #D8D8D8;border-radius:.8em;top:4.3em}.cd-watch [class$='-band']{width:9em;height:7.05em;background:#81DAF5;left:50%;margin-left:-4.5em}.cd-watch .cd-top-band{border-radius:.5em .5em 0 0;top:-1.2em;transform:perspective(30em) rotateX(45deg);-webkit-transform:perspective(30em) rotateX(45deg);-ms-transform:perspective(30em) rotateX(45deg)}.cd-watch .cd-bottom-band{border-radius:0 0 .5em .5em;bottom:-1.2em;transform:perspective(30em) rotateX(-45deg);-webkit-transform:perspective(30em) rotateX(-45deg);-ms-transform:perspective(30em) rotateX(-45deg)}.cd-watch .cd-crown{width:.8em;height:3.4em;right:0;top:50%;margin-top:-5em;background:#D8D8D8;border-radius:0 .4em .4em 0}.cd-watch .cd-button{width:.4em;height:5em;right:.4em;top:50%;background:#D8D8D8;border-radius:0 .3em .3em 0}.cd-watch .cd-body{height:18em;width:15.2em;top:50%;left:50%;margin-top:-9em;margin-left:-7.6em;border:0.7em solid #D8D8D8;border-radius:3em;background:black}.cd-watch .cd-screen{background:black;overflow:hidden;width:11.2em;height:14em;top:50%;left:50%;margin-top:-7em;margin-left:-5.6em;border-radius:.5em}.cd-watch .cd-screen>*{border-radius:.5em}.cd-watch.cd-no-bracket .cd-bracket{display:none}.cd-watch.cd-black .cd-crown,.cd-watch.cd-black .cd-button{background:#585858}.cd-watch.cd-black .cd-bracket,.cd-watch.cd-black .cd-body{border-color:#585858}.cd-watch.cd-gold .cd-crown,.cd-watch.cd-gold .cd-button{background:#e9d296}.cd-watch.cd-gold .cd-bracket,.cd-watch.cd-gold .cd-body{border-color:#e9d296}.cd-watch.cd-rosegold .cd-crown,.cd-watch.cd-rosegold .cd-button{background:#e9bfa9}.cd-watch.cd-rosegold .cd-bracket,.cd-watch.cd-rosegold .cd-body{border-color:#e9bfa9}.cd-watch.cd-white-band [class$='-band']{background:#F2F2F2}.cd-watch.cd-blue-band [class$='-band']{background:#81DAF5}.cd-watch.cd-green-band [class$='-band']{background:#C8FE2E}.cd-watch.cd-pink-band [class$='-band']{background:#F66E64}.cd-watch.cd-black-band [class$='-band']{background:#2E2E2E}.cd-watch.cd-brown-band [class$='-band']{background:#876450}.cd-watch.cd-tan-band [class$='-band']{background:#BEB0A6}.cd-watch.cd-navy-band [class$='-band']{background:#56586b}.cd-watch.cd-red-band [class$='-band']{background:#ff4838}.cd-watch.cd-linked-band [class$='-band']{background:linear-gradient(to bottom, #595959, #595959 10%, #B6B6B6 10%, #B6B6B6);background-size:100% 2em}.cd-watch.cd-blueprint .cd-bracket{top:4.6em;height:18.9em}.cd-watch.cd-blueprint .cd-crown,.cd-watch.cd-blueprint .cd-button{border-left:none !important}.cd-watch.cd-blueprint .cd-screen{border:none !important}body{font-size:14px}[class^='cd-'],[class^='cd-']::after,[class^='cd-']::before{margin:0;padding:0;display:block;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;position:relative}.cd-iphone-5s,.cd-iphone-5c,.cd-iphone-6,.cd-iphone-6-plus,.cd-ipad,.cd-mac{border:0;position:relative;z-index:50;font-size:14px;display:block}[class^='cd-'] *,[class^='cd-'] *::after,[class^='cd-'] *::before{position:absolute}.cd-screen{overflow:hidden}.cd-screen>*{display:none;position:absolute;top:0;left:0;right:0;bottom:0;width:100%;height:100%;color:white;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;-o-user-select:none;user-select:none;-webkit-user-drag:none;-moz-user-drag:none;user-drag:none}.cd-screen>*:first-child{display:block}.cd-screen.cd-screen-scrolling{overflow-y:scroll}.cd-screen.cd-screen-scrolling>*{bottom:none;height:auto}.cd-scale-10{font-size:10% !important}.cd-scale-20{font-size:20% !important}.cd-scale-30{font-size:30% !important}.cd-scale-40{font-size:40% !important}.cd-scale-50{font-size:50% !important}.cd-scale-60{font-size:60% !important}.cd-scale-70{font-size:70% !important}.cd-scale-80{font-size:80% !important}.cd-scale-90{font-size:90% !important}.cd-padded-device{margin:20px}.cd-fill-parent,.cd-device-loader{visibility:hidden}.cd-center{position:relative;margin-left:auto;margin-right:auto;display:block}[class^='cd-'].cd-blueprint *,[class^='cd-'].cd-blueprint *::after,[class^='cd-'].cd-blueprint *::before{background:white !important;border:1px solid #000 !important}.cd-blueprint .cd-sound,.cd-blueprint .cd-sound::before,.cd-blueprint .cd-sound::after{background:#000 !important;width:1px !important;border:none !important}.cd-blueprint .cd-screen *{border:none !important}.cd-slideshow>*:nth-child(n+2){display:none}.cd-smart-loader>:first-child{display:none}.cd-landscape-left.cd-iphone-5s,.cd-landscape-left.cd-iphone-5c,.cd-landscape-right.cd-iphone-5s,.cd-landscape-right.cd-iphone-5c{height:19.8em;width:42em}.cd-landscape-left.cd-iphone-6,.cd-landscape-right.cd-iphone-6{height:23em;width:48em}.cd-landscape-left.cd-iphone-6-plus,.cd-landscape-right.cd-iphone-6-plus{height:25.5em;width:53em}.cd-landscape-left.cd-ipad,.cd-landscape-right.cd-ipad{height:33.6em;width:48em}.cd-landscape-left>.cd-body,.cd-landscape-left>.cd-body>.cd-screen{transform-origin:0 0;-webkit-transform-origin:0 0;-ms-transform-origin:0 0}.cd-landscape-left>.cd-body{transform:rotate(-90deg) translate(-100%, 0);-webkit-transform:rotate(-90deg) translate(-100%, 0);-ms-transform:rotate(-90deg) translate(-100%, 0)}.cd-landscape-left>.cd-body>.cd-screen{transform:rotate(90deg) translate(0, -100%);-webkit-transform:rotate(90deg) translate(0, -100%);-ms-transform:rotate(90deg) translate(0, -100%)}.cd-landscape-left:not(.cd-landscape-fixed-screen).cd-iphone-5s>.cd-body>.cd-screen,.cd-landscape-left:not(.cd-landscape-fixed-screen).cd-iphone-5c>.cd-body>.cd-screen{height:17.8em;width:31em}.cd-landscape-left:not(.cd-landscape-fixed-screen).cd-iphone-6>.cd-body>.cd-screen{height:21em;width:37em}.cd-landscape-left:not(.cd-landscape-fixed-screen).cd-iphone-6-plus>.cd-body>.cd-screen{height:23.6em;width:42em}.cd-landscape-left:not(.cd-landscape-fixed-screen).cd-ipad>.cd-body>.cd-screen{height:30em;width:40em}.cd-landscape-right>.cd-body,.cd-landscape-right>.cd-body>.cd-screen{transform-origin:0 0;-webkit-transform-origin:0 0;-ms-transform-origin:0 0}.cd-landscape-right>.cd-body{transform:rotate(90deg) translate(0, -100%);-webkit-transform:rotate(90deg) translate(0, -100%);-ms-transform:rotate(90deg) translate(0, -100%)}.cd-landscape-right>.cd-body>.cd-screen{transform:rotate(-90deg) translate(-100%, 0);-webkit-transform:rotate(-90deg) translate(-100%, 0);-ms-transform:rotate(-90deg) translate(-100%, 0)}.cd-landscape-right:not(.cd-landscape-fixed-screen).cd-iphone-5s>.cd-body>.cd-screen,.cd-landscape-right:not(.cd-landscape-fixed-screen).cd-iphone-5c>.cd-body>.cd-screen{height:17.8em;width:31em}.cd-landscape-right:not(.cd-landscape-fixed-screen).cd-iphone-6>.cd-body>.cd-screen{height:21em;width:37em}.cd-landscape-right:not(.cd-landscape-fixed-screen).cd-iphone-6-plus>.cd-body>.cd-screen{height:23.6em;width:42em}.cd-landscape-right:not(.cd-landscape-fixed-screen).cd-ipad>.cd-body>.cd-screen{height:30em;width:40em}.cd-landscape-fixed-screen>.cd-body>.cd-screen{transform:none;-webkit-transform:none;-ms-transform:none;transform-origin:none;-webkit-transform-origin:none;-ms-transform-origin:none} 3 | -------------------------------------------------------------------------------- /docs/css/poet.css: -------------------------------------------------------------------------------- 1 | /*! Poet v0.0.1 | MIT license | Maintained by Stuart Yamartino */ 2 | html, 3 | body { 4 | text-rendering: optimizeLegibility; 5 | -webkit-font-smoothing: antialiased; 6 | -moz-osx-font-smoothing: grayscale; 7 | font-size: 16px; 8 | line-height: 1.61803; 9 | color: #333333; } 10 | html p, 11 | body p { 12 | margin-top: 0; } 13 | 14 | html { 15 | -ms-text-size-adjust: 100%; 16 | -webkit-text-size-adjust: 100%; } 17 | 18 | a { 19 | background-color: transparent; } 20 | 21 | a:active, 22 | a:hover { 23 | outline: 0; } 24 | 25 | h1, .h1, 26 | h2, .h2, 27 | h3, .h3, 28 | h4, .h4, 29 | h5, .h5, 30 | h6, .h6 { 31 | display: block; 32 | margin-top: 0.5rem; 33 | margin-bottom: 1rem; 34 | font-family: inherit; 35 | line-height: 1.1; 36 | color: inherit; } 37 | h1 small, 38 | h1 .small, .h1 small, 39 | .h1 .small, 40 | h2 small, 41 | h2 .small, .h2 small, 42 | .h2 .small, 43 | h3 small, 44 | h3 .small, .h3 small, 45 | .h3 .small, 46 | h4 small, 47 | h4 .small, .h4 small, 48 | .h4 .small, 49 | h5 small, 50 | h5 .small, .h5 small, 51 | .h5 .small, 52 | h6 small, 53 | h6 .small, .h6 small, 54 | .h6 .small { 55 | color: #818a91; } 56 | 57 | h1, .h1 { 58 | font-size: 1.8rem; } 59 | 60 | @media only screen and (min-width: 420px) { 61 | h1, .h1 { 62 | font-size: 1.87rem; } } 63 | @media only screen and (min-width: 480px) { 64 | h1, .h1 { 65 | font-size: 1.94rem; } } 66 | @media only screen and (min-width: 540px) { 67 | h1, .h1 { 68 | font-size: 2.01rem; } } 69 | @media only screen and (min-width: 600px) { 70 | h1, .h1 { 71 | font-size: 2.08rem; } } 72 | @media only screen and (min-width: 660px) { 73 | h1, .h1 { 74 | font-size: 2.15rem; } } 75 | @media only screen and (min-width: 720px) { 76 | h1, .h1 { 77 | font-size: 2.22rem; } } 78 | @media only screen and (min-width: 780px) { 79 | h1, .h1 { 80 | font-size: 2.29rem; } } 81 | @media only screen and (min-width: 840px) { 82 | h1, .h1 { 83 | font-size: 2.36rem; } } 84 | @media only screen and (min-width: 900px) { 85 | h1, .h1 { 86 | font-size: 2.43rem; } } 87 | @media only screen and (min-width: 960px) { 88 | h1, .h1 { 89 | font-size: 2.5rem; } } 90 | h2, .h2 { 91 | font-size: 1.6rem; } 92 | 93 | @media only screen and (min-width: 420px) { 94 | h2, .h2 { 95 | font-size: 1.64rem; } } 96 | @media only screen and (min-width: 480px) { 97 | h2, .h2 { 98 | font-size: 1.68rem; } } 99 | @media only screen and (min-width: 540px) { 100 | h2, .h2 { 101 | font-size: 1.72rem; } } 102 | @media only screen and (min-width: 600px) { 103 | h2, .h2 { 104 | font-size: 1.76rem; } } 105 | @media only screen and (min-width: 660px) { 106 | h2, .h2 { 107 | font-size: 1.8rem; } } 108 | @media only screen and (min-width: 720px) { 109 | h2, .h2 { 110 | font-size: 1.84rem; } } 111 | @media only screen and (min-width: 780px) { 112 | h2, .h2 { 113 | font-size: 1.88rem; } } 114 | @media only screen and (min-width: 840px) { 115 | h2, .h2 { 116 | font-size: 1.92rem; } } 117 | @media only screen and (min-width: 900px) { 118 | h2, .h2 { 119 | font-size: 1.96rem; } } 120 | @media only screen and (min-width: 960px) { 121 | h2, .h2 { 122 | font-size: 2rem; } } 123 | h3, .h3 { 124 | font-size: 1.45rem; } 125 | 126 | @media only screen and (min-width: 420px) { 127 | h3, .h3 { 128 | font-size: 1.475rem; } } 129 | @media only screen and (min-width: 480px) { 130 | h3, .h3 { 131 | font-size: 1.5rem; } } 132 | @media only screen and (min-width: 540px) { 133 | h3, .h3 { 134 | font-size: 1.525rem; } } 135 | @media only screen and (min-width: 600px) { 136 | h3, .h3 { 137 | font-size: 1.55rem; } } 138 | @media only screen and (min-width: 660px) { 139 | h3, .h3 { 140 | font-size: 1.575rem; } } 141 | @media only screen and (min-width: 720px) { 142 | h3, .h3 { 143 | font-size: 1.6rem; } } 144 | @media only screen and (min-width: 780px) { 145 | h3, .h3 { 146 | font-size: 1.625rem; } } 147 | @media only screen and (min-width: 840px) { 148 | h3, .h3 { 149 | font-size: 1.65rem; } } 150 | @media only screen and (min-width: 900px) { 151 | h3, .h3 { 152 | font-size: 1.675rem; } } 153 | @media only screen and (min-width: 960px) { 154 | h3, .h3 { 155 | font-size: 1.7rem; } } 156 | h4, .h4 { 157 | font-size: 1.3rem; } 158 | 159 | @media only screen and (min-width: 420px) { 160 | h4, .h4 { 161 | font-size: 1.32rem; } } 162 | @media only screen and (min-width: 480px) { 163 | h4, .h4 { 164 | font-size: 1.34rem; } } 165 | @media only screen and (min-width: 540px) { 166 | h4, .h4 { 167 | font-size: 1.36rem; } } 168 | @media only screen and (min-width: 600px) { 169 | h4, .h4 { 170 | font-size: 1.38rem; } } 171 | @media only screen and (min-width: 660px) { 172 | h4, .h4 { 173 | font-size: 1.4rem; } } 174 | @media only screen and (min-width: 720px) { 175 | h4, .h4 { 176 | font-size: 1.42rem; } } 177 | @media only screen and (min-width: 780px) { 178 | h4, .h4 { 179 | font-size: 1.44rem; } } 180 | @media only screen and (min-width: 840px) { 181 | h4, .h4 { 182 | font-size: 1.46rem; } } 183 | @media only screen and (min-width: 900px) { 184 | h4, .h4 { 185 | font-size: 1.48rem; } } 186 | @media only screen and (min-width: 960px) { 187 | h4, .h4 { 188 | font-size: 1.5rem; } } 189 | h5, .h5 { 190 | font-size: 1.15rem; } 191 | 192 | @media only screen and (min-width: 420px) { 193 | h5, .h5 { 194 | font-size: 1.16rem; } } 195 | @media only screen and (min-width: 480px) { 196 | h5, .h5 { 197 | font-size: 1.17rem; } } 198 | @media only screen and (min-width: 540px) { 199 | h5, .h5 { 200 | font-size: 1.18rem; } } 201 | @media only screen and (min-width: 600px) { 202 | h5, .h5 { 203 | font-size: 1.19rem; } } 204 | @media only screen and (min-width: 660px) { 205 | h5, .h5 { 206 | font-size: 1.2rem; } } 207 | @media only screen and (min-width: 720px) { 208 | h5, .h5 { 209 | font-size: 1.21rem; } } 210 | @media only screen and (min-width: 780px) { 211 | h5, .h5 { 212 | font-size: 1.22rem; } } 213 | @media only screen and (min-width: 840px) { 214 | h5, .h5 { 215 | font-size: 1.23rem; } } 216 | @media only screen and (min-width: 900px) { 217 | h5, .h5 { 218 | font-size: 1.24rem; } } 219 | @media only screen and (min-width: 960px) { 220 | h5, .h5 { 221 | font-size: 1.25rem; } } 222 | h6, .h6 { 223 | font-size: 1rem; } 224 | 225 | .mighty-1, 226 | .mighty-2, 227 | .mighty-3, 228 | .mighty-4 { 229 | display: block; 230 | margin-top: 0.5rem; 231 | margin-bottom: 1rem; 232 | font-family: inherit; 233 | line-height: 1.1; 234 | color: inherit; } 235 | .mighty-1 small, 236 | .mighty-1 .small, 237 | .mighty-2 small, 238 | .mighty-2 .small, 239 | .mighty-3 small, 240 | .mighty-3 .small, 241 | .mighty-4 small, 242 | .mighty-4 .small { 243 | color: #818a91; } 244 | 245 | .mighty-1 { 246 | font-size: 4rem; } 247 | 248 | @media only screen and (min-width: 420px) { 249 | .mighty-1 { 250 | font-size: 4.2rem; } } 251 | @media only screen and (min-width: 480px) { 252 | .mighty-1 { 253 | font-size: 4.4rem; } } 254 | @media only screen and (min-width: 540px) { 255 | .mighty-1 { 256 | font-size: 4.6rem; } } 257 | @media only screen and (min-width: 600px) { 258 | .mighty-1 { 259 | font-size: 4.8rem; } } 260 | @media only screen and (min-width: 660px) { 261 | .mighty-1 { 262 | font-size: 5rem; } } 263 | @media only screen and (min-width: 720px) { 264 | .mighty-1 { 265 | font-size: 5.2rem; } } 266 | @media only screen and (min-width: 780px) { 267 | .mighty-1 { 268 | font-size: 5.4rem; } } 269 | @media only screen and (min-width: 840px) { 270 | .mighty-1 { 271 | font-size: 5.6rem; } } 272 | @media only screen and (min-width: 900px) { 273 | .mighty-1 { 274 | font-size: 5.8rem; } } 275 | @media only screen and (min-width: 960px) { 276 | .mighty-1 { 277 | font-size: 6rem; } } 278 | .mighty-2 { 279 | font-size: 3.6rem; } 280 | 281 | @media only screen and (min-width: 420px) { 282 | .mighty-2 { 283 | font-size: 3.74rem; } } 284 | @media only screen and (min-width: 480px) { 285 | .mighty-2 { 286 | font-size: 3.88rem; } } 287 | @media only screen and (min-width: 540px) { 288 | .mighty-2 { 289 | font-size: 4.02rem; } } 290 | @media only screen and (min-width: 600px) { 291 | .mighty-2 { 292 | font-size: 4.16rem; } } 293 | @media only screen and (min-width: 660px) { 294 | .mighty-2 { 295 | font-size: 4.3rem; } } 296 | @media only screen and (min-width: 720px) { 297 | .mighty-2 { 298 | font-size: 4.44rem; } } 299 | @media only screen and (min-width: 780px) { 300 | .mighty-2 { 301 | font-size: 4.58rem; } } 302 | @media only screen and (min-width: 840px) { 303 | .mighty-2 { 304 | font-size: 4.72rem; } } 305 | @media only screen and (min-width: 900px) { 306 | .mighty-2 { 307 | font-size: 4.86rem; } } 308 | @media only screen and (min-width: 960px) { 309 | .mighty-2 { 310 | font-size: 5rem; } } 311 | .mighty-3 { 312 | font-size: 3rem; } 313 | 314 | @media only screen and (min-width: 420px) { 315 | .mighty-3 { 316 | font-size: 3.1rem; } } 317 | @media only screen and (min-width: 480px) { 318 | .mighty-3 { 319 | font-size: 3.2rem; } } 320 | @media only screen and (min-width: 540px) { 321 | .mighty-3 { 322 | font-size: 3.3rem; } } 323 | @media only screen and (min-width: 600px) { 324 | .mighty-3 { 325 | font-size: 3.4rem; } } 326 | @media only screen and (min-width: 660px) { 327 | .mighty-3 { 328 | font-size: 3.5rem; } } 329 | @media only screen and (min-width: 720px) { 330 | .mighty-3 { 331 | font-size: 3.6rem; } } 332 | @media only screen and (min-width: 780px) { 333 | .mighty-3 { 334 | font-size: 3.7rem; } } 335 | @media only screen and (min-width: 840px) { 336 | .mighty-3 { 337 | font-size: 3.8rem; } } 338 | @media only screen and (min-width: 900px) { 339 | .mighty-3 { 340 | font-size: 3.9rem; } } 341 | @media only screen and (min-width: 960px) { 342 | .mighty-3 { 343 | font-size: 4rem; } } 344 | .mighty-4 { 345 | font-size: 2.7rem; } 346 | 347 | @media only screen and (min-width: 420px) { 348 | .mighty-4 { 349 | font-size: 2.78rem; } } 350 | @media only screen and (min-width: 480px) { 351 | .mighty-4 { 352 | font-size: 2.86rem; } } 353 | @media only screen and (min-width: 540px) { 354 | .mighty-4 { 355 | font-size: 2.94rem; } } 356 | @media only screen and (min-width: 600px) { 357 | .mighty-4 { 358 | font-size: 3.02rem; } } 359 | @media only screen and (min-width: 660px) { 360 | .mighty-4 { 361 | font-size: 3.1rem; } } 362 | @media only screen and (min-width: 720px) { 363 | .mighty-4 { 364 | font-size: 3.18rem; } } 365 | @media only screen and (min-width: 780px) { 366 | .mighty-4 { 367 | font-size: 3.26rem; } } 368 | @media only screen and (min-width: 840px) { 369 | .mighty-4 { 370 | font-size: 3.34rem; } } 371 | @media only screen and (min-width: 900px) { 372 | .mighty-4 { 373 | font-size: 3.42rem; } } 374 | @media only screen and (min-width: 960px) { 375 | .mighty-4 { 376 | font-size: 3.5rem; } } 377 | .text-success { 378 | color: #5cb85c; } 379 | 380 | .text-warning { 381 | color: #f0ad4e; } 382 | 383 | .text-danger { 384 | color: #d9534f; } 385 | 386 | .text-muted { 387 | color: #818a91; } 388 | 389 | .text-info { 390 | color: #0275d8; } 391 | 392 | i, 393 | em, 394 | .text-italic { 395 | font-style: italic; } 396 | 397 | .text-strike { 398 | text-decoration: line-through; } 399 | 400 | .text-underline, a:hover, 401 | a:visited:hover, 402 | .text-link:hover, 403 | .text-link:visited:hover { 404 | text-decoration: underline; } 405 | 406 | mark, 407 | .text-highlight { 408 | padding: .2em; 409 | color: #000; 410 | background: #FFFF80; } 411 | 412 | .text-user-highlight::-moz-selection, 413 | .text-user-highlight *::-moz-selection { 414 | color: #000; 415 | background: #FFFF80; } 416 | 417 | .text-user-highlight::selection, 418 | .text-user-highlight *::selection { 419 | color: #000; 420 | background: #FFFF80; } 421 | 422 | small, 423 | .text-small { 424 | font-size: .80em; } 425 | 426 | .text-uppercase { 427 | text-transform: uppercase; } 428 | 429 | .text-lowercase { 430 | text-transform: lowercase; } 431 | 432 | .text-capitalize { 433 | text-transform: capitalize; } 434 | 435 | sub, 436 | sup, 437 | .text-superscript, 438 | .text-subscript { 439 | font-size: 75%; 440 | line-height: 0; 441 | position: relative; 442 | vertical-align: baseline; } 443 | 444 | sup, 445 | .text-superscript { 446 | top: -0.5em; 447 | left: 0.05em; } 448 | 449 | sub, 450 | .text-subscript { 451 | bottom: -0.25em; 452 | left: 0.05em; } 453 | 454 | .text-smart-underline, .text-smart-underline-noshadow { 455 | display: inline !important; 456 | text-decoration: none; 457 | background-image: linear-gradient(to bottom, transparent 50%, #333333 50%); 458 | background-repeat: repeat-x; 459 | background-size: 0.1em 0.1em; 460 | background-position: 0 .99em; 461 | text-shadow: .032em 0 #fff, .06em 0 0 #fff, -.032em 0 #fff, -.06em 0 0 #fff; } 462 | @media not all, only screen and (min-resolution: 2dppx), only screen and (-webkit-min-device-pixel-ratio: 2) { 463 | .text-smart-underline, .text-smart-underline-noshadow { 464 | background-image: linear-gradient(to bottom, #333333 30%, transparent 30%); 465 | background-position: 0 1.04em; } } 466 | 467 | .text-smart-underline-noshadow { 468 | text-shadow: none; } 469 | 470 | a.text-smart-underline, a.text-smart-underline-noshadow, 471 | .text-link.text-smart-underline, 472 | .text-link.text-smart-underline-noshadow { 473 | background-image: none; } 474 | 475 | a:hover.text-smart-underline, a.text-smart-underline-noshadow:hover, 476 | .text-link:hover.text-smart-underline, 477 | .text-link.text-smart-underline-noshadow:hover { 478 | text-decoration: none; 479 | background-image: linear-gradient(to bottom, transparent 50%, #084B8A 50%); } 480 | @media not all, only screen and (min-resolution: 2dppx), only screen and (-webkit-min-device-pixel-ratio: 2) { 481 | a:hover.text-smart-underline, a.text-smart-underline-noshadow:hover, 482 | .text-link:hover.text-smart-underline, 483 | .text-link.text-smart-underline-noshadow:hover { 484 | background-image: linear-gradient(to bottom, #084B8A 30%, transparent 30%); } } 485 | 486 | .text-center-block { 487 | width: 100%; 488 | text-align: center; 489 | display: block; } 490 | 491 | .text-left { 492 | text-align: left; } 493 | 494 | .text-center { 495 | text-align: center; } 496 | 497 | .text-right { 498 | text-align: right; } 499 | 500 | .text-ultra-light { 501 | font-weight: 100; } 502 | 503 | .text-light, .mighty-1, 504 | .mighty-2, 505 | .mighty-3, 506 | .mighty-4 { 507 | font-weight: 300; } 508 | 509 | .text-normal, html, 510 | body { 511 | font-weight: normal; } 512 | 513 | .text-heavy { 514 | font-weight: 500; } 515 | 516 | b, 517 | strong, 518 | .text-bold, 519 | h1, 520 | .h1, 521 | h2, 522 | .h2, 523 | h3, 524 | .h3, 525 | h4, 526 | .h4, 527 | h5, 528 | .h5, 529 | h6, 530 | .h6, 531 | .text-chapter::first-letter, 532 | .text-chapter-cursive::first-letter { 533 | font-weight: bold; } 534 | 535 | .font-san-serif, html, 536 | body, 537 | .font-sanserif { 538 | font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; } 539 | 540 | .font-serif { 541 | font-family: TimesNewRoman, 'Times New Roman', Times, Baskerville, Georgia, serif; } 542 | 543 | .font-monospace { 544 | font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace; } 545 | 546 | .font-cursive, .text-chapter-cursive::first-letter { 547 | font-family: cursive; } 548 | 549 | .text-chapter::first-letter, .text-chapter-cursive::first-letter { 550 | font-size: 6.5em; 551 | text-transform: uppercase; 552 | display: block; 553 | float: left; 554 | margin: 0 0.17em 0 0; 555 | line-height: .9; } 556 | 557 | .text-chapter.text-indent, .text-indent.text-chapter-cursive::first-letter, 558 | .text-chapter-cursive.text-indent { 559 | text-indent: 0; } 560 | 561 | .text-indent { 562 | text-indent: 2.5em; } 563 | 564 | .text-fluid { 565 | font-size: 1.1rem; } 566 | 567 | @media only screen and (min-width: 420px) { 568 | .text-fluid { 569 | font-size: 1.15rem; } } 570 | @media only screen and (min-width: 480px) { 571 | .text-fluid { 572 | font-size: 1.2rem; } } 573 | @media only screen and (min-width: 540px) { 574 | .text-fluid { 575 | font-size: 1.25rem; } } 576 | @media only screen and (min-width: 600px) { 577 | .text-fluid { 578 | font-size: 1.3rem; } } 579 | @media only screen and (min-width: 660px) { 580 | .text-fluid { 581 | font-size: 1.35rem; } } 582 | @media only screen and (min-width: 720px) { 583 | .text-fluid { 584 | font-size: 1.4rem; } } 585 | @media only screen and (min-width: 780px) { 586 | .text-fluid { 587 | font-size: 1.45rem; } } 588 | @media only screen and (min-width: 840px) { 589 | .text-fluid { 590 | font-size: 1.5rem; } } 591 | @media only screen and (min-width: 900px) { 592 | .text-fluid { 593 | font-size: 1.55rem; } } 594 | @media only screen and (min-width: 960px) { 595 | .text-fluid { 596 | font-size: 1.6rem; } } 597 | p { 598 | font-size: 0.9rem; } 599 | 600 | @media only screen and (min-width: 420px) { 601 | p { 602 | font-size: 0.91rem; } } 603 | @media only screen and (min-width: 480px) { 604 | p { 605 | font-size: 0.92rem; } } 606 | @media only screen and (min-width: 540px) { 607 | p { 608 | font-size: 0.93rem; } } 609 | @media only screen and (min-width: 600px) { 610 | p { 611 | font-size: 0.94rem; } } 612 | @media only screen and (min-width: 660px) { 613 | p { 614 | font-size: 0.95rem; } } 615 | @media only screen and (min-width: 720px) { 616 | p { 617 | font-size: 0.96rem; } } 618 | @media only screen and (min-width: 780px) { 619 | p { 620 | font-size: 0.97rem; } } 621 | @media only screen and (min-width: 840px) { 622 | p { 623 | font-size: 0.98rem; } } 624 | @media only screen and (min-width: 900px) { 625 | p { 626 | font-size: 0.99rem; } } 627 | @media only screen and (min-width: 960px) { 628 | p { 629 | font-size: 1rem; } } 630 | a, 631 | a:visited, 632 | .text-link, 633 | .text-link:visited { 634 | color: #084B8A; 635 | text-decoration: none; 636 | cursor: pointer; } 637 | a:hover, 638 | a:visited:hover, 639 | .text-link:hover, 640 | .text-link:visited:hover { 641 | color: #084B8A; } 642 | 643 | .text-unselectable { 644 | -webkit-user-select: none; 645 | -khtml-user-select: none; 646 | -moz-user-select: -moz-none; 647 | -ms-user-select: none; 648 | user-select: none; } 649 | -------------------------------------------------------------------------------- /docs/css/prism.css: -------------------------------------------------------------------------------- 1 | /* http://prismjs.com/download.html?themes=prism&languages=markup+css+clike+javascript */ 2 | /** 3 | * prism.js default theme for JavaScript, CSS and HTML 4 | * Based on dabblet (http://dabblet.com) 5 | * @author Lea Verou 6 | */ 7 | 8 | code[class*="language-"], 9 | pre[class*="language-"] { 10 | color: black; 11 | text-shadow: 0 1px white; 12 | font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace; 13 | direction: ltr; 14 | text-align: left; 15 | white-space: pre; 16 | word-spacing: normal; 17 | word-break: normal; 18 | word-wrap: normal; 19 | line-height: 1.5; 20 | 21 | -moz-tab-size: 4; 22 | -o-tab-size: 4; 23 | tab-size: 4; 24 | 25 | -webkit-hyphens: none; 26 | -moz-hyphens: none; 27 | -ms-hyphens: none; 28 | hyphens: none; 29 | } 30 | 31 | pre[class*="language-"]::-moz-selection, pre[class*="language-"] ::-moz-selection, 32 | code[class*="language-"]::-moz-selection, code[class*="language-"] ::-moz-selection { 33 | text-shadow: none; 34 | background: #b3d4fc; 35 | } 36 | 37 | pre[class*="language-"]::selection, pre[class*="language-"] ::selection, 38 | code[class*="language-"]::selection, code[class*="language-"] ::selection { 39 | text-shadow: none; 40 | background: #b3d4fc; 41 | } 42 | 43 | @media print { 44 | code[class*="language-"], 45 | pre[class*="language-"] { 46 | text-shadow: none; 47 | } 48 | } 49 | 50 | /* Code blocks */ 51 | pre[class*="language-"] { 52 | padding: 1em; 53 | margin: .5em 0; 54 | overflow: auto; 55 | } 56 | 57 | :not(pre) > code[class*="language-"], 58 | pre[class*="language-"] { 59 | background: #f5f2f0; 60 | } 61 | 62 | /* Inline code */ 63 | :not(pre) > code[class*="language-"] { 64 | padding: .1em; 65 | border-radius: .3em; 66 | white-space: normal; 67 | } 68 | 69 | .token.comment, 70 | .token.prolog, 71 | .token.doctype, 72 | .token.cdata { 73 | color: slategray; 74 | } 75 | 76 | .token.punctuation { 77 | color: #999; 78 | } 79 | 80 | .namespace { 81 | opacity: .7; 82 | } 83 | 84 | .token.property, 85 | .token.tag, 86 | .token.boolean, 87 | .token.number, 88 | .token.constant, 89 | .token.symbol, 90 | .token.deleted { 91 | color: #905; 92 | } 93 | 94 | .token.selector, 95 | .token.attr-name, 96 | .token.string, 97 | .token.char, 98 | .token.builtin, 99 | .token.inserted { 100 | color: #690; 101 | } 102 | 103 | .token.operator, 104 | .token.entity, 105 | .token.url, 106 | .language-css .token.string, 107 | .style .token.string { 108 | color: #a67f59; 109 | background: hsla(0, 0%, 100%, .5); 110 | } 111 | 112 | .token.atrule, 113 | .token.attr-value, 114 | .token.keyword { 115 | color: #07a; 116 | } 117 | 118 | .token.function { 119 | color: #DD4A68; 120 | } 121 | 122 | .token.regex, 123 | .token.important, 124 | .token.variable { 125 | color: #e90; 126 | } 127 | 128 | .token.important, 129 | .token.bold { 130 | font-weight: bold; 131 | } 132 | .token.italic { 133 | font-style: italic; 134 | } 135 | 136 | .token.entity { 137 | cursor: help; 138 | } 139 | 140 | -------------------------------------------------------------------------------- /docs/css/style.css: -------------------------------------------------------------------------------- 1 | *{ 2 | box-sizing: border-box; 3 | } 4 | 5 | html, body{ 6 | font-family: 'Open Sans', sans-serif; 7 | } 8 | 9 | header{ 10 | color: white; 11 | padding: 20px; 12 | margin-bottom: 40px; 13 | } 14 | 15 | .device-container{ 16 | background: white; 17 | } 18 | 19 | .device-circle{ 20 | position: absolute; 21 | top: 50%; 22 | left: 50%; 23 | margin-top: -5em; 24 | margin-left: -5em; 25 | width: 10em; 26 | height: 10em; 27 | border-radius: 50%; 28 | cursor: pointer; 29 | } 30 | 31 | .iphone-circle{ 32 | /* Permalink - use to edit and share this gradient: http://colorzilla.com/gradient-editor/#8921a5+0,7db9e8+100&1+0,0+100 */ 33 | background: -moz-linear-gradient(top, rgba(137,33,165,1) 0%, rgba(125,185,232,0) 100%); /* FF3.6-15 */ 34 | background: -webkit-linear-gradient(top, rgba(137,33,165,1) 0%,rgba(125,185,232,0) 100%); /* Chrome10-25,Safari5.1-6 */ 35 | background: linear-gradient(top bottom, rgba(137,33,165,1) 0%,rgba(125,185,232,0) 100%); /* W3C, IE10+, FF16+, Chrome26+, Opera12+, Safari7+ */ 36 | background-color: #d9534f; 37 | } 38 | 39 | .mac-circle{ 40 | /* Permalink - use to edit and share this gradient: http://colorzilla.com/gradient-editor/#3e54ad+0,7db9e8+100&1+0,0+100 */ 41 | background: -moz-linear-gradient(top, rgba(62,84,173,1) 0%, rgba(125,185,232,0) 100%); /* FF3.6-15 */ 42 | background: -webkit-linear-gradient(top, rgba(62,84,173,1) 0%,rgba(125,185,232,0) 100%); /* Chrome10-25,Safari5.1-6 */ 43 | background: linear-gradient(to bottom, rgba(62,84,173,1) 0%,rgba(125,185,232,0) 100%); /* W3C, IE10+, FF16+, Chrome26+, Opera12+, Safari7+ */ 44 | background-color: #d9534f; 45 | } 46 | 47 | .pressure-failed{ 48 | display: none; 49 | margin: 10px 0 0 0; 50 | } 51 | 52 | .supported-device{ 53 | width: 100%; 54 | padding: 0.5rem; 55 | } 56 | 57 | .supported-device .device-name{ 58 | font-size: 1.2rem; 59 | font-weight: 600; 60 | } 61 | 62 | .fixed-docs-nav{ 63 | position: fixed; 64 | top: 0; 65 | left: 0; 66 | background: white; 67 | width: 100%; 68 | padding: 5px; 69 | display: none; 70 | border-bottom: 1px solid #eee; 71 | } 72 | 73 | .navbar-toggler{ 74 | color: white; 75 | } 76 | 77 | .nav-pills .nav-link:hover{ 78 | color: #fff; 79 | cursor: default; 80 | background-color: #0275d8; 81 | cursor: pointer; 82 | } 83 | 84 | .why-pressure-item{ 85 | width: 100%; 86 | border: 1px solid #eee; 87 | text-align: center; 88 | padding: 1rem; 89 | margin-bottom: 2rem; 90 | } 91 | 92 | .why-pressure-item img{ 93 | max-width: 100px; 94 | padding-bottom: 1rem; 95 | } 96 | 97 | .mighty-2{ 98 | margin: 0; 99 | } 100 | 101 | .container{ 102 | max-width: 900px; 103 | } 104 | 105 | .doc-section{ 106 | border: 5px solid #f7f7f9; 107 | border-radius: 5px; 108 | margin-bottom: 40px; 109 | } 110 | 111 | .twitter-share-button{ 112 | -webkit-transform: scale(1.1); 113 | -moz-transform: scale(1.1); 114 | -ms-transform: scale(1.1); 115 | transform: scale(1.1); 116 | } 117 | 118 | .doc-top{ 119 | padding: 10px; 120 | } 121 | 122 | .doc-title{ 123 | font-weight: 600 !important; 124 | margin-bottom: 0.6rem; 125 | } 126 | 127 | pre{ 128 | background: #f7f7f9 !important; 129 | margin: 0 !important; 130 | border: 0 !important; 131 | } 132 | 133 | hr{ 134 | margin-top:10px; 135 | margin-bottom:10px; 136 | border:0; 137 | border-top:1px solid #eee 138 | } 139 | 140 | .social-buttons{ 141 | display: table; 142 | margin: 10px auto 20px auto; 143 | position: relative; 144 | } 145 | 146 | footer{ 147 | padding: 20px 0; 148 | border-top: 1px solid #f7f7f9; 149 | } 150 | 151 | #demo-1{ 152 | 153 | } 154 | .element{ 155 | width: 200px; 156 | padding: 20px; 157 | background: rgb(255,100,0); 158 | margin-bottom: 10px; 159 | border-radius: 5px; 160 | } 161 | h3{ 162 | margin-top: 0; 163 | margin-bottom: 10px; 164 | } 165 | 166 | .wrap-peanuts{ 167 | width: 100% !important; 168 | overflow: hidden; 169 | } 170 | 171 | #peanuts{ 172 | width: 100% !important; 173 | -moz-filter: blur(20px); 174 | -webkit-filter: blur(20px); 175 | filter: blur(20px); 176 | cursor: default; 177 | } 178 | 179 | #spinning-cube{ 180 | width: 200px; 181 | height: 200px; 182 | -webkit-transform: rotateY(0deg); 183 | cursor: default; 184 | } 185 | -------------------------------------------------------------------------------- /docs/documentation.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | Pressure.js 15 | 16 | 17 | 18 | 43 |
44 | 45 | 65 | 66 |
67 |
68 | 88 |
89 |
90 | 91 | 92 |
93 | 94 |
1. Installation
95 |

Pressure is really simple to install, you can use npm or bower, or head over to the github and download the repo itself. All you need is the pressure.min.js or the pressure.js file.

96 |
97 |
98 |
bower
99 |
100 |
101 |
bower install pressure --save
102 |
103 |
104 |
105 |
106 |
npm
107 |
108 |
109 |
npm install pressure --save
110 |
111 |
112 | 113 | 114 |
115 | 116 |
2. Setup
117 |

You can use pressure with browserify, debowerify, or anything that uses CommonJS to include packages. Pressure can be used normally if no module object exists.

118 |
119 |
120 |
// Regular
121 | Pressure.set('#test', {
122 |   change: function(force, event){
123 |     console.log(force);
124 |   }
125 | });
126 |
127 |
128 |
129 |
130 |
// Browserify, Debowerify, CommonJS
131 | var Pressure = require('pressure');
132 | 
133 | Pressure.set('#test', {
134 |   change: function(force, event){
135 |     console.log(force);
136 |   }
137 | });
138 |
139 |
140 |
141 |
142 |
// RequireJS
143 | requirejs(['pressure'], function( Pressure ) {
144 | 
145 |   Pressure.set('#test', {
146 |     change: function(force, event){
147 |       console.log(force);
148 |     }
149 |   });
150 | 
151 | });
152 | 
153 |
154 |
155 | 156 | 157 |
158 | 159 |
3. Usage
160 |

Pressure has a really simple method signature. The first argument is the element(s) you are targeting and the second argument is an object with optional callback functions. This lists all the element types that can be passed into Pressure, anything from a string selector, to a DOM node object.

161 |
162 |
163 |
// target all links
164 | Pressure.set('a', {});
165 | 
166 |
167 |
168 | 169 |
170 |
171 |
// pass in jQuery elements
172 | var elements = $('.dogs');
173 | Pressure.set(elements, {});
174 | 
175 |
176 |
177 | 178 |
179 |
180 |
// pass in element list or single element
181 | var elements2 = document.querySelectorAll('.cats');
182 | Pressure.set(elements2, {});
183 | var elements3 = document.getElementById('cat');
184 | Pressure.set(elements3, {});
185 | 
186 |
187 |
188 | 189 |
190 |
191 |
// element with the 'stuart' ID being selected and calling the 'change' callback
192 | Pressure.set('#stuart', {
193 |   change: function(force, event){
194 |     // the force value is passed back as well as the full event
195 |     this.innerHTML = force;
196 |   }
197 | });
198 | 
199 |
200 |
201 | 202 | 203 | 204 |

This example uses all of the optional methods available to you in the callback object.

205 |
206 |
207 |
Pressure.set('#element', {
208 |   start: function(event){
209 |     // this is called on force start
210 |   },
211 |   end: function(){
212 |     // this is called on force end
213 |   },
214 |   startDeepPress: function(event){
215 |     // this is called on "force click" / "deep press", aka once the force is greater than 0.5
216 |   },
217 |   endDeepPress: function(){
218 |     // this is called when the "force click" / "deep press" end
219 |   },
220 |   change: function(force, event){
221 |     // this is called every time there is a change in pressure
222 |     // 'force' is a value ranging from 0 to 1
223 |   },
224 |   unsupported: function(){
225 |     // NOTE: this is only called if the polyfill option is disabled!
226 |     // this is called once there is a touch on the element and the device or browser does not support Force or 3D touch
227 |   }
228 | });
229 |
230 |
231 | 232 | 233 |
234 | 235 |
4. jQuery Usage
236 |

Use the jquery.pressure.min.js file if you are using jQuery in your project. Using the Pressure jQuery library makes it simple to attach pressure events to jQuery elements.

237 |
238 |
239 |
$('a').pressure({
240 |   start: function(event){
241 |     // this is called on force start
242 |   },
243 |   end: function(){
244 |     // this is called on force end
245 |   },
246 |   startDeepPress: function(event){
247 |     // this is called on "force click" / "deep press", aka once the force is greater than 0.5
248 |   },
249 |   endDeepPress: function(){
250 |     // this is called when the "force click" / "deep press" end
251 |   },
252 |   change: function(force, event){
253 |     // this is called every time there is a change in pressure
254 |     // 'force' is a value ranging from 0 to 1
255 |   },
256 |   unsupported: function(){
257 |     // NOTE: this is only called if the polyfill option is disabled!
258 |     // this is called once there is a touch on the element and the device or browser does not support Force or 3D touch
259 |   }
260 | });
261 | 
262 |
263 |
264 | 265 | 266 |
267 | 268 |
5. Options
269 |

With Pressure, the third paramater is an optional object of options that can be passed in.

270 |

To set any of the config globally use the Config Helper.

271 |

Polyfill Support

272 |

Using the "polyfill" keyword, you can disable polyfill support for the element. The polyfill is enabled by default and is useful if the device or browser does not support pressure, it will fall back to using time. For example instead of force from 0 to 1, it counts up from 0 to 1 over the course of one second, as long as you are holding the element. Try some of the examples on the main page on a devices that does not support pressure and see for yourself how it works.

273 |
274 |
275 |
Click on me on any device :)
276 |
277 |
278 |
Pressure.set('#polyfill-example', {
279 |   change: function(force, event){
280 |     this.innerHTML = force;
281 |   },
282 |   unsupported: function(){
283 |     alert("Oh no, this device does not support pressure.")
284 |   }
285 | }, {polyfill: false});
286 |
287 |
288 | 289 |

Polyfill Speed Up

290 |

If you are using the polyfill, you can use the "polyfillSpeedUp" speed to determine how fast the polyfill takes to go from 0 to 1. The value is an integer in milliseconds and the default is 1000 (1 second).

291 |
292 |
293 |
Click on me on any device :)
294 |
295 |
296 |
Pressure.set('#polyfill-speed-up', {
297 |   change: function(force, event){
298 |     this.innerHTML = force;
299 |   }
300 | }, {polyfillSpeedUp: 5000});
301 | // takes 5 seconds to go from a force value of 0 to 1
302 | // only on devices that do not support pressure
303 |
304 |
305 | 306 |

Polyfill Speed Down

307 |

If you are using the polyfill, you can use the "polyfillSpeedDown" speed to determine how fast the polyfill takes to go from 1 to 0 when the elemnt is released. The value is an integer in milliseconds and the default is 0 (aka off).

308 |
309 |
310 |
Click on me on any device :)
311 |
312 |
313 |
Pressure.set('#polyfill-speed-down', {
314 |   change: function(force, event){
315 |     this.innerHTML = force;
316 |   }
317 | }, {polyfillSpeedDown: 2000});
318 | // takes 2 seconds to go from a force value of 1 to 0
319 | // only on devices that do not support pressure
320 |
321 |
322 | 323 |

Device Detection

324 |

With Pressure, the third paramater is an optional object of options that can be passed in. The first option is device targeting. Using the "only" keyword, you can define if you want pressure to respond to ONLY touch, ONLY Mouse, or ONLY Pointer events.

325 |
326 |
327 |
Click Me On an iPhone 6s or iPhone 7
328 |
329 |
330 |
Pressure.set('#element-touch', {
331 |   change: function(force, event){
332 |     this.innerHTML = force + 'on an iphone';
333 |   }
334 | }, {only: 'touch'});
335 |
336 |
337 | 338 |
339 |
340 |
Click Me On a Mac with Force Touch
341 |
342 |
343 |
Pressure.set('#element-mouse', {
344 |   change: function(force, event){
345 |     this.innerHTML = force + 'on a Mac';
346 |   }
347 | }, {only: 'mouse'});
348 |
349 |
350 | 351 |
352 |
353 |
Click Me On a Wacom Table or device that supports Pointer Events
354 |
355 |
356 |
Pressure.set('#element-pointer', {
357 |   change: function(force, event){
358 |     this.innerHTML = force + 'on a Mac';
359 |   }
360 | }, {only: 'pointer'});
361 |
362 |
363 | 364 |

Prevent Select

365 |

Both Mac and iPhones have system wide features that they default to when force clicking on something (ex. defining a word on Mac or "peeking and popping" an image on iOS). Pressure prevents those actions from happening, however if you still want those actions to be possible on "Pressure" elements, you can pass in "preventSelect" as an option.

366 |
Pressure sets user-select: none; css property on all of the elements it is attached to. It does this to keep text from being selected while force touching. However, sometimes you may want this turned off. The "preventSelect" option also handles removing that."
367 |
368 |
369 |
Push the image hard on an iPhone6s or iPhone7 to "peek and pop" it
370 | 371 |
372 |
373 |
Pressure.set('#element-touch-prevent', {
374 |   start: function(event){
375 |     console.log('cool it started');
376 |   }
377 | }, {only: 'touch', preventSelect: false});
378 |
379 |
380 | 381 |
382 |
383 |
Force touch one of these words on a Force Touch trackpad to define it.
384 |
385 |
386 |
Pressure.set('#element-mouse-prevent', {
387 |   start: function(event){
388 |     console.log('cool it started');
389 |   }
390 | }, {only: 'mouse', preventSelect: false});
391 |
392 |
393 | 394 | 395 |
396 | 397 |
6. Helpers
398 |
Config:
399 |

You can use Pressure.config() to set default configurations for site wide setup. All of the configurations are the same as the options listed above.

400 |
401 | Heads Up: If you have a config set, you can always overide the config on individual Pressure elements by passing in any of the options listed above to a specific Pressure block. 402 |
403 |
When using the jQuery Pressure library, use $.pressureConfig() rather than Pressure.config()
404 |
405 |
406 |
// These are the default configs set by Pressure
407 | Pressure.config({
408 |   polyfill: true,
409 |   polyfillSpeedUp: 1000,
410 |   polyfillSpeedDown: 0,
411 |   preventSelect: true,
412 |   only: null
413 | });
414 |
415 |
416 | 417 |
Map:
418 |

You can use Pressure.map() to map a value from one range of values to another. It takes 5 params: Pressure.map(inputValue, inputValueMin, inputValueMax, mapToMin, mapToMax); Here is a good write up on how this works in the Processing framework: Map Function.

419 |
When using the jQuery Pressure library, use $.pressureMap() rather than Pressure.map()
420 |
421 |
422 |
Pressure.set('#element', {
423 |   change: function(force, event){
424 |     // this takes the force, given that the force can range from 0 to 1, and maps that force value on a 100 to 200 range
425 |     this.style.width = Pressure.map(force, 0, 1, 100, 200);
426 |   }
427 | });
428 |
429 |
430 | 431 |
432 | 433 | 434 | 435 |
Pressure is created and maintained by Stuart Yamartino | Twitter | GitHub
436 | 437 | 438 | 439 | 440 | 441 | 442 | 443 | 452 | 453 | 454 | 455 | 460 | 461 | 462 | 463 | -------------------------------------------------------------------------------- /docs/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stuyam/pressure/f0a35fb1f3dc5605fda9547af525954c539a18c8/docs/favicon.png -------------------------------------------------------------------------------- /docs/img/icons/devices.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 8 | 11 | 13 | 14 | 15 | 17 | 18 | 19 | 21 | 22 | 24 | 26 | 27 | 28 | 32 | 33 | 34 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | -------------------------------------------------------------------------------- /docs/img/icons/drag.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 12 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 28 | 31 | 32 | 33 | 36 | 38 | 40 | 42 | 45 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /docs/img/icons/flask.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 9 | 11 | 12 | 15 | 17 | 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 | 50 | 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /docs/img/icons/stopwatch.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 7 | 11 | 14 | 17 | 19 | 20 | 22 | 24 | 25 | 27 | 30 | 32 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | -------------------------------------------------------------------------------- /docs/img/pickle.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stuyam/pressure/f0a35fb1f3dc5605fda9547af525954c539a18c8/docs/img/pickle.jpg -------------------------------------------------------------------------------- /docs/img/pressure-heart.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stuyam/pressure/f0a35fb1f3dc5605fda9547af525954c539a18c8/docs/img/pressure-heart.gif -------------------------------------------------------------------------------- /docs/img/pressure.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stuyam/pressure/f0a35fb1f3dc5605fda9547af525954c539a18c8/docs/img/pressure.gif -------------------------------------------------------------------------------- /docs/img/sierra.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stuyam/pressure/f0a35fb1f3dc5605fda9547af525954c539a18c8/docs/img/sierra.jpg -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | Pressure.js 15 | 16 | 17 | 18 | Hey 👋. Ever wish you could write Bootstrap markup in HTML emails? Well now you can with Bootstrap Email, check it out 💌 19 | 44 |
45 |
46 |

Pressure.js

47 |
current v2.1.1 | 2.5kB gzip
48 | 52 |

Pressure is a JavaScript library for handling Force Touch, 3D Touch, and Pointer Pressure on the web, bundled under one library with a simple API that makes working with them painless.

53 | 54 | 55 |
56 |
57 | 58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 | 74 |
75 | 76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 | 90 |
Click the Circles Above to Test Pressure
91 |
Your device does not support force touch or 3d touch so Pressure.js has fallen back to the polyfill.
92 |
93 |
94 |
95 | 96 | 97 |

Features

98 |
99 |
100 |
101 | 102 |

All Devices

103 |

Devices implement force differently from one another. Pressure has a simple to use API that works across all devices.

104 |
105 |
106 |
107 |
108 | 109 |

Support Testing

110 |

It can be hard to know if a user is using a force sensitive device. With Pressure, you can determine supported and upsupported devices.

111 |
112 |
113 |
114 |
115 | 116 |

Multitouch Support

117 |

Pressure will handle multiple fingers on the screen pushing with different amounts of force on different elements simultaneously.

118 |
119 |
120 |
121 |
122 | 123 |

Polyfill

124 |

Don't have a force sensitive device? Not a problem, use the polyfill to use time as a replacement for force.

125 |
126 |
127 |
128 | 129 |
130 | 131 |

Device Support

132 |
Future devices and browsers that support pressure will be added when they are released.
133 |
134 |
Microsoft Surface, Wacom Tablets (Device that supports pointer events)
135 |
Chrome, Opera, IE, Edge (Can I use Pointer Events)
136 |
137 | 138 |
139 |
iPhone 6s, iPhone 6s Plus, iPhone 7, iPhone 7 Plus
140 |
Safari, Chrome, anything using WKWebView
141 |
142 | 143 |
144 |
MacBook 2015 & MacBook Pro 2015
145 | 146 |
147 | 148 |
149 |
Magic Trackpad 2
150 | 151 |
152 | 153 |
154 |
iPad Pro with Apple Pencil
155 |
Safari, Chrome, anything using WKWebView
156 |
157 | 158 |
159 | 160 |

Examples

161 |
162 | Note: This page is using the "polyfill" option on most of the Pressure elements to give support for browsers that do not support Force or 3D touch. The "polyfill" option is enabled by default but can be useful in lots of situations where time is an ok replacement for force. For Example: The Instagram app uses 3D touch to allow users to quick look images, however if you do not have 3D touch support, you can simply hold down on the image for a second and it will have the same effect. 163 |
164 |
1. Pressure Change
165 |

The best part about Pressure is that is uses a single API that works on both Force Touch and 3D Touch. (note: Apple currently only has support for desktop and mobile safari, but luckily with pressure you can test for that)

166 |
167 |
168 |

Targets All

169 |
0
170 |

Targets ONLY Force Touch (mouse)

171 |
0
172 |

Targets ONLY 3D Touch (touch)

173 |
0
174 |

Targets ONLY Pointer Events (pointer)

175 |
0
176 |
177 |
178 | 179 |
2. Unblur Photo
180 |

The harder you press on the image the less blurry it will become.

181 |
182 |
183 |
184 | 185 |
186 |
187 |
188 | 189 |
3. Text Size
190 |

The harder you press on the text, the bigger it gets. Then when you let go it sticks at that size till the next time you press. This could be useful for accessibility on online articles or blogs

191 |
192 |
193 |
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. 194 |
195 |
196 |
197 | 198 |
4. Rotating Square
199 |

The harder you press on the button, the farther the image will spin. Try pressing with variable amounts of pressure. 200 |

201 |
202 | 203 | 204 |
205 |
206 | 207 |
5. "Deep" or Force Press
208 |

Press hard on the button, a "Deep" or Force press will occur and launch a bootstrap popover. 209 |

210 |
211 | 212 |
213 |
214 |
215 |
If you are not using one of the devices / browsers listed above, here is a gif to show it in action.
216 | 217 |
218 | 219 | 220 |
Pressure is created and maintained by Stuart Yamartino | Twitter | GitHub
221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 236 | 237 | 238 | 239 | -------------------------------------------------------------------------------- /docs/js/app.js: -------------------------------------------------------------------------------- 1 | $(function () { 2 | $('[data-toggle="popover"]').popover({trigger: 'manual'}); 3 | }); 4 | 5 | Pressure.set('.device-circle', { 6 | change: function(force){ 7 | console.log(force); 8 | this.style.width = Pressure.map(force, 0, 1, 10, $(this).data('size')) + 'em'; 9 | this.style.height = Pressure.map(force, 0, 1, 10, $(this).data('size')) + 'em'; 10 | this.style.marginTop = '-' + Pressure.map(force, 0, 1, 10, $(this).data('size'))/2 + 'em'; 11 | this.style.marginLeft = '-' + Pressure.map(force, 0, 1, 10, $(this).data('size'))/2 + 'em'; 12 | }, 13 | startDeepPress: function(){ 14 | this.style.backgroundColor = '#5bc0de'; 15 | }, 16 | endDeepPress: function(){ 17 | this.style.backgroundColor = '#d9534f'; 18 | }, 19 | end: function(){ 20 | this.style.width = '10em'; 21 | this.style.height = '10em'; 22 | this.style.marginTop = '-5em'; 23 | this.style.marginLeft = '-5em'; 24 | } 25 | }, {polyfillSpeedDown: 250}); 26 | 27 | Pressure.set('.device-circle', { 28 | change: function(){ 29 | $('.pressure-failed').hide(); 30 | }, 31 | unsupported: function(){ 32 | $('.pressure-failed').show(); 33 | } 34 | }, {polyfill: false}); 35 | 36 | var block = { 37 | 38 | change: function(force, event){ 39 | this.style.width = ((200 * force) + 200) + 'px'; 40 | this.innerHTML = force; 41 | this.style.backgroundColor = "rgb(" + parseInt(Pressure.map(force, 0, 1, 255, 0)) + ",100," + parseInt(Pressure.map(force, 0, 1, 0, 255)) +")"; 42 | this.style.color = force > 0.3 ? 'white' : 'black'; 43 | }, 44 | 45 | end: function(){ 46 | this.style.width = '200px'; 47 | this.innerHTML = 0; 48 | this.style.backgroundColor = 'rgb(255,100,0)'; 49 | this.style.color = 'black'; 50 | }, 51 | 52 | unsupported: function(){ 53 | this.innerHTML = 'Sorry! Check the devices and browsers that Pressure works on above ^^^^'; 54 | } 55 | } 56 | 57 | Pressure.set('#el1', block); 58 | Pressure.set('#el2', block, {only: 'mouse'}); 59 | Pressure.set('#el3', block, {only: 'touch'}); 60 | Pressure.set('#el4', block, {only: 'pointer'}); 61 | 62 | Pressure.set('#pressure-test', { 63 | start: function(){ 64 | this.innerHTML = 'Pressure is Supported!'; 65 | }, 66 | unsupported: function(){ 67 | this.innerHTML = 'Pressure is NOT Supported!'; 68 | } 69 | }); 70 | 71 | 72 | Pressure.set('#peanuts', { 73 | change: function(force, event){ 74 | this.style.webkitFilter = 'blur(' + Pressure.map(force, 0, 0.7, 20, 0) + 'px)'; 75 | }, 76 | 77 | end: function(){ 78 | this.style.webkitFilter = 'blur(20px)'; 79 | }, 80 | 81 | unsupported: function(){ 82 | this.innerHTML = 'Your device / browser does not support this :('; 83 | } 84 | }); 85 | 86 | var saveForce = 0; 87 | Pressure.set('#text-sizer', { 88 | change: function(force){ 89 | if(force > saveForce){ 90 | this.style.fontSize = Pressure.map(force, 0, 1, 16, 30); 91 | saveForce = force; 92 | } 93 | }, 94 | 95 | end: function(){ 96 | saveForce = 0; 97 | } 98 | }); 99 | 100 | Pressure.set('#cube-btn', { 101 | change: function(force){ 102 | document.getElementById('spinning-cube').style.webkitTransform = 'rotateZ(' + Pressure.map(force, 0, 1, 0, 360) + 'deg)'; 103 | }, 104 | end: function(){ 105 | document.getElementById('spinning-cube').style.webkitTransform = 'rotateZ(0deg)'; 106 | } 107 | }); 108 | 109 | Pressure.set('#popover', { 110 | startDeepPress: function(force){ 111 | $(this).popover('show'); 112 | }, 113 | endDeepPress: function(){ 114 | $(this).popover('hide'); 115 | } 116 | }); 117 | 118 | // docs 119 | Pressure.set('#output-element', { 120 | change: function(force, event){ 121 | this.innerHTML = force; 122 | } 123 | }, {polyfill: false}); 124 | 125 | Pressure.set('#element-touch', { 126 | change: function(force, event){ 127 | this.innerHTML = force + 'on an iphone'; 128 | } 129 | }, {only: 'touch', polyfill: false}); 130 | 131 | Pressure.set('#element-mouse', { 132 | change: function(force, event){ 133 | this.innerHTML = force + 'on a Mac'; 134 | } 135 | }, {only: 'mouse', polyfill: false}); 136 | 137 | Pressure.set('#element-pointer', { 138 | change: function(force, event){ 139 | this.innerHTML = force + 'on a pointer device'; 140 | } 141 | }, {only: 'pointer', polyfill: false}); 142 | 143 | Pressure.set('#element-touch-prevent', {}, {only: 'touch', preventDefault: false}); 144 | 145 | Pressure.set('#element-mouse-prevent', {}, {only: 'mouse', preventDefault: false}); 146 | 147 | Pressure.set('#polyfill-example', { 148 | change: function(force, event){ 149 | this.innerHTML = force; 150 | }, 151 | end: function(){ 152 | this.innerHTML = 0; 153 | }, 154 | unsupported: function(){ 155 | alert("Oh no, this device does not support pressure.") 156 | } 157 | }); 158 | 159 | Pressure.set('#polyfill-speed-up', { 160 | change: function(force, event){ 161 | this.innerHTML = force; 162 | }, 163 | end: function(){ 164 | this.innerHTML = 0; 165 | } 166 | }, {polyfillSpeedUp: 5000}); 167 | 168 | Pressure.set('#polyfill-speed-down', { 169 | change: function(force, event){ 170 | this.innerHTML = force; 171 | }, 172 | end: function(){ 173 | this.innerHTML = 0; 174 | } 175 | }, {polyfillSpeedDown: 2000}); 176 | 177 | // Twitter BTN 178 | window.twttr = (function(d, s, id) { 179 | var js, fjs = d.getElementsByTagName(s)[0], 180 | t = window.twttr || {}; 181 | if (d.getElementById(id)) return t; 182 | js = d.createElement(s); 183 | js.id = id; 184 | js.src = "https://platform.twitter.com/widgets.js"; 185 | fjs.parentNode.insertBefore(js, fjs); 186 | 187 | t._e = []; 188 | t.ready = function(f) { 189 | t._e.push(f); 190 | }; 191 | 192 | return t; 193 | }(document, "script", "twitter-wjs")); 194 | 195 | // Google Analytics 196 | (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ 197 | (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), 198 | m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) 199 | })(window,document,'script','//www.google-analytics.com/analytics.js','ga'); 200 | 201 | ga('create', 'UA-72492481-1', 'auto'); 202 | ga('send', 'pageview'); 203 | -------------------------------------------------------------------------------- /docs/js/cssdevices.js: -------------------------------------------------------------------------------- 1 | /*! CSSDevices v2.2.0 | MIT license | Maintained by Stuart Yamartino | http://cssdevices.io */ 2 | $(function(){ 3 | $('.cd-screen').each(function(){ 4 | if($(this).hasClass('cd-smart-loader')){ 5 | if(isSlideShow(this)){ 6 | $('> :gt(0)', this).hide(); 7 | } 8 | $('> :first-child',this).each(function(){ 9 | handleLoadBinding(this); 10 | }); 11 | } 12 | else{ 13 | callSlideShow(this); 14 | } 15 | }); 16 | 17 | function isSlideShow(_self){ 18 | return $(_self).children().length > 1 && ! $(_self).hasClass('cd-no-slideshow'); 19 | } 20 | 21 | function callSlideShow(_self){ 22 | if(isSlideShow(_self)){ 23 | var pauseSpeed = getOptionalData(_self, 'pause-speed', 5000); 24 | var transitionSpeed = getOptionalData(_self, 'transition-speed', 1000); 25 | $('> :gt(0)', _self).hide(); 26 | if ( ! $(_self).hasClass('cd-smart-loader') ) { 27 | $('> :eq(0)', _self).css('display', 'block'); 28 | } 29 | if( $(_self).hasClass('cd-transition-slider') ) { 30 | setInterval(function(){$('> :first-child',_self).animate({ 'margin-left': '-100%' }, transitionSpeed).next().css({'display':'block','margin-left': '100%'}).animate({ 'margin-left': '0%' }, transitionSpeed).end().appendTo(_self);}, pauseSpeed); 31 | } 32 | else{ 33 | setInterval(function(){$('> :first-child',_self).fadeOut(transitionSpeed).next().fadeIn(transitionSpeed).end().appendTo(_self);}, pauseSpeed); 34 | } 35 | } 36 | } 37 | 38 | function handleLoadBinding(_self){ 39 | $(_self).on('load', handleLoad); 40 | if (_self.complete) { 41 | $(_self).off('load', handleLoad); 42 | handleLoad.call(_self); 43 | } 44 | } 45 | 46 | function handleLoad(){ 47 | var loadSpeed = getOptionalData($(this).parent('.cd-smart-loader')[0], 'fade-in-speed', 250); 48 | $(this).fadeIn(loadSpeed); 49 | callSlideShow($(this).parent()[0]); 50 | } 51 | 52 | function getOptionalData(saveThis, data, defaultValue){ 53 | if ($(saveThis).attr('data-' + data)) { 54 | return $(saveThis).data(data); 55 | } 56 | return defaultValue; 57 | } 58 | 59 | $('.cd-device-loader').each(function(){ 60 | if( ! $(this).hasClass('cd-fill-parent')){ 61 | fadeDeviceIn(this); 62 | } 63 | }); 64 | 65 | //// Container Filler //// 66 | var firstGo = true; 67 | var scale; 68 | fillContainer(); 69 | $(window).resize(function(){ 70 | fillContainer(); 71 | }); 72 | 73 | function fillContainer(){ 74 | $('.cd-fill-parent').each(function(){ 75 | if( firstGo ){ 76 | $(this).data('initial-width', $(this).width()); 77 | } 78 | if( $(this).hasClass('cd-padded-device')){ 79 | var fontPercentage = ($(this).parent().width() - 40)/parseInt($(this).data('initial-width')) * 100; 80 | } 81 | else{ 82 | var fontPercentage = $(this).parent().width()/parseInt($(this).data('initial-width')) * 100; 83 | } 84 | $(this).css('font-size', fontPercentage + '%'); 85 | if( firstGo ){ 86 | fadeDeviceIn(this); 87 | } 88 | }); 89 | firstGo = false; 90 | } 91 | 92 | function fadeDeviceIn(_self){ 93 | $(_self).css('visibility', 'visible').hide().fadeIn(getOptionalData(_self, 'fade-in-speed', 250)); 94 | } 95 | 96 | }); 97 | -------------------------------------------------------------------------------- /docs/js/prism.js: -------------------------------------------------------------------------------- 1 | /* http://prismjs.com/download.html?themes=prism&languages=markup+css+clike+javascript */ 2 | var _self="undefined"!=typeof window?window:"undefined"!=typeof WorkerGlobalScope&&self instanceof WorkerGlobalScope?self:{},Prism=function(){var e=/\blang(?:uage)?-(?!\*)(\w+)\b/i,t=_self.Prism={util:{encode:function(e){return e instanceof n?new n(e.type,t.util.encode(e.content),e.alias):"Array"===t.util.type(e)?e.map(t.util.encode):e.replace(/&/g,"&").replace(/e.length)break e;if(!(d instanceof a)){u.lastIndex=0;var m=u.exec(d);if(m){c&&(f=m[1].length);var y=m.index-1+f,m=m[0].slice(f),v=m.length,k=y+v,b=d.slice(0,y+1),w=d.slice(k+1),P=[p,1];b&&P.push(b);var A=new a(i,g?t.tokenize(m,g):m,h);P.push(A),w&&P.push(w),Array.prototype.splice.apply(r,P)}}}}}return r},hooks:{all:{},add:function(e,n){var a=t.hooks.all;a[e]=a[e]||[],a[e].push(n)},run:function(e,n){var a=t.hooks.all[e];if(a&&a.length)for(var r,l=0;r=a[l++];)r(n)}}},n=t.Token=function(e,t,n){this.type=e,this.content=t,this.alias=n};if(n.stringify=function(e,a,r){if("string"==typeof e)return e;if("Array"===t.util.type(e))return e.map(function(t){return n.stringify(t,a,e)}).join("");var l={type:e.type,content:n.stringify(e.content,a,r),tag:"span",classes:["token",e.type],attributes:{},language:a,parent:r};if("comment"==l.type&&(l.attributes.spellcheck="true"),e.alias){var i="Array"===t.util.type(e.alias)?e.alias:[e.alias];Array.prototype.push.apply(l.classes,i)}t.hooks.run("wrap",l);var o="";for(var s in l.attributes)o+=(o?" ":"")+s+'="'+(l.attributes[s]||"")+'"';return"<"+l.tag+' class="'+l.classes.join(" ")+'" '+o+">"+l.content+""},!_self.document)return _self.addEventListener?(_self.addEventListener("message",function(e){var n=JSON.parse(e.data),a=n.language,r=n.code,l=n.immediateClose;_self.postMessage(t.highlight(r,t.languages[a],a)),l&&_self.close()},!1),_self.Prism):_self.Prism;var a=document.getElementsByTagName("script");return a=a[a.length-1],a&&(t.filename=a.src,document.addEventListener&&!a.hasAttribute("data-manual")&&document.addEventListener("DOMContentLoaded",t.highlightAll)),_self.Prism}();"undefined"!=typeof module&&module.exports&&(module.exports=Prism),"undefined"!=typeof global&&(global.Prism=Prism); 3 | Prism.languages.markup={comment://,prolog:/<\?[\w\W]+?\?>/,doctype://,cdata://i,tag:{pattern:/<\/?(?!\d)[^\s>\/=.$<]+(?:\s+[^\s>\/=]+(?:=(?:("|')(?:\\\1|\\?(?!\1)[\w\W])*\1|[^\s'">=]+))?)*\s*\/?>/i,inside:{tag:{pattern:/^<\/?[^\s>\/]+/i,inside:{punctuation:/^<\/?/,namespace:/^[^\s>\/:]+:/}},"attr-value":{pattern:/=(?:('|")[\w\W]*?(\1)|[^\s>]+)/i,inside:{punctuation:/[=>"']/}},punctuation:/\/?>/,"attr-name":{pattern:/[^\s>\/]+/,inside:{namespace:/^[^\s>\/:]+:/}}}},entity:/&#?[\da-z]{1,8};/i},Prism.hooks.add("wrap",function(a){"entity"===a.type&&(a.attributes.title=a.content.replace(/&/,"&"))}),Prism.languages.xml=Prism.languages.markup,Prism.languages.html=Prism.languages.markup,Prism.languages.mathml=Prism.languages.markup,Prism.languages.svg=Prism.languages.markup; 4 | Prism.languages.css={comment:/\/\*[\w\W]*?\*\//,atrule:{pattern:/@[\w-]+?.*?(;|(?=\s*\{))/i,inside:{rule:/@[\w-]+/}},url:/url\((?:(["'])(\\(?:\r\n|[\w\W])|(?!\1)[^\\\r\n])*\1|.*?)\)/i,selector:/[^\{\}\s][^\{\};]*?(?=\s*\{)/,string:/("|')(\\(?:\r\n|[\w\W])|(?!\1)[^\\\r\n])*\1/,property:/(\b|\B)[\w-]+(?=\s*:)/i,important:/\B!important\b/i,"function":/[-a-z0-9]+(?=\()/i,punctuation:/[(){};:]/},Prism.languages.css.atrule.inside.rest=Prism.util.clone(Prism.languages.css),Prism.languages.markup&&(Prism.languages.insertBefore("markup","tag",{style:{pattern:/()[\w\W]*?(?=<\/style>)/i,lookbehind:!0,inside:Prism.languages.css,alias:"language-css"}}),Prism.languages.insertBefore("inside","attr-value",{"style-attr":{pattern:/\s*style=("|').*?\1/i,inside:{"attr-name":{pattern:/^\s*style/i,inside:Prism.languages.markup.tag.inside},punctuation:/^\s*=\s*['"]|['"]\s*$/,"attr-value":{pattern:/.+/i,inside:Prism.languages.css}},alias:"language-css"}},Prism.languages.markup.tag)); 5 | Prism.languages.clike={comment:[{pattern:/(^|[^\\])\/\*[\w\W]*?\*\//,lookbehind:!0},{pattern:/(^|[^\\:])\/\/.*/,lookbehind:!0}],string:/(["'])(\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/,"class-name":{pattern:/((?:\b(?:class|interface|extends|implements|trait|instanceof|new)\s+)|(?:catch\s+\())[a-z0-9_\.\\]+/i,lookbehind:!0,inside:{punctuation:/(\.|\\)/}},keyword:/\b(if|else|while|do|for|return|in|instanceof|function|new|try|throw|catch|finally|null|break|continue)\b/,"boolean":/\b(true|false)\b/,"function":/[a-z0-9_]+(?=\()/i,number:/\b-?(?:0x[\da-f]+|\d*\.?\d+(?:e[+-]?\d+)?)\b/i,operator:/--?|\+\+?|!=?=?|<=?|>=?|==?=?|&&?|\|\|?|\?|\*|\/|~|\^|%/,punctuation:/[{}[\];(),.:]/}; 6 | Prism.languages.javascript=Prism.languages.extend("clike",{keyword:/\b(as|async|await|break|case|catch|class|const|continue|debugger|default|delete|do|else|enum|export|extends|finally|for|from|function|get|if|implements|import|in|instanceof|interface|let|new|null|of|package|private|protected|public|return|set|static|super|switch|this|throw|try|typeof|var|void|while|with|yield)\b/,number:/\b-?(0x[\dA-Fa-f]+|0b[01]+|0o[0-7]+|\d*\.?\d+([Ee][+-]?\d+)?|NaN|Infinity)\b/,"function":/[_$a-zA-Z\xA0-\uFFFF][_$a-zA-Z0-9\xA0-\uFFFF]*(?=\()/i}),Prism.languages.insertBefore("javascript","keyword",{regex:{pattern:/(^|[^\/])\/(?!\/)(\[.+?]|\\.|[^\/\\\r\n])+\/[gimyu]{0,5}(?=\s*($|[\r\n,.;})]))/,lookbehind:!0}}),Prism.languages.insertBefore("javascript","class-name",{"template-string":{pattern:/`(?:\\`|\\?[^`])*`/,inside:{interpolation:{pattern:/\$\{[^}]+\}/,inside:{"interpolation-punctuation":{pattern:/^\$\{|\}$/,alias:"punctuation"},rest:Prism.languages.javascript}},string:/[\s\S]+/}}}),Prism.languages.markup&&Prism.languages.insertBefore("markup","tag",{script:{pattern:/()[\w\W]*?(?=<\/script>)/i,lookbehind:!0,inside:Prism.languages.javascript,alias:"language-javascript"}}),Prism.languages.js=Prism.languages.javascript; 7 | -------------------------------------------------------------------------------- /docs/press.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | Pressure.js 15 | 16 | 17 | 18 | 43 |
44 |

Tweet at me @PressureJs with the project you are working on that uses pressure.js and I will add you here to the expo! :)

45 |
46 |

Examples

47 | 48 |

Henry Blyth Valentines

49 | 50 |
51 |
52 |

Pressure.js Scale

53 |

This is still a work in progress, the "zero" doesn't work, but it is a cool example of what can be done with 3D Touch on the new iPhone 6s

54 |
55 |

56 |

Press

57 | 92 |
93 | 94 |
Pressure is created and maintained by Stuart Yamartino | Twitter | GitHub
95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 110 | 111 | 112 | 113 | -------------------------------------------------------------------------------- /examples/example.js: -------------------------------------------------------------------------------- 1 | // Example of change method with a failure closure 2 | // This structure can be used in any methods of Pressure 3 | // The failure block will return with an "error" and message showing why the device doesn't support 3D Touch and Force Touch 4 | 5 | $.pressureConfig({ 6 | polyfill: false 7 | }); 8 | 9 | var block = { 10 | start: function(event) { 11 | console.log('start', event); 12 | }, 13 | 14 | change: function(force, event) { 15 | // event.preventDefault(); 16 | this.style.width = Pressure.map(force, 0, 1, 200, 300) + 'px'; 17 | this.innerHTML = force; 18 | console.log('change', force); 19 | }, 20 | 21 | startDeepPress: function(event) { 22 | console.log('start deep press', event); 23 | this.style.backgroundColor = '#FF0040'; 24 | }, 25 | 26 | endDeepPress: function() { 27 | console.log('end deep press'); 28 | this.style.backgroundColor = '#0080FF'; 29 | }, 30 | 31 | end: function() { 32 | console.log('end'); 33 | this.style.width = '200px'; 34 | this.innerHTML = 0; 35 | }, 36 | 37 | unsupported: function() { 38 | console.log(this); 39 | this.innerHTML = 'Your device / browser does not support this :('; 40 | } 41 | } 42 | 43 | Pressure.set(document.querySelectorAll('#el1'), block); 44 | Pressure.set(document.querySelectorAll('#el2'), block, {only: 'mouse'}); 45 | Pressure.set($('#el3'), block, {only: 'touch'}); 46 | Pressure.set('#el4', block, {only: 'pointer'}); 47 | 48 | $('#el1-jquery').pressure(block); 49 | $('#el2-jquery').pressure(block, {only: 'mouse'}); 50 | $('#el3-jquery').pressure(block, {only: 'touch'}); 51 | $('#el4-jquery').pressure(block, {only: 'pointer'}); 52 | 53 | $('img').pressure({ 54 | change: function(force, event){ 55 | console.log(force); 56 | } 57 | }); 58 | -------------------------------------------------------------------------------- /examples/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 23 | 24 | 25 | 26 | 27 |

Pressure Tests

28 |

Change Pressure

29 |
0
30 |

Change Pressure (target ONLY Force Touch | Mac, Magic Trackpad 2)

31 |
0
32 |

Change Pressure (target ONLY 3D Touch | iPhone6s, iPhone7, Apple Pencil)

33 |
0
34 |

Change Pressure (target ONLY Pointer Events)

35 |
0
36 |
37 |

jQuery Pressure Tests

38 |

Change Pressure

39 |
0
40 |

Change Pressure (target ONLY Force Touch | Mac, Magic Trackpad 2)

41 |
0
42 |

Change Pressure (target ONLY 3D Touch | iPhone6s, iPhone7, Apple Pencil)

43 |
0
44 |

Change Pressure (target ONLY Pointer Events)

45 |
0
46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | // include plug-ins 2 | var gulp = require('gulp'); 3 | var concat = require('gulp-concat'); 4 | var umd = require('gulp-umd'); 5 | var inject = require('gulp-inject-string') 6 | var rename = require('gulp-rename'); 7 | var uglify = require('gulp-uglify'); 8 | var babel = require('gulp-babel'); 9 | var HEADER_COMMENT = '// Pressure v2.2.0 | Created By Stuart Yamartino | MIT License | 2015 - 2020\n'; 10 | var DESTINATION = '.'; 11 | 12 | // JS concat, strip debugging and minify 13 | async function build() { 14 | gulp.src([ 15 | './src/pressure.js', 16 | './src/element.js', 17 | './src/adapters/adapter.js', 18 | './src/adapters/adapter_force_touch.js', 19 | './src/adapters/adapter_3d_touch.js', 20 | './src/adapters/adapter_pointer.js', 21 | './src/config.js', 22 | './src/helpers.js', 23 | ]) 24 | .pipe(concat('dist/pressure.js')) 25 | .pipe(babel()) 26 | .pipe(umd({ 27 | exports: function() { 28 | return 'Pressure'; 29 | } 30 | })) 31 | .pipe(inject.prepend(HEADER_COMMENT)) 32 | // This will output the non-minified version 33 | .pipe(gulp.dest(DESTINATION)) 34 | 35 | // This will minify and rename to pressure.min.js 36 | .pipe(uglify()) 37 | .pipe(inject.prepend(HEADER_COMMENT)) 38 | .pipe(rename({ extname: '.min.js' })) 39 | .pipe(gulp.dest(DESTINATION)); 40 | } 41 | 42 | async function buildJquery() { 43 | gulp.src([ 44 | './src/jquery_pressure.js', 45 | './src/element.js', 46 | './src/adapters/adapter.js', 47 | './src/adapters/adapter_force_touch.js', 48 | './src/adapters/adapter_3d_touch.js', 49 | './src/adapters/adapter_pointer.js', 50 | './src/config.js', 51 | './src/helpers.js', 52 | ]) 53 | .pipe(concat('dist/jquery.pressure.js')) 54 | .pipe(babel()) 55 | .pipe(umd({ 56 | dependencies: function() { 57 | return [ 58 | { 59 | name: 'jquery', 60 | amd: 'jquery', 61 | cjs: 'jquery', 62 | global: 'jQuery', 63 | param: '$' 64 | } 65 | ] 66 | }, 67 | /** 68 | * The UMD wrapper expects an exports() string for an expression the factory() should return, 69 | * and a namespace() string for a global value that should be set when no loader is present. 70 | * However, since jquery_pressure.js mutates $ several times instead of returning a value, 71 | * it does not need to use these features, so we set them to harmless no-ops. 72 | */ 73 | namespace: function() { 74 | // sets `window.jQuery__pressure` to undefined 75 | return 'jQuery__pressure'; 76 | }, 77 | exports: function() { 78 | // safely returns undefined from factory 79 | return 'void 0'; 80 | } 81 | })) 82 | .pipe(inject.prepend(HEADER_COMMENT)) 83 | // This will output the non-minified version 84 | .pipe(gulp.dest(DESTINATION)) 85 | 86 | // This will minify and rename to jquery.pressure.min.js 87 | .pipe(uglify()) 88 | .pipe(inject.prepend(HEADER_COMMENT)) 89 | .pipe(rename({ extname: '.min.js' })) 90 | .pipe(gulp.dest(DESTINATION)); 91 | } 92 | 93 | function watch() { 94 | gulp.watch(['src/*', 'src/adapters/*'], build); 95 | gulp.watch(['src/*', 'src/adapters/*'], buildJquery); 96 | } 97 | 98 | var dist = gulp.series(build, buildJquery) 99 | 100 | exports.build = build; 101 | exports.buildJquery = buildJquery; 102 | exports.watch = watch; 103 | exports.dist = dist; 104 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "pressure", 3 | "version": "2.2.0", 4 | "description": "Pressure is a lightweight JavaScript library for Force Touch, 3D Touch, and Pointer Pressure through a single API.", 5 | "main": "dist/pressure.min.js", 6 | "dependencies": {}, 7 | "devDependencies": { 8 | "@babel/core": "^7.11.6", 9 | "@babel/preset-env": "^7.11.5", 10 | "gulp": "^4.0.2", 11 | "gulp-babel": "^8.0.0", 12 | "gulp-concat": "^2.6.1", 13 | "gulp-inject-string": "^1.1.2", 14 | "gulp-rename": "^2.0.0", 15 | "gulp-uglify": "^3.0.2", 16 | "gulp-umd": "^2.0.0" 17 | }, 18 | "scripts": { 19 | "dist": "gulp dist", 20 | "watch": "gulp watch", 21 | "prepublish": "npm run dist" 22 | }, 23 | "repository": { 24 | "type": "git", 25 | "url": "git+https://github.com/stuyam/pressure.git" 26 | }, 27 | "keywords": [ 28 | "forcetouch", 29 | "force-touch", 30 | "3dtouch", 31 | "3d-touch", 32 | "pressure", 33 | "pressurejs", 34 | "iphone6s", 35 | "iphone6splus", 36 | "iphone7", 37 | "iphone7plus", 38 | "magic-trackpad", 39 | "apple-pencil" 40 | ], 41 | "author": "Stuart Yamartino", 42 | "license": "MIT", 43 | "bugs": { 44 | "url": "https://github.com/stuyam/pressure/issues" 45 | }, 46 | "homepage": "https://github.com/stuyam/pressure#readme" 47 | } 48 | -------------------------------------------------------------------------------- /src/adapters/adapter.js: -------------------------------------------------------------------------------- 1 | /* 2 | This is the base adapter from which all the other adapters extend. 3 | */ 4 | class Adapter { 5 | constructor(el, block, options) { 6 | this.el = el; 7 | this.block = block; 8 | this.options = options; 9 | this.pressed = false; 10 | this.deepPressed = false; 11 | this.nativeSupport = false; 12 | this.runningPolyfill = false; 13 | this.runKey = Math.random(); 14 | } 15 | 16 | setPressed(boolean) { 17 | this.pressed = boolean; 18 | } 19 | 20 | setDeepPressed(boolean) { 21 | this.deepPressed = boolean; 22 | } 23 | 24 | isPressed() { 25 | return this.pressed; 26 | } 27 | 28 | isDeepPressed() { 29 | return this.deepPressed; 30 | } 31 | 32 | add(event, set) { 33 | this.el.addEventListener(event, set, false); 34 | } 35 | 36 | runClosure(method) { 37 | if(method in this.block) { 38 | // call the closure method and apply nth arguments if they exist 39 | this.block[method].apply(this.el, Array.prototype.slice.call(arguments, 1)); 40 | } 41 | } 42 | 43 | fail(event, runKey) { 44 | if(Config.get('polyfill', this.options)) { 45 | if(this.runKey === runKey) { 46 | this.runPolyfill(event); 47 | } 48 | } else { 49 | this.runClosure('unsupported', event); 50 | } 51 | } 52 | 53 | bindUnsupportedEvent() { 54 | this.add(supportsTouch ? 'touchstart' : 'mousedown', (event) => this.runClosure('unsupported', event)); 55 | } 56 | 57 | _startPress(event) { 58 | if(this.isPressed() === false) { 59 | this.runningPolyfill = false; 60 | this.setPressed(true); 61 | this.runClosure('start', event); 62 | } 63 | } 64 | 65 | _startDeepPress(event) { 66 | if(this.isPressed() && this.isDeepPressed() === false) { 67 | this.setDeepPressed(true); 68 | this.runClosure('startDeepPress', event); 69 | } 70 | } 71 | 72 | _changePress(force, event) { 73 | this.nativeSupport = true; 74 | this.runClosure('change', force, event); 75 | } 76 | 77 | _endDeepPress() { 78 | if(this.isPressed() && this.isDeepPressed()) { 79 | this.setDeepPressed(false); 80 | this.runClosure('endDeepPress'); 81 | } 82 | } 83 | 84 | _endPress() { 85 | if(this.runningPolyfill === false){ 86 | if(this.isPressed()){ 87 | this._endDeepPress(); 88 | this.setPressed(false); 89 | this.runClosure('end'); 90 | } 91 | this.runKey = Math.random(); 92 | this.nativeSupport = false; 93 | } else { 94 | this.setPressed(false); 95 | } 96 | } 97 | 98 | deepPress(force, event) { 99 | force >= 0.5 ? this._startDeepPress(event) : this._endDeepPress(); 100 | } 101 | 102 | runPolyfill(event) { 103 | this.increment = Config.get('polyfillSpeedUp', this.options) === 0 ? 1 : 10 / Config.get('polyfillSpeedUp', this.options); 104 | this.decrement = Config.get('polyfillSpeedDown', this.options) === 0 ? 1 : 10 / Config.get('polyfillSpeedDown', this.options); 105 | this.setPressed(true); 106 | this.runClosure('start', event); 107 | if(this.runningPolyfill === false) { 108 | this.loopPolyfillForce(0, event); 109 | } 110 | } 111 | 112 | loopPolyfillForce(force, event) { 113 | if(this.nativeSupport === false){ 114 | if(this.isPressed()) { 115 | this.runningPolyfill = true; 116 | force = force + this.increment > 1 ? 1 : force + this.increment; 117 | this.runClosure('change', force, event); 118 | this.deepPress(force, event); 119 | setTimeout(this.loopPolyfillForce.bind(this, force, event), 10); 120 | } else { 121 | force = force - this.decrement < 0 ? 0 : force - this.decrement; 122 | if(force < 0.5 && this.isDeepPressed()) { 123 | this.setDeepPressed(false); 124 | this.runClosure('endDeepPress'); 125 | } 126 | if(force === 0) { 127 | this.runningPolyfill = false; 128 | this.setPressed(true); 129 | this._endPress(); 130 | } else { 131 | this.runClosure('change', force, event); 132 | this.deepPress(force, event); 133 | setTimeout(this.loopPolyfillForce.bind(this, force, event), 10); 134 | } 135 | } 136 | } 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /src/adapters/adapter_3d_touch.js: -------------------------------------------------------------------------------- 1 | /* 2 | This adapter is more mobile devices that support 3D Touch. 3 | */ 4 | class Adapter3DTouch extends Adapter { 5 | constructor(el, block, options) { 6 | super(el, block, options); 7 | } 8 | 9 | bindEvents() { 10 | if(supportsTouchForceChange) { 11 | this.add('touchforcechange', this.start.bind(this)); 12 | this.add('touchstart', this.support.bind(this, 0)); 13 | this.add('touchend', this._endPress.bind(this)); 14 | } else { 15 | this.add('touchstart', this.startLegacy.bind(this)); 16 | this.add('touchend', this._endPress.bind(this)); 17 | } 18 | } 19 | 20 | start(event) { 21 | if(event.touches.length > 0) { 22 | this._startPress(event); 23 | this.touch = this.selectTouch(event); 24 | if (this.touch) { 25 | this._changePress(this.touch.force, event); 26 | } 27 | } 28 | } 29 | 30 | support(iter, event, runKey = this.runKey) { 31 | if(this.isPressed() === false) { 32 | if(iter <= 6) { 33 | iter++; 34 | setTimeout(this.support.bind(this, iter, event, runKey), 10); 35 | } else { 36 | this.fail(event, runKey); 37 | } 38 | } 39 | } 40 | 41 | startLegacy(event) { 42 | this.initialForce = event.touches[0].force; 43 | this.supportLegacy(0, event, this.runKey, this.initialForce); 44 | } 45 | 46 | // this checks up to 6 times on a touch to see if the touch can read a force value 47 | // if the force value has changed it means the device supports pressure 48 | // more info from this issue https://github.com/yamartino/pressure/issues/15 49 | supportLegacy(iter, event, runKey, force) { 50 | if(force !== this.initialForce) { 51 | this._startPress(event); 52 | this.loopForce(event); 53 | } else if(iter <= 6) { 54 | iter++; 55 | setTimeout(this.supportLegacy.bind(this, iter, event, runKey, force), 10); 56 | } else { 57 | this.fail(event, runKey); 58 | } 59 | } 60 | 61 | loopForce(event) { 62 | if(this.isPressed()) { 63 | this.touch = this.selectTouch(event); 64 | setTimeout(this.loopForce.bind(this, event), 10); 65 | this._changePress(this.touch.force, event); 66 | } 67 | } 68 | 69 | // link up the touch point to the correct element, this is to support multitouch 70 | selectTouch(event) { 71 | if(event.touches.length === 1) { 72 | return this.returnTouch(event.touches[0], event); 73 | } else { 74 | for(var i = 0; i < event.touches.length; i++) { 75 | // if the target press is on this element 76 | if(event.touches[i].target === this.el || this.el.contains(event.touches[i].target)) { 77 | return this.returnTouch(event.touches[i], event); 78 | } 79 | } 80 | } 81 | } 82 | 83 | // return the touch and run a start or end for deep press 84 | returnTouch(touch, event) { 85 | this.deepPress(touch.force, event); 86 | return touch; 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/adapters/adapter_force_touch.js: -------------------------------------------------------------------------------- 1 | /* 2 | This adapter is for Macs with Force Touch trackpads. 3 | */ 4 | class AdapterForceTouch extends Adapter { 5 | constructor(el, block, options) { 6 | super(el, block, options); 7 | } 8 | 9 | bindEvents() { 10 | this.add('webkitmouseforcewillbegin', this._startPress.bind(this)); 11 | this.add('mousedown', this.support.bind(this)); 12 | this.add('webkitmouseforcechanged', this.change.bind(this)); 13 | this.add('webkitmouseforcedown', this._startDeepPress.bind(this)); 14 | this.add('webkitmouseforceup', this._endDeepPress.bind(this)); 15 | this.add('mouseleave', this._endPress.bind(this)); 16 | this.add('mouseup', this._endPress.bind(this)); 17 | } 18 | 19 | support(event) { 20 | if(this.isPressed() === false) { 21 | this.fail(event, this.runKey); 22 | } 23 | } 24 | 25 | change(event) { 26 | if(this.isPressed() && event.webkitForce > 0) { 27 | this._changePress(this.normalizeForce(event.webkitForce), event); 28 | } 29 | } 30 | 31 | // make the force the standard 0 to 1 scale and not the 1 to 3 scale 32 | normalizeForce(force) { 33 | return this.reachOne(map(force, 1, 3, 0, 1)); 34 | } 35 | 36 | // if the force value is above 0.995 set the force to 1 37 | reachOne(force) { 38 | return force > 0.995 ? 1 : force; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/adapters/adapter_pointer.js: -------------------------------------------------------------------------------- 1 | /* 2 | This adapter is for devices that support pointer events. 3 | */ 4 | class AdapterPointer extends Adapter { 5 | constructor(el, block, options) { 6 | super(el, block, options); 7 | } 8 | 9 | bindEvents() { 10 | this.add('pointerdown', this.support.bind(this)); 11 | this.add('pointermove', this.change.bind(this)); 12 | this.add('pointerup', this._endPress.bind(this)); 13 | this.add('pointerleave', this._endPress.bind(this)); 14 | } 15 | 16 | support(event) { 17 | if(this.isPressed() === false) { 18 | if(event.pressure === 0 || event.pressure === 0.5 || event.pressure > 1){ 19 | this.fail(event, this.runKey); 20 | } else { 21 | this._startPress(event); 22 | this._changePress(event.pressure, event); 23 | } 24 | } 25 | } 26 | 27 | change(event) { 28 | if(this.isPressed() && event.pressure > 0 && event.pressure !== 0.5){ 29 | this._changePress(event.pressure, event); 30 | this.deepPress(event.pressure, event); 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/config.js: -------------------------------------------------------------------------------- 1 | // This class holds the states of the the Pressure config 2 | var Config = { 3 | // 'false' will make polyfill not run when pressure is not supported and the 'unsupported' method will be called 4 | polyfill: true, 5 | // milliseconds it takes to go from 0 to 1 for the polyfill 6 | polyfillSpeedUp: 1000, 7 | // milliseconds it takes to go from 1 to 0 for the polyfill 8 | polyfillSpeedDown: 0, 9 | // 'true' prevents the selecting of text and images via css properties 10 | preventSelect: true, 11 | // 'touch', 'mouse', or 'pointer' will make it run only on that type of device 12 | only: null, 13 | // this will get the correct config / option settings for the current pressure check 14 | get(option, options) { 15 | return options.hasOwnProperty(option) ? options[option] : this[option]; 16 | }, 17 | // this will set the global configs 18 | set(options) { 19 | for (var k in options) { 20 | if (options.hasOwnProperty(k) && this.hasOwnProperty(k) && k != 'get' && k != 'set') { 21 | this[k] = options[k]; 22 | } 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/element.js: -------------------------------------------------------------------------------- 1 | class Element { 2 | constructor(el, block, options) { 3 | this.routeEvents(el, block, options); 4 | this.preventSelect(el, options); 5 | } 6 | 7 | routeEvents(el, block, options) { 8 | var type = Config.get('only', options); 9 | // for devices that support Force Touch 10 | if(supportsMouse && (type === 'mouse' || type === null)) { 11 | this.adapter = new AdapterForceTouch(el, block, options).bindEvents(); 12 | } 13 | // for devices that support pointer events 14 | else if(supportsPointer && (type === 'pointer' || type === null)) { 15 | this.adapter = new AdapterPointer(el, block, options).bindEvents(); 16 | } 17 | // for devices that support 3D Touch 18 | else if(supportsTouch && (type === 'touch' || type === null)) { 19 | this.adapter = new Adapter3DTouch(el, block, options).bindEvents(); 20 | } 21 | // unsupported if it is requesting a type and your browser is of other type 22 | else { 23 | this.adapter = new Adapter(el, block).bindUnsupportedEvent(); 24 | } 25 | } 26 | 27 | // prevent the default action of text selection, "peak & pop", and force touch special feature 28 | preventSelect(el, options) { 29 | if(Config.get('preventSelect', options)){ 30 | el.style.webkitTouchCallout = "none"; 31 | el.style.webkitUserSelect = "none"; 32 | el.style.khtmlUserSelect = "none"; 33 | el.style.MozUserSelect = "none"; 34 | el.style.msUserSelect = "none"; 35 | el.style.userSelect = "none"; 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/helpers.js: -------------------------------------------------------------------------------- 1 | //------------------- Helpers -------------------// 2 | 3 | // accepts jQuery object, node list, string selector, then called a setup for each element 4 | var loopPressureElements = function(selector, closure, options = {}) { 5 | // if a string is passed in as an element 6 | if(typeof selector === 'string' || selector instanceof String) { 7 | var elements = document.querySelectorAll(selector); 8 | for (var i = 0; i < elements.length; i++) { 9 | new Element(elements[i], closure, options); 10 | } 11 | // if a single element object is passed in 12 | } else if(isElement(selector)) { 13 | new Element(selector, closure, options); 14 | // if a node list is passed in ex. jQuery $() object 15 | } else { 16 | for (var i = 0; i < selector.length; i++) { 17 | new Element(selector[i], closure, options); 18 | } 19 | } 20 | } 21 | 22 | //Returns true if it is a DOM element 23 | var isElement = function(o) { 24 | return ( 25 | typeof HTMLElement === "object" ? o instanceof HTMLElement : //DOM2 26 | o && typeof o === "object" && o !== null && o.nodeType === 1 && typeof o.nodeName==="string" 27 | ); 28 | } 29 | 30 | // the map method allows for interpolating a value from one range of values to another 31 | // example from the Arduino documentation: https://www.arduino.cc/en/Reference/Map 32 | var map = function(x, in_min, in_max, out_min, out_max) { 33 | return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min; 34 | } 35 | 36 | var supportsMouse = false; 37 | var supportsTouch = false; 38 | var supportsPointer = false; 39 | var supportsTouchForce = false; 40 | var supportsTouchForceChange = false; 41 | 42 | if (typeof window !== 'undefined') { 43 | // only attempt to assign these in a browser environment. 44 | // on the server, this is a no-op, like the rest of the library 45 | if (typeof Touch !== 'undefined') { 46 | // In Android, new Touch requires arguments. 47 | try { 48 | if (Touch.prototype.hasOwnProperty('force') || 'force' in new Touch()) { 49 | supportsTouchForce = true; 50 | } 51 | } catch (e) {} 52 | } 53 | supportsTouch = 'ontouchstart' in window.document && supportsTouchForce; 54 | supportsMouse = 'onmousemove' in window.document && 'onwebkitmouseforcechanged' in window.document && !supportsTouch; 55 | supportsPointer = 'onpointermove' in window.document; 56 | supportsTouchForceChange = 'ontouchforcechange' in window.document; 57 | } 58 | -------------------------------------------------------------------------------- /src/jquery_pressure.js: -------------------------------------------------------------------------------- 1 | //--------------------- Public jQuery API Section ---------------------// 2 | if($) { 3 | $.fn.pressure = function(closure, options) { 4 | loopPressureElements(this, closure, options); 5 | return this; 6 | }; 7 | 8 | $.pressureConfig = function(options) { 9 | Config.set(options); 10 | }; 11 | 12 | $.pressureMap = function(x, in_min, in_max, out_min, out_max) { 13 | return map.apply(null, arguments); 14 | }; 15 | 16 | } else { 17 | throw new Error( "Pressure jQuery requires jQuery to be loaded." ); 18 | } 19 | -------------------------------------------------------------------------------- /src/pressure.js: -------------------------------------------------------------------------------- 1 | //--------------------- Public API Section ---------------------// 2 | // this is the Pressure Object, this is the only object that is accessible to the end user 3 | // only the methods in this object can be called, making it the "public api" 4 | var Pressure = { 5 | // targets any device with Force or 3D Touch 6 | set(selector, closure, options) { 7 | loopPressureElements(selector, closure, options); 8 | }, 9 | 10 | // set configuration options for global config 11 | config(options) { 12 | Config.set(options); 13 | }, 14 | 15 | // the map method allows for interpolating a value from one range of values to another 16 | // example from the Arduino documentation: https://www.arduino.cc/en/Reference/Map 17 | map(x, in_min, in_max, out_min, out_max) { 18 | return map.apply(null, arguments); 19 | } 20 | } 21 | --------------------------------------------------------------------------------