Ошибка цикла удержания угловой кнопки js

Мне нужна кнопка, которую можно нажать один раз, чтобы выполнить одну команду. Но также должна быть возможность удерживать кнопку и выполнять команду несколько раз, удерживая кнопку. Я использую AngularJs (хотя я не думаю, что это связано с проблемой)

Что у меня было до сих пор:

<button type="button" 
        class="btn btn-default" 
        ng-click="ChangeSetPoint('Up')"
        ng-mousedown="startLoopingUp()"
        ng-mouseup="stopLoopingUp()"
        ng-mouseleave="stopLoopingUp()">
        +
</button>

и в контроллере:

$scope.ChangeSetPoint = function(direction){
            //Stuff to actually change the setpoint
        }

        var looping = false;
        var promis;
        $scope.startLoopingUp = function(){
            looping = true;
            promis = setTimeout(loop('Up'),1000);           
        }

        var loop = function(direction){                                         
            $scope.ChangeSetPoint(direction);
            if(looping){
                promis = setTimeout(loop(direction),300)
            }
        }

        $scope.stopLoopingUp = function(){
           looping = false;
           clearTimeout(promis);
        }

Это работало до того, как я использовал этот параметр «направление». Раньше я использовал arguments.callee в setTimeout, но когда я посмотрел, как передать аргумент с помощью этой функции, я заметил, что использование arguments.callee не рекомендуется (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/arguments/callee). С тех пор я получаю ошибки «Превышен максимальный размер стека вызовов».


person Stijn Van Antwerpen    schedule 12.04.2016    source источник
comment
stackoverflow.com/questions/25180332/   -  person CoderHawk    schedule 12.04.2016
comment
@CoderHawk, я видел этот вопрос и вдохновил меня на его первоначальное решение. Но использование директив — это другой подход.   -  person Stijn Van Antwerpen    schedule 12.04.2016


Ответы (2)


Некоторое время назад я использовал следующую функцию внутри директивы. Я создал его, имея в виду следующее:

Функция может содержать три отдельные функции обратного вызова. «Короткий» обратный вызов вызывается одним щелчком мыши. При удержании кнопки короткий обратный вызов вызывается повторно. Когда вы все еще удерживаете кнопку, «длинный» обратный вызов запускается повторно. В конце, когда пользователь перестанет нажимать, будет запущен третий, «последний» обратный вызов.

Возможно, это не точное решение вашей проблемы, но, возможно, вдохновит и немного поможет :) Удачи.

/**
  *
  * @param {Event} evt
  * @param {Function} shortCallback
  * @param {Function} longCallback
  * @param {Function} [finishCallback] optional
  */
var onBtnClick = function (evt, shortCallback, longCallback, finishCallback) {
    //prevent mobile browser from long tap behaviour (simulated right click)
    evt.preventDefault();
    //only react to left mouse button or a touch event
    if (evt.which === 1 || evt.type === "touchstart") {
        //save 'this' context and interval/timeout IDs
        var self = this,
            short = {
                timeout     : null,
                interval    : null,
                callback    : angular.isFunction(shortCallback) ? shortCallback : angular.noop
            },
            long = {
                timeout     : null,
                interval    : null,
                callback    : angular.isFunction(longCallback) ? longCallback : short.callback
            },
            listener = "mouseup mouseleave touchend touchcancel",
            //
            cancelShort = function () {
                $timeout.cancel(short.timeout);
                $interval.cancel(short.interval);
            },
            //
            cancelLong = function () {
                $timeout.cancel(long.timeout);
                $interval.cancel(long.interval);
            };

        //react to a single click
        short.callback();

        //when user leaves the button cancel timeout/interval, lose focus and unbind recently bound listeners
        self.one(listener, function (e) {
            e.preventDefault();
            cancelShort();
            cancelLong();

            if (angular.isFunction(finishCallback)) {
                finishCallback();
            }

            self.blur();
        });

        //on a long click call the callback function within an interval for faster value changing
        short.timeout = $timeout(function () {
            short.interval = $interval(short.callback, 50, 0, false);
        }, 300, false);

        //when pressed even longer, cancel previous callback and fire "long" one
        long.timeout = $timeout(function () {
            cancelShort();
            long.interval = $interval(long.callback, 50, 0, false);
        }, 1500, false);
    }
};

Эта функция была привязана к элементу со следующим:

/**
 *
 * @param {String} selector
 * @param {Function} clickCallback
 * @param {Function} fastCallback
 * @param {Function} [finishCallback] optional
 */
 var bindEvent = function (selector, clickCallback, fastCallback, finishCallback) {
     $element.on("mousedown touchstart", selector, function (evt) {
         onBtnClick.call($(this), evt, clickCallback, fastCallback, finishCallback);
     });
 };
person Fidel90    schedule 12.04.2016

Это был параметр, который причинил вред:

при изменении

setTimeout(loop, 1000) to setTimeout(loop('Up'), 1000)

Я не давал функцию в качестве параметра, а выполнял функцию и возвращал результат в качестве параметра.

Я должен был сделать:

promis = setTimeout(function(){ loop('Up') },1000); 
person Stijn Van Antwerpen    schedule 12.04.2016