ilyachalov (ilyachalov) wrote,
ilyachalov
ilyachalov

Category:

JavaScript: мяч на футбольном поле

Решил задачу «Поместите мяч в центр поля» к подразделу 1.9 «Размеры и прокрутка элементов» второй части учебника по JavaScript.

На HTML-странице средствами языков HTML и CSS нарисовано футбольное поле, в левом верхнем углу которого расположен футбольный мяч. Футбольное поле задано HTML-элементом div, а футбольный мяч задан HTML-элементом img (картинкой).

Код этой HTML-страницы можно посмотреть в песочнице.

Стили HTML-элементов, представляющих футбольное поле и мяч, расположены в заголовочной части HTML-страницы:
<style>
  #field {                     /* стиль футбольного поля */
    width: 200px;
    border: 10px groove black;
    background-color: #00ff00;
    position: relative;
  }

  #ball {                      /* стиль мяча */
    position: absolute;
  }
</style>

В теле HTML-страницы помещаем HTML-элементы, представляющие футбольное поле и мяч:
<div id="field">
  <img src="https://en.js.cx/clipart/ball.svg" id="ball">
  . . . . . . . . . . . . . . . . . . . . . . . . .
  . . . . . . . . . . . . . . . . . . . . . . . . .
  . . . . . . . . . . . . . . . . . . . . . . . . .
  . . . . . . . . . . . . . . . . . . . . . . . . .
  . . . . . . . . . . . . . . . . . . . . . . . . .
  . . . . . . . . . . . . . . . . . . . . . . . . .
  . . . . . . . . . . . . . . . . . . . . . . . . .
</div>

У меня в браузере («Microsoft Edge» на движке «Chromium») это выглядит так (картинка):



Тонкая серая линия сверху и слева от футбольного поля — это край окна браузера. Я оставил ее на картинке, чтобы было понятно, где относительно границ окна браузера отображается HTML-элемент, представляющий футбольное поле.

Ширина HTML-элемента div, представляющего футбольное поле, задана в вышеприведенном стиле футбольного поля и она равна 200px. Высота же этого HTML-элемента div задается его содержимым, которое представляет собой текст из чередующихся точек и пробельных символов (в том числе пробелов и символов переноса строки). Я расположил точки в 7 рядов по 25 точек в каждом ряду (итого 175 точек). На самом деле, неважно, как располагать точки: можно хоть в одну строку, хоть в любое другое число строк. Главное, чтобы между соседними точками был хотя бы один пробельный символ. Браузер при любом расположении точек в текстовом исходнике HTML-страницы отобразит футбольное поле так, как изображено на картинке выше. Это связано с тем, что браузер при отображении HTML-страницы объединяет любое количество соседних пробельных символов в текстовом исходнике в один пробельный символ в отображении.

Зачем высота HTML-элемента div, представляющего футбольное поле, задается именно так (текстовым содержимым этого HTML-элемента)? Ведь можно было задать высоту футбольного поля в его стиле с помощью CSS-свойства height. Я думаю, что, во-первых, так наше футбольное поле выглядит интереснее, чем если бы у него был простой зеленый фон. Во-вторых, с помощью текстового содержимого мы наглядно показываем, что HTML-элемент img, представляющий мяч, на самом деле находится не в содержимом HTML-элемента div, представляющего футбольное поле (как можно было бы подумать, взглянув на вышеприведенную картинку), а над этим содержимым (на рисунке видно, что часть точек находится под мячом).

Как получилось так, что футбольный мяч оказался над текстовым содержимым футбольного поля, не конфликтуя с ним (обычно, ведь, текст обтекает картинку, если они находятся в одном потоке)? Это обеспечивается с помощью CSS-позиционирования (я о нем уже писал). В стиле футбольного поля указано CSS-свойство position: relative;, а в стиле футбольного мяча указано CSS-свойство position: absolute;. Здесь они действуют в комплексе. Указание position: relative; в стиле футбольного поля делает HTML-элемент div, представляющий футбольное поле, HTML-элементом, относительно которого будет позиционирован дочерний HTML-элемент img, представляющий мяч. Указание же position: absolute; в стиле мяча разрешает абсолютное позиционирование HTML-элемента img, представляющего мяч (то есть можно указывать координаты левого верхнего угла этого HTML-элемента с помощью CSS-свойств top и left). Тут подробнее:

https://developer.mozilla.org/ru/docs/Learn/CSS/CSS_layout/Positioning

После этих пояснений, думаю, в принципе, уже примерно понятно, что нужно делать. Однако, стоит сделать еще одно важное замечание. Когда мы говорим о ширине HTML-элемента div, представляющего футбольное поле, нужно понимать, что эта ширина не включает в себя границу (на рисунке выше она изображена толстой черной сплошной линией). То есть ширина в 200px — это ширина именно прямоугольника зеленого цвета. Ширина черной границы в стиле футбольного поля определена в 10px, значит ширина футбольного поля с включением черной границы составит 220px.

Исходя из этого замечания, отсчет координат для позиционирования мяча нужно вести от левого верхнего угла зеленого прямоугольника. То есть для определения центра футбольного поля следует использовать JavaScript-свойства (метрики) clientWidth и clientHeight, а не offsetWidth и offsetHeight. Пишем код:
let x = field.clientWidth / 2;  // определяем координаты центра
let y = field.clientHeight / 2; // футбольного поля

ball.style.left = x + "px";     // передвигаем мяч в центр
ball.style.top = y + "px";      // футбольного поля

У меня в браузере получилось следующее (картинка):



Я временно сделал вокруг HTML-элемента img, представляющего мяч, красную границу, чтобы было видно контур этого элемента. На этой иллюстрации видно, что наш скрипт поместил в центр футбольного поля не центр мяча, а левый верхний угол HTML-элемента img, представляющего мяч.

Координаты следует откорректировать на половину ширины и половину высоты мяча, чтобы мяч встал в центр футбольного поля. Тут (для мяча) логичнее использовать свойства offsetWidth и offsetHeight, хоть в нашем случае они и равны для нашего мяча соответственно свойствам clientWidth и clientHeight (в других подобных задачах передвигаемый HTML-элемент может быть с границей, тогда этот выбор сыграет свою роль). Меняем код:
let x = field.clientWidth / 2 - ball.offsetWidth / 2;
let y = field.clientHeight / 2 - ball.offsetHeight / 2;

ball.style.left = x + "px";
ball.style.top = y + "px";

У меня в браузере получилось следующее (картинка):



В общем, задача решена.

Но хотелось бы еще написать про два момента. Во-первых (это указано и в решении авторов задачи), из-за того, что у HTML-элемента img в нашем коде прямо нигде не указаны ни высота, ни ширина, при первой загрузке HTML-страницы (Ctrl+F5) координаты для передвижения мяча будут вычислены неверно. Только когда картинка с рисунком мяча попадет в кэш браузера после первой загрузки HTML-страницы, при последующих загрузках (F5) браузер будет знать высоту и ширину HTML-элемента img и всё заработает, как требуется. В своем решении авторы задачи рекомендуют для обхода этой особенности работы браузера прямо указывать размер HTML-элемента img либо в коде на языке HTML, либо в стиле мяча на языке CSS.

Во-вторых, в задаче сказано, что ее можно решить и средствами чистого CSS, но задача состоит именно в том, чтобы написать решение на языке JavaScript, что я и сделал выше. Однако, мне всё же хотелось видеть и решение с помощью чистого CSS, чтобы понимать, о чем идет речь.

Итак, чтобы решить эту задачу с помощью чистого CSS (без использования скрипта на языке JavaScript), я поменял описанный в начале поста стиль мяча и получилось следующее:
<style>
  #field {                     /* стиль футбольного поля */
    width: 200px;
    border: 10px groove black;
    background-color: #00ff00;
    position: relative;
  }

  #ball {                      /* стиль мяча */
    position: absolute;
    top: 50%;                         /* двигаем по вертикали */
    left: 50%;                        /* двигаем по горизонтали */
    transform: translate(-50%, -50%); /* корректируем на половинку размеров мяча */
  }
</style>

Тут подробнее:
https://www.w3.org/Style/Examples/007/center.ru.html
Tags: Образование, Программирование
Subscribe

Recent Posts from This Journal

  • Post a new comment

    Error

    Anonymous comments are disabled in this journal

    default userpic

    Your IP address will be recorded 

  • 0 comments