Уважаемые пользователи Голос!
Сайт доступен в режиме «чтение» до сентября 2020 года. Операции с токенами Golos, Cyber можно проводить, используя альтернативные клиенты или через эксплорер Cyberway. Подробности здесь: https://golos.io/@goloscore/operacii-s-tokenami-golos-cyber-1594822432061
С уважением, команда “Голос”
GOLOS
RU
EN
UA
rassen
7 лет назад

Стать программистом. Практика JavaScript для новичков. Занятие 12.

Доброго времени суток, друзья!

В нашем прошлом занятии мы начали работать на функциональностью для движения нашего питомца. Для этого были созданы два метода startWalk() и stopWalk(). В процессе работы с этими методами, мы еще раз попрактиковались с такими приемами как сохранение контекста и множественное присвоение. Кроме этого мы познакомились с таким встроенным методом, как parseInt() и на примере преобразования значения полученного из стиля мы из строки в цифру, получили наглядный пример его работы.

В конце нашего прошлого занятия мы столкнулись с двумя моментами, которые нам необходимо решить, прежде чем двигаться дальше.

Давайте начнем.

На текущий момент, из предыдущего занятия к нам перекочевали два момента:

  1. Это достаточно большая организация метода startWalk() с дублированием строчек, которые по сути выполняют однотипную работу.
  2. Отсутствие проверок границ «Домика» во время увеличения/уменьшения координат нашего питомца во время движения. Из-за чего наш питомец легко «убегает»
    Давайте начнем со второго пункта. Тем более мы уже делали аналогичную проверку границ в методе getPosition(). Давайте добавим такую же в наш метод startWalk().
var Pet = function(x, y) {
    this.selector = document.getElementById('pet');
    this.x = x || 0;
    this.y = y || 0;
    this.walkInterval = null;
}

Pet.prototype =  {
    /* Задаем начальную позицию питомца в домике */
    setPosition: function() {
        var x = this.x;
        var y = this.y;
        var petWidth = this.selector.offsetWidth;
        var petHeight = this.selector.offsetHeight;
        var bottomEnd = Booth.getHeight() - petHeight;
        var rightEnd = Booth.getWidth() - petWidth;
        /* проверрим на верхнюю и левую границы домика */
        if( x < 0 ) {
            this.x = x = 0;
        }
        if( y < 0 ) {
            this.y = y = 0;
        }
        /* проверрим на правую и нижнюю границы домика */
        if( x > rightEnd) {
            this.x = x = rightEnd;
        }
        if( y > bottomEnd) {
            this.y = y = bottomEnd;
        }

        this.selector.style.top = y + 'px';
        this.selector.style.left = x + 'px';
    },
    /* передвижение питомца */
    startWalk: function(direction) {
        var self = this;
        var timeForWalk = 50;
        var petWidth = this.selector.offsetWidth;
        var petHeight = this.selector.offsetHeight;

        if(direction == 'left') {
            this.walkInterval = setInterval(function() {
                var currentLeft = parseInt(self.selector.style.left) || 0;
                if(currentLeft != 0) {
                    self.selector.style.left = (currentLeft - 1) + 'px';
                }
            }, timeForWalk);
        }

        if(direction == 'right') {
            this.walkInterval = setInterval(function() {
                var currentLeft = parseInt(self.selector.style.left) || 0;
                if(currentLeft < Booth.getWidth() - petWidth) {
                    self.selector.style.left = (currentLeft + 1) + 'px';
                }
            }, timeForWalk);
        }

        if(direction == 'up') {
            this.walkInterval = setInterval(function() {
                var currentTop = parseInt(self.selector.style.top) || 0;
                if(currentTop != 0) {
                    self.selector.style.top = (currentTop - 1) + 'px';
                }
            }, timeForWalk);
        }

        if(direction == 'down') {
            this.walkInterval = setInterval(function() {
                var currentTop = parseInt(self.selector.style.top) || 0;
                if(currentTop < Booth.getHeight() - petHeight) {
                    self.selector.style.top = (currentTop + 1) + 'px';
                }
            }, timeForWalk);
        }

    },
    /* остановка питомца */
    stopWalk: function() {
        var self = this;
        clearInterval(self.walkInterval);
    }
}

/* Создаем муравья, используя функцию-родитель */
var ant = new Pet(0, 0);

Наш метод разросся. Мы снова высчитали ширину и высоту нашего питомца (оранжевая рамка) и затем добавили в каждый интервал проверку на крайне доступные координаты наших границ. Как мы уже знаем верхняя и левая границы «Домика» не должны быть меньше чем 0 (синие рамки). Поэтому мы ставим проверку, которая позволяет уменьшать наши величины на единицу только если они уже не равны нулю. Ну а правая и нижняя границы нашего «Домика» имеют соответственно проверку на ширину Домика минус Ширина питомца и Высота Домика минус Высота питомца (зеленая рамка). Всё это мы разбирали в методе setPosition(). Поэтому я рассчитываю на то, что логика понятна. Как видим, у нас опять имеет место быть дублирование кода, но пока задача касается только учета границ, поэтому нам остается только проверить работают ли эти проверки в браузере:

Как видим для движения вверх/вниз проверки работают. Питомец остается в нужных нам пределах. Для движения влево/вправо наши проверки так же сделают свое дело. Давайте считать, что задачу с границами мы решили и перейдем к оптимизации нашего метода. Постараемся организовать наш код таким образом, чтобы убрать дублирование кода.

На прошлом скриншоте мы уже сделали важную вещь – отметили рамками одинакового цвета операции, которые дублируют друг друга. Блоки синего цвета отличаются между собой только свойством, с которым идет работа. Остальное всё одинаково. Два оставшихся блока (зеленая рамка) отличаются и стилем, и параметрами (ширина/высота) в зависимости от направления. Давайте для начала попробуем объединить эти пары в функции, которые уберут дублирование кода.

Мы создали две функции leftOrUp() и rightOrBottom(), вынесли в них наши дублирования. Те части, которыми это дублирование так или иначе отличались, мы вынесли в параметры. И теперь на месте где раньше были дублирования, мы расположили вызовы этих функций. Уже неплохо, но это еще не всё. Обратите внимание, не важно какое направление используется в данный момент, мы на каждую проверку создаем интервал setInterval() и записываем его в свойство walkInterval. Чем отличаются все эти интервалы? Они отличаются только функцией, которую он вызывает. В нашем случае это уже функции leftOrUp() и rightOrBottom(). Давайте попробуем реорганизовать наш код таким образом, чтобы создавать только один раз.

var Pet = function(x, y) {
    this.selector = document.getElementById('pet');
    this.x = x || 0;
    this.y = y || 0;
    this.walkInterval = null;
}

Pet.prototype =  {
    /* Задаем начальную позицию питомца в домике */
    setPosition: function() {
        var x = this.x;
        var y = this.y;
        var petWidth = this.selector.offsetWidth;
        var petHeight = this.selector.offsetHeight;
        var bottomEnd = Booth.getHeight() - petHeight;
        var rightEnd = Booth.getWidth() - petWidth;
        /* проверрим на верхнюю и левую границы домика */
        if( x < 0 ) {
            this.x = x = 0;
        }
        if( y < 0 ) {
            this.y = y = 0;
        }
        /* проверрим на правую и нижнюю границы домика */
        if( x > rightEnd) {
            this.x = x = rightEnd;
        }
        if( y > bottomEnd) {
            this.y = y = bottomEnd;
        }

        this.selector.style.top = y + 'px';
        this.selector.style.left = x + 'px';
    },
    /* передвижение питомца */
    startWalk: function(direction) {
        var self = this;
        var timeForWalk = 50;
        var petWidth = this.selector.offsetWidth;
        var petHeight = this.selector.offsetHeight;
        var leftOrUp = function(styleName) {
            var current = parseInt(self.selector.style[styleName]) || 0;
                if(current != 0) {
                    self.selector.style[styleName] = (current - 1) + 'px';
                }
        };
        var rightOrBottom = function(styleName, border) {
                var current = parseInt(self.selector.style[styleName]) || 0;
                if(current < border) {
                    self.selector.style[styleName] = (current + 1) + 'px';
                }
        };
        var currentIntervalFunction = null;

        if(direction == 'left') {
            currentIntervalFunction = function() {
                leftOrUp('left');
            }
        }

        if(direction == 'right') {
            currentIntervalFunction = function() {
                var border = Booth.getWidth() - petWidth;
                rightOrBottom('left', border);
            }
        }

        if(direction == 'up') {
            currentIntervalFunction = function() {
                leftOrUp('top');
            }
        }

        if(direction == 'down') {
            currentIntervalFunction = function() {
                var border = Booth.getHeight() - petHeight;
                rightOrBottom('top', border);
            }
        }

        this.walkInterval = setInterval(currentIntervalFunction, timeForWalk);

    },
    /* остановка питомца */
    stopWalk: function() {
        var self = this;

        clearInterval(self.walkInterval);
    }
}

/* Создаем муравья, используя функцию-родитель */

var ant = new Pet(0, 0);

Обратите внимание на то, что мы сделали. Мы создали переменную currentIntervalFunction без явного задания значения (из предыдущего курса мы знаем, что в этом случае оно будет равно undefined), а затем в зависимости от того, какое направление было выбрано, мы присваиваем этой переменной ту функцию (синие стрелки и рамки), которая должна выполнятся каждый интервал времени. Это позволило нам организовать код таким образом, что вместо четырех строк кода с инициализацией интервала, мы написали её только один раз (оранжевая рамка).

Теперь можно сказать, что наш метод организован в более оптимальном ключе. Проверим на работоспособность:

Наш питомец двигается. Что нам и требовалось. В следующем занятии мы с Вами сделаем так, чтобы питомец двигался беспрерывно и в разных направлениях.

А на сегодня всё. Продолжение следует…

Ссылки на предыдущие занятия:

Практика JavaScript для новичков. Занятие 1,
Практика JavaScript для новичков. Занятие 2,
Практика JavaScript для новичков. Занятие 3,
Практика JavaScript для новичков. Занятие 4,
Практика JavaScript для новичков. Занятие 5,
Практика JavaScript для новичков. Занятие 6,
Практика JavaScript для новичков. Занятие 7,
Практика JavaScript для новичков. Занятие 8,
Практика JavaScript для новичков. Занятие 9,
Практика JavaScript для новичков. Занятие 10,
Практика JavaScript для новичков. Занятие 11

Ссылки на предыдущий курс:

Урок 1 - Окружение.,
Урок 2 - Некоторые особенности синтаксиса.,
Урок 3 - Переменные.,
Урок 4 - Типы переменных, как основа для их взаимодействия.,
Урок 5 - Операции с переменными одного типа.,
Урок 6 - Операции с переменными одного типа. Часть II.,
Урок 7 - Взаимодействие переменных с разными типами.,
Урок 8 - Взаимодействие переменных разного типа. часть II.,
Урок 9 - Взаимодействие переменных разного типа. Часть III.,
Урок 10 - Другие возможные взаимодействия между переменными.,
Урок 11 - Другие возможные взаимодействия между переменными. Часть II.,
Урок 12 - Другие возможные взаимодействия между переменными. Операторы присваивания.,
Урок 13 - Другие возможные взаимодействия между переменными. Операторы сравнения.,
Урок 14 - Сложные переменные. Array и Object.,
Урок 15 - Условные операторы.,
Урок 16 - Циклы.,
Урок 17 - Циклы. Часть II.,
Урок 18 - Функции.,
Урок 19 - Функции. Часть II.,
Урок 20 - Профилирование. Функции, часть III.,
Урок 21 - Функции, Часть IV. Аргументы.,
Урок 22 - Objects (Объекты).,
Урок 23 - Встроенные функции и объекты.,
Урок 24 - Встроенные функции и Объекты, Часть II. Глобальные функции и переменные.,
Урок 25 - Встроенные функции и Объекты, Часть III. Document Object Model.,
Урок 26 - Встроенные функции и Объекты, Часть III. Document Object Model.
Урок 27 - Встроенные объекты. Объект Style, Events, Часть II.
Урок 28 - Встроенная переменная this. Глобальная и локальная области видимости.
Урок 29 - Объектно-ориентированное Программирование. Введение.
Урок 30 - Объектно-ориентированное Программирование. Часть II. Полиморфизм.
Урок 31 - OОП. Наследование, Часть I. Оператор new.
Урок 32 - ООП. Наследование, Часть II. PROTOTYPE.
Урок 33 - ООП. Часть II. Полиморфизм.
Урок 34 - ООП. Часть III. Инкапсуляция.

1
65.125 GOLOS
На Golos с December 2016
Комментарии (2)
Сортировать по:
Сначала старые