256 lines
7.6 KiB
JavaScript
256 lines
7.6 KiB
JavaScript
'use strict';
|
|
|
|
angular.module('copayApp.directives')
|
|
.directive('slideToAccept', function($timeout, $window, $q) {
|
|
return {
|
|
restrict: 'E',
|
|
templateUrl: 'views/includes/slideToAccept.html',
|
|
transclude: true,
|
|
scope: {
|
|
sendStatus: '=slideSendStatus',
|
|
onConfirm: '&slideOnConfirm',
|
|
isDisabled: '=isDisabled'
|
|
},
|
|
link: function(scope, element, attrs) {
|
|
|
|
var KNOB_WIDTH = 71;
|
|
var MAX_SLIDE_START_PERCENTAGE = 50;
|
|
var FULLY_SLID_PERCENTAGE = 72;
|
|
var PERCENTAGE_BUMP = 5;
|
|
var JIGGLE_EASING = linear;
|
|
var JIGGLE_DURATION = 100;
|
|
var RECEDE_DURATION = 250;
|
|
var INITIAL_TAP_EASE_DURATION = 75;
|
|
|
|
var elm = element[0];
|
|
var isSliding = false;
|
|
var curSliderPct = getKnobWidthPercentage();
|
|
var curBitcoinPct = 0;
|
|
var curTextPct = 0;
|
|
var currentEaseStartTime;
|
|
var bezier = $window.BezierEasing(0.175, 0.885, 0.320, 1.275);
|
|
|
|
scope.isSlidFully = false;
|
|
scope.displaySendStatus = '';
|
|
|
|
scope.$watch('sendStatus', function() {
|
|
if (!scope.sendStatus) {
|
|
reset();
|
|
} else if (scope.sendStatus === 'success') {
|
|
scope.displaySendStatus = '';
|
|
$timeout(function() {
|
|
reset();
|
|
}, 500);
|
|
} else {
|
|
scope.displaySendStatus = scope.sendStatus;
|
|
}
|
|
});
|
|
|
|
function easePosition(fromPct, pct, duration, easeFx, animateFx) {
|
|
var deferred = $q.defer();
|
|
currentEaseStartTime = Date.now();
|
|
var startTime = currentEaseStartTime;
|
|
var initialPct = fromPct;
|
|
var distance = pct - fromPct;
|
|
|
|
function ease() {
|
|
if (startTime !== currentEaseStartTime) {
|
|
return;
|
|
}
|
|
$window.requestAnimationFrame(function() {
|
|
var now = Date.now();
|
|
var elapsed = now - startTime;
|
|
var normalizedElapsedTime = elapsed / duration;
|
|
var newVal = easeFx(normalizedElapsedTime);
|
|
var newPct = newVal * distance + initialPct;
|
|
animateFx(newPct);
|
|
scope.$digest();
|
|
if (elapsed < duration) {
|
|
ease();
|
|
} else {
|
|
deferred.resolve();
|
|
}
|
|
});
|
|
}
|
|
ease();
|
|
return deferred.promise;
|
|
}
|
|
|
|
function linear(t) {
|
|
return t;
|
|
}
|
|
|
|
function easeInOutBack(t) {
|
|
return bezier(t);
|
|
}
|
|
|
|
function reset() {
|
|
scope.isSlidFully = false;
|
|
isSliding = false;
|
|
setNewSliderStyle(getKnobWidthPercentage());
|
|
setNewBitcoinStyle(0);
|
|
setNewTextStyle(0);
|
|
}
|
|
|
|
function setNewSliderStyle(pct) {
|
|
var knobWidthPct = getKnobWidthPercentage();
|
|
var translatePct = pct - knobWidthPct;
|
|
if (isSliding) {
|
|
translatePct += 0.35 * pct;
|
|
}
|
|
scope.sliderStyle = getTransformStyle(translatePct);
|
|
curSliderPct = pct;
|
|
}
|
|
|
|
function setNewBitcoinStyle(pct) {
|
|
var translatePct = -2.25 * pct;
|
|
scope.bitcoinStyle = getTransformStyle(translatePct);
|
|
curBitcoinPct = pct;
|
|
}
|
|
|
|
function setNewTextStyle(pct) {
|
|
var translatePct = -0.1 * pct;
|
|
scope.textStyle = getTransformStyle(translatePct);
|
|
curTextPct = pct;
|
|
}
|
|
|
|
function getTransformStyle(translatePct) {
|
|
return {
|
|
'transform': 'translateX(' + translatePct + '%)',
|
|
'-webkit-transform': 'translateX(' + translatePct + '%)'
|
|
};
|
|
}
|
|
|
|
function getKnobWidthPercentage() {
|
|
var knobWidthPct = (KNOB_WIDTH / elm.clientWidth) * 100;
|
|
return knobWidthPct;
|
|
}
|
|
|
|
function setSliderPosition(pct) {
|
|
setNewSliderStyle(pct);
|
|
setNewBitcoinStyle(pct);
|
|
setNewTextStyle(pct);
|
|
}
|
|
|
|
function easeSliderPosition(pct) {
|
|
var duration = INITIAL_TAP_EASE_DURATION;
|
|
easePosition(curSliderPct, pct, duration, JIGGLE_EASING, function(pct) {
|
|
setNewSliderStyle(pct);
|
|
});
|
|
easePosition(curBitcoinPct, pct, duration, JIGGLE_EASING, function(pct) {
|
|
setNewBitcoinStyle(pct);
|
|
});
|
|
easePosition(curTextPct, pct, duration, JIGGLE_EASING, function(pct) {
|
|
setNewTextStyle(pct);
|
|
});
|
|
}
|
|
|
|
function jiggleSlider() {
|
|
var pct = getKnobWidthPercentage() + PERCENTAGE_BUMP;
|
|
var duration = JIGGLE_DURATION;
|
|
var p1 = easePosition(curSliderPct, pct, duration, JIGGLE_EASING, function(pct) {
|
|
setNewSliderStyle(pct);
|
|
});
|
|
var p2 = easePosition(curBitcoinPct, pct, duration, JIGGLE_EASING, function(pct) {
|
|
setNewBitcoinStyle(pct);
|
|
});
|
|
|
|
$q.all([p1, p2]).then(function() {
|
|
recede();
|
|
});
|
|
}
|
|
|
|
function recede() {
|
|
var duration = RECEDE_DURATION;
|
|
easePosition(curSliderPct, getKnobWidthPercentage(), duration, easeInOutBack, function(pct) {
|
|
setNewSliderStyle(pct);
|
|
});
|
|
easePosition(curBitcoinPct, 0, duration, easeInOutBack, function(pct) {
|
|
setNewBitcoinStyle(pct);
|
|
});
|
|
easePosition(curTextPct, 0, duration, easeInOutBack, function(pct) {
|
|
setNewTextStyle(pct);
|
|
});
|
|
}
|
|
|
|
function alertSlidFully() {
|
|
scope.isSlidFully = true;
|
|
scope.onConfirm();
|
|
}
|
|
|
|
function getTouchXPosition($event) {
|
|
var x;
|
|
if ($event.touches || $event.changedTouches) {
|
|
if ($event.touches.length) {
|
|
x = $event.touches[0].clientX;
|
|
} else {
|
|
x = $event.changedTouches[0].clientX;
|
|
}
|
|
} else {
|
|
x = $event.clientX;
|
|
}
|
|
return x;
|
|
}
|
|
|
|
function getSlidPercentage($event) {
|
|
var x = getTouchXPosition($event);
|
|
var width = elm.clientWidth;
|
|
var pct = (x / width) * 100;
|
|
if (x >= width) {
|
|
pct = 100;
|
|
}
|
|
return pct;
|
|
}
|
|
|
|
scope.onTouchstart = function($event) {
|
|
if (scope.isSlidFully) {
|
|
return;
|
|
}
|
|
if (!isSliding) {
|
|
var pct = getSlidPercentage($event);
|
|
if (pct > MAX_SLIDE_START_PERCENTAGE) {
|
|
jiggleSlider();
|
|
return;
|
|
} else {
|
|
isSliding = true;
|
|
var knobWidthPct = getKnobWidthPercentage();
|
|
if (pct < knobWidthPct) {
|
|
pct = knobWidthPct;
|
|
}
|
|
pct += PERCENTAGE_BUMP;
|
|
easeSliderPosition(pct);
|
|
}
|
|
}
|
|
};
|
|
|
|
scope.onTouchmove = function($event) {
|
|
if (!isSliding || scope.isSlidFully) {
|
|
return;
|
|
}
|
|
var pct = getSlidPercentage($event);
|
|
var knobWidthPct = getKnobWidthPercentage();
|
|
if (pct < knobWidthPct) {
|
|
pct = knobWidthPct;
|
|
}
|
|
pct += PERCENTAGE_BUMP;
|
|
currentEaseStartTime = null;
|
|
setSliderPosition(pct);
|
|
};
|
|
|
|
scope.onTouchend = function($event) {
|
|
if (scope.isSlidFully) {
|
|
return;
|
|
}
|
|
var pct = getSlidPercentage($event);
|
|
if (isSliding && pct > FULLY_SLID_PERCENTAGE) {
|
|
pct = 100;
|
|
setSliderPosition(pct);
|
|
alertSlidFully();
|
|
} else {
|
|
recede();
|
|
}
|
|
isSliding = false;
|
|
};
|
|
}
|
|
};
|
|
});
|