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

в прошлом году

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

В нашем прошлом занятии мы начали работу с нашим питомцем. Разобрали логику, определились с поставленными задачами, разобрали теории касательно системы координат, которая используется в браузере и приступили к созданию функциональности, благодаря которой мы можем управлять позицией нашего питомца в браузере.
Однако, как мы помним у нас, остался небольшой нюанс, связанный с тем, что при инициализации нашего «муравья» теоретически могут быть введены такие значения координат, при которых наш питомец «выпрыгнет» за допустимые пределы. Давайте продолжим и попробуем исправить эту оплошность.

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

Итак, перед нами стоит задача скорректировать координаты, введенные при создании нашего питомца, для того, чтобы не возникало такой ситуации:

Для этого нам в первую очередь необходимо установить величины, за пределы которых нам нельзя выступать. И для нас безусловно это будут границы нашего «Домика» вот эти:

Почему только эти? Почему не выделена верхняя и левая границы домика? Помните, в нашем предыдущем занятии, я приводил графический пример того как на наш элемент накладываются оси координат? Согласно тому примеру, все величины, которые идут выше домика по оси Y и левее по оси X имеют отрицательные значения. И если отталкиваться от этого, то мы можем быть уверены, что муравей не выйдет за пределы верхней или левой границы домика, если ни одна из его координат не будет меньше нуля, то есть не будет отрицательной. Другими словами, нам не требуется постоянно высчитывать верхнюю и левую границы, потому что их предел всегда будет 0, 0.
Другое дело границы, которые мы выделил красным. Дело в том, что эти границы не постоянны. Они меняются вместе с размером окна браузера или размером экрана. Их положение легко поменять если порастягивать туда-сюда окно:

Видите? Верхняя и левая границы остаются как бы на месте, относительно верхнего левого угла окна браузера, в то время как правая и нижняя границы постоянно меняют свое положение по оси X и Y. Именно из-за этого, нам требуется рассчитывать положение только двух границ, а не четырех.
Получить максимальные значения для нужных нам границ, на самом деле не так уж и сложно. Если мы обратим внимание на ширину нашего «Домика», то заметим, что она заканчивается как раз в крайней допустимой для нас точки X, а если мы обратим внимание на высоты нашего «Домика», то заметим, что это есть крайняя точка для оси Y.
Так как это величины, которые относятся к «Домику», то согласно нашей логике мы будем их добавлять в объект {Booth}. И для этого сначала нам требуется делать выборку html-элемента, отвечающего за наш домик. Идем в браузер и видим

что у нашего домика есть свой идентификатор. Делаем выборку, используя его.

И теперь, когда у нас есть выборка нужного нам элемента, нам не составит особого труда выяснить его ширину и высоту. Делается это так c помощью свойств выборки offsetHeight для высоты, и offsetWidth для ширины. Давайте для начала мы наберем их в браузере. Заодно проверим правильно ли мы сделали выборку нашего домика.

Отлично, как видим, высоту и ширину мы получаем. Однако значения, которые возвращают наши свойства формируются только на момент вызова offsetHeight и offsetWidth. Это значит, что, если мы сохраним их в переменную или как в нашем случае в свойства объекта {Booth}, а затем изменим размеры окна. То значения в наших переменны/свойствах не пересчитаются. Давайте проверим на примере высоты:

Как видим, это действительно так. В переменной h после изменения высоты окна содержится самое первое значение. То же поведение будет актуально и для ширины. Но нас такой момент не устраивает. Поэтому я предлагаю в нашем объекте {Booth} вместо свойств ширины и высоты, сохранить методы, при вызове которых нам каждый раз будет возвращаться актуальная высота и ширина:

var Booth = {
    selector: document.getElementById('booth'),
    getHeight: function() {
        return this.selector.offsetHeight;
    },
    getWidth: function() {
        return this.selector.offsetWidth;
    },
    informer: {
        selector: document.getElementById('info'),
        setText: function(text) {
            this.selector.innerText = text;
        }
    },
    timer1: {
        selector: document.getElementById('fatigue'),
        isOrange: false,
        isRed: false,
        interval: null,
        width: 100
    },
    timer2: {
        selector: document.getElementById('satiety'),
        isOrange: false,
        isRed: false,
        interval: null,
        width: 100
    },
    timer3: {
        selector: document.getElementById('mood'),
        isOrange: false,
        isRed: false,
        interval: null,
        width: 100
    },
    /* добавляем события к выбранным кнопкам */
    buttons: document.getElementsByClassName('monitor__btn'),
    addButtonsEvents: function() {
        var context = this; // равен объекту Booth

        for(var i = 0; i < this.buttons.length; i+=1) {
            this.buttons[i].addEventListener('click', function() {
                var action = this.getAttribute('data-state');
                if(action == 'fatigue') {
                    context.refreshProgressBar(context.timer1);
                }
                if(action == 'satiety') {
                    context.refreshProgressBar(context.timer2);
                }
                if(action == 'mood') {
                    context.refreshProgressBar(context.timer3);                     
                }   
            });
        }
    },
    /* Запускаем счетчики парметров */
    runProgressBar: function(timer, seconds) {
        var self = this; // равен объекту Booth
        var milliseconds = seconds*1000;
        timer.interval = setInterval(function() {
            if(timer.width != 0) {
                timer.width -= 1;
                timer.selector.style.width = timer.width + '%';

                /* смена цвета счетчика*/
                if(timer.isOrange == false && timer.width <= 50) {
                    timer.isOrange = true;
                    timer.selector.setAttribute('class', 'monitor-bar__progress attention');
                }

                if(timer.isRed == false && timer.width <= 25) {
                    timer.isRed = true;
                    timer.selector.setAttribute('class', 'monitor-bar__progress alarm');
                }

            } else {
                /* Останавливаем все счетчики */
                clearInterval(self.timer1.interval);
                clearInterval(self.timer2.interval);
                clearInterval(self.timer3.interval);
            }
        }, milliseconds);
    },
    /* пополняем счетчик */
    refreshProgressBar: function(timer) {
        timer.width = 100;
        timer.selector.setAttribute('class', 'monitor-bar__progress');
    },
    /* Запускаем наши счетчики */
    startTimers: function() {
        this.runProgressBar(this.timer1, 0.3);
        this.runProgressBar(this.timer2, 0.5);
        this.runProgressBar(this.timer3, 0.7);
    },
    /* Запускаем игру */
    startGame: function() {
        this.addButtonsEvents();
        this.startTimers();
    }
}

Мы создали два простых метода getWidth() для получения ширины и getHeight() для получения высоты выборки нашего «Домика» используя оператор return. И вот таким образом если мы попробуем еще раз проделать «фокус» с изменением высоты, то теперь мы получим следующее поведение:

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

И вот смотрите какая интересная ситуация. Мы выяснили что крайняя нижняя граница у нас является 175 пикселей. Но когда мы поменяли соответствующий стиль у нашего питомца, то он вывалился за границы домика. В чем же тут дело? А дело вот в чем. Если мы еще раз просмотрим нашего питомца в браузере, то увидим

что наш питомец - это по сути квадрат. У него есть своя собственная ширина и высота (красная рамка). И так же, как и у любого другого элемента позиция этого квадрата рассчитывается от верхнего левого угла (жёлтая точка). Когда мы задаем нашему питомцу координату, то она применяется к его углу. То есть сказав муравью сместится на 175 пикселей, он на самом деле смещает свой верхний левый угол (жёлтую точку) на эту величину и как, наверное, уже понятно, он абсолютно не учитывает, что сам по себе он занимает еще пространство равное его собственной высоте. Поэтому на самом деле наша крайняя точка по оси Y будет равна не Booth.getHeight(), а Booth.getHeight – высота нашего питомца. Соответственно крайняя точка по оси X будет равна Booth.getWidth() – ширина нашего питомца. Давайте проверим это в браузере, а затeм выполним в коде.

И вот та же самая высота в 175 пикселей. Вычитаем из нее собственную высоту нашего муравья – 100 пикселей, получаем 75. Применяем. И видим, что в принципе мы остаёмся в допустимых пределах.

Зная эти нюансы, давайте поправим наш метод setPosition() в функции-родителе (Pet).

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

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';
    }
}

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

var ant = new Pet(133755, 10283);

Что мы сделали? Для начала мы создали переменные petWidth и petHeight, куда положили ширину и высоту самого питомца. Затем, используя вышеупомянутую логику мы выяснили крайние точки для положения нашего питомца по оси X (крайне правую) и по оси Y (крайне нижнюю), и присвоили эти значения переменным rightEnd и bottomEnd. Ну а далее мы сделали проверку на координаты, введенные при инициализации нашего питомца. Сначала мы проверили их на отрицательные значения. И если вдруг они оказались меньше нуля, то присваиваем им значение 0 обратно. При этом мы используем прием множественного присвоения знакомый нам из прошлого курса. А затем мы сравниваем введенные координаты с крайними допустимыми точками, и в случае если координаты оказываются больше допустимых значений, мы так же переопределяем их, задавай крайние, но допустимые значения.
Далее, на строке 40 я задал нашему питомцу при инициализации очень большие значения координат. С такими значениями он должен вылететь не только за пределы нашего «Домика», но и за пределы наших экранов.
Давайте вызовем метод getPosition() в браузере и посмотрим, среагирует ли наша логика на такие огромные значения координат:

И вот мы видим, что в самом начале наш питомец получает огромные координаты для появления. Однако, как только мы вызываем наш метод getPosition() муравей во-первых остается в пределах своего «Домика», а во-вторых он меняется свои изначально огромные координаты на допустимые.

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

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

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

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

Урок 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. Инкапсуляция.

Авторы получают вознаграждение, когда пользователи голосуют за их посты.
Голосующие читатели также получают вознаграждение за свой голос.
Порядок сортировки:  Популярное
73
  ·  в прошлом году

Ваш пост поддержали следующие Инвесторы Сообщества "Добрый кит":
@volv, @gryph0n, @orezaku, @exan, @vika-teplo, @nims55
Поэтому я тоже проголосовал за него!
Узнать подробности о сообществе можно тут:
Разрешите представиться - Кит Добрый
Правила
Инструкция по внесению Инвестиционного взноса
Вы тоже можете стать Инвестором и поддержать проект!!!


Если Вы хотите отказаться от поддержки Доброго Кита, то ответьте на этот комментарий командой "!нехочу"
69
  ·  в прошлом году

@rassen Поздравляю! Вы добились некоторого прогресса на Голосе и были награждены следующими новыми бейджами:

Награда за количество голосов

Вы можете нажать на любой бейдж, чтобы увидеть свою страницу на Доске Почета.
Чтобы увидеть больше информации о Доске Почета, нажмите здесь

Если вы больше не хотите получать уведомления, ответьте на этот комментарий словом стоп

Голосуя за это уведомление, вы помогаете всем пользователям Голоса. Узнайте, как здесь.