JS30. Задание 16 CSS Text Shadow Mouse Move Effect
Демо https://js3014.github.io/
Код https://github.com/js3014/js3014.github.io
Создаём эффект движения тени текста вслед за мышкой. В видео автор демонстрирует этот эффект на примере заголовка одного из своих курсов по флексбоксах: https://flexbox.io/ но на данный момент эффект движения он убрал, оставив только статичные тени.
html-разметка очень простая: один блок и заголовок в нём:
<div class="hero">
<h1 contenteditable>🔥WOAH!</h1>
</div>
Смайлик с костром можно скопировать здесь: https://emojipedia.org/fire/
(другие смайлики там тоже есть)
UPD: почему-то у меня смайлик исчез. как же его автор добавлял?
Стили выглядят так:
.hero {
min-height: 100vh;
display: flex;
justify-content: center;
align-items: center;
color: black;
}
h1 {
text-shadow: 10px 10px 0 rgba(0,0,0,1);
font-size: 100px;
}
В очередной раз порадовалась простоте центрирования элемента при помощи флексбоксов, чтобы разместить блок по центру страницы достаточно указать ему свойства:
display: flex;
justify-content: center;
align-items: center;
и какую-нибудь высоту, если высота не существенна, указываем min-height
Переходим к js.
1. Находим элементы: блок hero и заголовок h1 внутри этого блока:
var hero = document.querySelector(".hero");
var text = hero.querySelector("h1");
Создаём функцию
function shadow(event) {
//TODO: Magic
}
Блоку hero добавляем слушатель, который будет отслеживать движения мышки и запускать функцию shadow если мышка движется
hero.addEventListener("mousemove", shadow);
Выведем событие event в консоль
В свойствах объекта MouseEvent видим offsetX и offsetY, показывающие отступ курсора мыши по горизонтали и вертикали от края целевого DOM узла.
Нам понадобятся размеры: ширина и высота нашего блока:
var { offsetWidth: width, offsetHeight: height } = hero;
А также текущее положение мыши:
var { offsetX: x, offsetY: y } = event;
С таким присваиванием сталкиваюсь впервые и не очень его понимаю
Автор говорит, что для сокращения количества кода использует возможности, предоставленные ES6
Код можно сделать более очевидным и понятным, хоть и менее лаконичным, если вместо
var { offsetWidth: width, offsetHeight: height } = hero;
написать
var width = hero.offsetWidth;
var height = hero.offsetHeight;
а строки
var { offsetX: x, offsetY: y } = event;
заменить на
x = event.offsetX;
y = event.offsetY;
Выведем в консоль значения console.log(x, y);
console.log(x, y);
Верхний левый угол страницы имеет координаты 0, 0, нижний правый 800, 800.
Но когда курсор попадает на блок с заголовком, в нём координаты курсора показывают положение заголовка относительно блока, и 0, 0 это уже верхний левый угол блока.
Если я правильно понимаю, это связано с всплытием (или погружением?): так как у целевого элемента hero есть потомок h1, то слушатель hero.addEventListener отслеживает события и на блоке и на заголовке тоже.
Вообще, это удобно и правильно, но в данном случае такое поведение нас не устраивает.
Автор предлагает добавить условие:
if (this !== event.target) {
x = x + event.target.offsetLeft;
y = y + event.target.offsetTop;
}
Здесь event.target - элемент, который вызывает событие (блок hero). Если целевой элемент не блок (а заголовок) то х и у равно смещение курсора внутри блока плюс расстояние блока от левого или верхнего края страницы.
Попробовала предотвратить распространение событий без условия при помощи event.stopImmediatePropagation(); или event.stopPropagation(); - не работает. Но ведь должно?
Размер смещения тени текста выбираем по своему усмотрению
var walk = 500; // 500px
Дальше пошла математика и воображение. Нам нужно, чтобы при движении курсора тени смещались симметрично, относительно текста.
Для этого автор использует формулы:
var xWalk = Math.round((x / width * walk) - (walk / 2));
var yWalk = Math.round((y / height * walk) - (walk / 2));
Формулы, пожалуй, слегка не очевидны, но они работают.
Остался последний шаг: добавить тени для текста и стили для них.
Добавим вначале одну тень:
text.style.textShadow = 10px 10px 0 red;
Первое 10рх - смещение тени по горизонтали, второе 10рх - смещение тени по вертикали, 0 - размытие, red - цвет.
Теперь изменим смещение на наши переменные xWalk и yWalk. Получим:
text.style.textShadow = `${xWalk}px ${yWalk}px 0 red`;
Прекрасно. Работает!
Попробуем добавить не одну, а несколько теней:
text.style.textShadow = `
${xWalk}px ${yWalk}px 0 rgba(255,0,255,0.7),
${xWalk * -1}px ${yWalk}px 0 rgba(0,255,255,0.7),
${yWalk}px ${xWalk * -1}px 0 rgba(0,255,0,0.7),
${yWalk * -1}px ${xWalk}px 0 rgba(0,0,255,0.7)
`;
Тоже работает.
Код полностью:
var hero = document.querySelector(".hero");
var text = hero.querySelector("h1");
var walk = 500; // 500px
function shadow(event) {
var width = hero.offsetWidth;
var height = hero.offsetHeight;
x = event.offsetX;
y = event.offsetY;
if (this !== event.target) {
x = x + event.target.offsetLeft;
y = y + event.target.offsetTop;
}
var xWalk = Math.round((x / width * walk) - (walk / 2));
var yWalk = Math.round((y / height * walk) - (walk / 2));
text.style.textShadow = `
${xWalk}px ${yWalk}px 0 rgba(255,0,255,0.7),
${xWalk * -1}px ${yWalk}px 0 rgba(0,255,255,0.7),
${yWalk}px ${xWalk * -1}px 0 rgba(0,255,0,0.7),
${yWalk * -1}px ${xWalk}px 0 rgba(0,0,255,0.7)
`;
}
hero.addEventListener("mousemove", shadow);
Код https://github.com/js3014/js3014.github.io
Создаём эффект движения тени текста вслед за мышкой. В видео автор демонстрирует этот эффект на примере заголовка одного из своих курсов по флексбоксах: https://flexbox.io/ но на данный момент эффект движения он убрал, оставив только статичные тени.
html-разметка очень простая: один блок и заголовок в нём:
<div class="hero">
<h1 contenteditable>🔥WOAH!</h1>
</div>
Смайлик с костром можно скопировать здесь: https://emojipedia.org/fire/
(другие смайлики там тоже есть)
UPD: почему-то у меня смайлик исчез. как же его автор добавлял?
Стили выглядят так:
.hero {
min-height: 100vh;
display: flex;
justify-content: center;
align-items: center;
color: black;
}
h1 {
text-shadow: 10px 10px 0 rgba(0,0,0,1);
font-size: 100px;
}
В очередной раз порадовалась простоте центрирования элемента при помощи флексбоксов, чтобы разместить блок по центру страницы достаточно указать ему свойства:
display: flex;
justify-content: center;
align-items: center;
и какую-нибудь высоту, если высота не существенна, указываем min-height
Переходим к js.
1. Находим элементы: блок hero и заголовок h1 внутри этого блока:
var hero = document.querySelector(".hero");
var text = hero.querySelector("h1");
Создаём функцию
function shadow(event) {
//TODO: Magic
}
Блоку hero добавляем слушатель, который будет отслеживать движения мышки и запускать функцию shadow если мышка движется
hero.addEventListener("mousemove", shadow);
Выведем событие event в консоль
В свойствах объекта MouseEvent видим offsetX и offsetY, показывающие отступ курсора мыши по горизонтали и вертикали от края целевого DOM узла.
Нам понадобятся размеры: ширина и высота нашего блока:
var { offsetWidth: width, offsetHeight: height } = hero;
А также текущее положение мыши:
var { offsetX: x, offsetY: y } = event;
С таким присваиванием сталкиваюсь впервые и не очень его понимаю
Автор говорит, что для сокращения количества кода использует возможности, предоставленные ES6
Код можно сделать более очевидным и понятным, хоть и менее лаконичным, если вместо
var { offsetWidth: width, offsetHeight: height } = hero;
написать
var width = hero.offsetWidth;
var height = hero.offsetHeight;
а строки
var { offsetX: x, offsetY: y } = event;
заменить на
x = event.offsetX;
y = event.offsetY;
Выведем в консоль значения console.log(x, y);
console.log(x, y);
Верхний левый угол страницы имеет координаты 0, 0, нижний правый 800, 800.
Но когда курсор попадает на блок с заголовком, в нём координаты курсора показывают положение заголовка относительно блока, и 0, 0 это уже верхний левый угол блока.
Если я правильно понимаю, это связано с всплытием (или погружением?): так как у целевого элемента hero есть потомок h1, то слушатель hero.addEventListener отслеживает события и на блоке и на заголовке тоже.
Вообще, это удобно и правильно, но в данном случае такое поведение нас не устраивает.
Автор предлагает добавить условие:
if (this !== event.target) {
x = x + event.target.offsetLeft;
y = y + event.target.offsetTop;
}
Здесь event.target - элемент, который вызывает событие (блок hero). Если целевой элемент не блок (а заголовок) то х и у равно смещение курсора внутри блока плюс расстояние блока от левого или верхнего края страницы.
Попробовала предотвратить распространение событий без условия при помощи event.stopImmediatePropagation(); или event.stopPropagation(); - не работает. Но ведь должно?
Размер смещения тени текста выбираем по своему усмотрению
var walk = 500; // 500px
Дальше пошла математика и воображение. Нам нужно, чтобы при движении курсора тени смещались симметрично, относительно текста.
Для этого автор использует формулы:
var xWalk = Math.round((x / width * walk) - (walk / 2));
var yWalk = Math.round((y / height * walk) - (walk / 2));
Формулы, пожалуй, слегка не очевидны, но они работают.
Остался последний шаг: добавить тени для текста и стили для них.
Добавим вначале одну тень:
text.style.textShadow = 10px 10px 0 red;
Первое 10рх - смещение тени по горизонтали, второе 10рх - смещение тени по вертикали, 0 - размытие, red - цвет.
Теперь изменим смещение на наши переменные xWalk и yWalk. Получим:
text.style.textShadow = `${xWalk}px ${yWalk}px 0 red`;
Прекрасно. Работает!
Попробуем добавить не одну, а несколько теней:
text.style.textShadow = `
${xWalk}px ${yWalk}px 0 rgba(255,0,255,0.7),
${xWalk * -1}px ${yWalk}px 0 rgba(0,255,255,0.7),
${yWalk}px ${xWalk * -1}px 0 rgba(0,255,0,0.7),
${yWalk * -1}px ${xWalk}px 0 rgba(0,0,255,0.7)
`;
Тоже работает.
Код полностью:
var hero = document.querySelector(".hero");
var text = hero.querySelector("h1");
var walk = 500; // 500px
function shadow(event) {
var width = hero.offsetWidth;
var height = hero.offsetHeight;
x = event.offsetX;
y = event.offsetY;
if (this !== event.target) {
x = x + event.target.offsetLeft;
y = y + event.target.offsetTop;
}
var xWalk = Math.round((x / width * walk) - (walk / 2));
var yWalk = Math.round((y / height * walk) - (walk / 2));
text.style.textShadow = `
${xWalk}px ${yWalk}px 0 rgba(255,0,255,0.7),
${xWalk * -1}px ${yWalk}px 0 rgba(0,255,255,0.7),
${yWalk}px ${xWalk * -1}px 0 rgba(0,255,0,0.7),
${yWalk * -1}px ${xWalk}px 0 rgba(0,0,255,0.7)
`;
}
hero.addEventListener("mousemove", shadow);