Skip to content

Instantly share code, notes, and snippets.

@ucavus
Forked from leecrossley/shake.js
Last active November 18, 2018 14:18
Show Gist options
  • Star 5 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save ucavus/5418463 to your computer and use it in GitHub Desktop.
Save ucavus/5418463 to your computer and use it in GitHub Desktop.
Detect shake gestures in Cordova (Phonegap).
/*
The only required option is the "success" callback. Usage:
var shake = new Shake({
frequency: 300, //milliseconds between polls for accelerometer data.
waitBetweenShakes: 1000, //milliseconds to wait before watching for more shake events.
threshold: 12, //how hard the shake has to be to register.
success: function(magnitude, accelerationDelta, timestamp) {}, //callback when shake is detected. "this" will be the "shake" object.
failure: function() {}, //callback when watching/getting acceleration fails. "this" will be the "shake" object.
});
shake.startWatch();
shake.stopWatch();
*/
function Shake(options) {
var shake = this,
watchId = null,
defaultOptions = {
frequency: 300,
waitBetweenShakes: 1000,
threshold: 12,
success: undefined,
failure: undefined
},
previousAcceleration;
for (var p in defaultOptions)
if (!options.hasOwnProperty(p))
options[p] = defaultOptions[p];
// Start watching the accelerometer for a shake gesture
shake.startWatch = function () {
if (watchId)
return;
watchId = navigator.accelerometer.watchAcceleration(getAccelerationSnapshot, handleError, {
frequency: options.frequency
});
};
// Stop watching the accelerometer for a shake gesture
shake.stopWatch = function () {
if (!watchId)
return;
navigator.accelerometer.clearWatch(watchId);
watchId = null;
};
// Gets the current acceleration snapshot from the last accelerometer watch
function getAccelerationSnapshot() {
navigator.accelerometer.getCurrentAcceleration(assessCurrentAcceleration, handleError);
}
// Assess the current acceleration parameters to determine a shake
function assessCurrentAcceleration(acceleration) {
if (!previousAcceleration) {
previousAcceleration = acceleration;
return;
}
var accelerationDelta = {
x: acceleration.x - previousAcceleration.x,
y: acceleration.y - previousAcceleration.y,
z: acceleration.z - previousAcceleration.z
};
var magnitude = Math.sqrt(
Math.pow(accelerationDelta.x, 2) +
Math.pow(accelerationDelta.y, 2) +
Math.pow(accelerationDelta.z, 2)
);
if (magnitude >= options.threshold) {
// Shake detected
if (options.waitBetweenShakes > 0) {
shake.stopWatch();
previousAcceleration = undefined;
}
options.success.call(shake, magnitude, accelerationDelta, acceleration.timestamp);
if (options.waitBetweenShakes > 0)
setTimeout(
function() {
shake.startWatch();
},
options.waitBetweenShakes
);
}
else
previousAcceleration = acceleration;
}
// Handle errors here
function handleError() {
if (options.failure)
options.failure.call(shake);
}
};
@ucavus
Copy link
Author

ucavus commented Apr 23, 2013

I had a few issues with the original:

  • The maths was a bit weird. It did not actually measure the difference in acceleration. (Math.abs() only takes one argument.)
  • It was not very configurable per usage
  • Error handling did nothing.
  • The function called on the shake event received no information about the gesture.
  • The same function only fired once since it was cleared after a shake event occurred.
  • You could only do one thing upon a shake. (This version allows multiple instances, but you'll probably only want to have one watching for shakes at any given time.)

@dannythunder
Copy link

Do you mind providing a working HTML-file for this? I cant get it to work!

I´ve tried
shake.startWatch(myFunction());

wich fires upon start, but when I shake the phone, nothing happens!

My index.php:

<?php
    header("Cache-Control: no-cache, must-revalidate"); // HTTP/1.1
    header("Expires: Sat, 26 Jul 1997 05:00:00 GMT"); // Date in the past
?>
<!DOCTYPE HTML>
<html lang="sv-SE">
<head>
    <title>Test</title>
    <meta name="viewport" content="width=device-width, minimum-scale=1.0, maximum-scale=1.0"/>
    <link href="styles.css?<?php echo time(); ?>" rel="stylesheet" type="text/css">
    <script type="text/javascript" src="shake.js?<?php echo time(); ?>"></script>
    <script type="text/javascript" charset="utf-8">

         // Wait for Cordova to load
        //
        document.addEventListener("deviceready", onDeviceReady, false);

        // Cordova is ready
        //
        function onDeviceReady() {

            var element = document.getElementById('deviceProperties');

            element.innerHTML = 'Device Name: '     + device.name     + '<br />' + 
                                'Device Cordova: '  + device.cordova + '<br />' + 
                                'Device Platform: ' + device.platform + '<br />' + 
                                'Device UUID: '     + device.uuid     + '<br />' + 
                                'Device Model: '    + device.model     + '<br />' + 
                                'Device Version: '  + device.version  + '<br />';

            var shake = new Shake({
                frequency: 300,                                                //milliseconds between polls for accelerometer data.
                waitBetweenShakes: 1000,                                       //milliseconds to wait before watching for more shake events.
                threshold: 12,                                                 //how hard the shake has to be to register.
                success: function(magnitude, accelerationDelta, timestamp) {}, //callback when shake is detected. "this" will be the "shake" object.
                failure: function() {},                                        //callback when watching/getting acceleration fails. "this" will be the "shake" object.
            });

            shake.startWatch();
            shake.stopWatch(myFunction()    );

            function myFunction(){
                alert("test");
            }

        }
        </script>
</head>
<body>
    <div><p id="deviceProperties">Loading device properties...</p></div>

        <!--  javascrtipt for later use -->
    <script type="text/javascript" src="jquery-1.9.1.js?<?php echo time(); ?>"></script>
    <script type="text/javascript" src="iscroll.js?<?php echo time(); ?>"></script>
    <script type="text/javascript" src="scroll.js?<?php echo time(); ?>"></script>
    <script type="text/javascript" src="cordova-2.7.0.js?<?php echo time(); ?>"></script>
    <!--<script type="text/javascript" src="shake.js?<?php echo time(); ?>"></script>-->
</body>

</html>

@sonicwong
Copy link

dannythunder, because you called "shake.stopWatch" after you start it.

@rubencodes
Copy link

As a heads-up, neither this nor the original seem to be compatible with iOS 8. :'(
(at least I'm guessing that's the weakest link on my end)

@mackhowell
Copy link

hey @ucavus and @rubencodes can you confirm if/how this is now broken in os 8+?

@mackhowell
Copy link

nm.. works with ios8.

@dandv
Copy link

dandv commented Apr 17, 2015

All the fixes proposed by this plugin have been addressed by the original author, @leecrossley. See https://github.com/leecrossley/cordova-plugin-shake-detection

@ucavus
Copy link
Author

ucavus commented Apr 21, 2015

@dandv Not so. The main issue that prompted me to fork in the first place is still not right. Lee apparently does not know how to get a 3d vector's magnitude. In his code, a move of 6 in dimension x and -6 in dimension y will result in a nett movement of 0. His does not provide any way to use the error handling function without modifying the plugin. It will also fire shake events continuously which is not what I wanted (see my waitBetweenShakes option). It's nice that it's been Cordova-ised, but I wouldn't use it.

Edit: I was wrong about the nett movement bit, but it's still not getting the magnitude correctly. I'm just not a fan of that shake detection logic.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment