Про веб-разработку
404 subscribers
39 photos
2 videos
25 links
Авторский блог про: тренды веб-разработки; обучение на примерах; создание проектов.
Связь @deovenkpr
Download Telegram
#исследуем
🎞 Запись видео на JS используя WebRTC + сохранение видео на сервер на примере PHP

Иногда, чтобы развеять свою "CRUD рутину", можно пощупать какие-то технологии, которые не так часто встречаются во время разработки стандартных веб-приложений или реализовать функционал, который редко встречается на сайтах. Хочу сделать это постоянной рубрикой, будем разбирать на реальных кейсах (если концепт интересен, то ставь огонёк 🔥).

Первая статья про WebRTC
Технология реализована для потоковой передачи видео/аудио. Пример сервиса, завязанного на WebRTC: Google Meet. Мы разберём более простой кейс использования этой технологии из реальной практики:
📋 Была поставлена задача реализовать запись видео-отзыва после заполнения формы, после чего сохранять его на сервер.

Рассмотрим два варианта решения этой задачи:
1. Давать пользователям самим загружать файлы
Думаю не стоит объяснять что пользователи вряд-ли будут усложнять себе жизнь записывая видео отдельно. На мобильных устройствах с этим попроще. Можно использовать input с атрибутом capture, который указывает какую камеру включить (если значение user - открывается фронтальная камера, а если environment - задняя):
<input type="file" accept="video/*" capture="user">
Слишком большой размер входного файла + отсутствие предпросмотра/возможности записи на ПК отбрасывает этот метод (можно его использовать как вспомогательный, если WebRTC не поддерживается).

2. Использовать WebRTC
Казалось бы, как WebRTC может тут помочь? Для этого он имеет специальный MediaRecorder, который позволяет хранить транслируемое медиа как Blob). Удобный для пользователей вариант, который позволяет: выводить; пересмотреть и без проблем перезаписать видео.

Выбираем второй вариант
Для большего удобства использовал специальную библиотеку: RecordRTC.

На наглядный пример работы RecordRTC можете посмотреть по ссылке. Там ещё есть 30 других демо + код.
По примеру можно понять что есть много настраиваемых опций: формат; кодек; битрейт; фреймрейт и т.д..
Единственное что, перед выставлением этих опций нужно проверить совместимость (например, Safari запись видео в H264 кодеке не поддерживает). Кстати наличие SSL сертификата для работы #webrtc обязательно.

Из-за вопросов совместимости принял решение записывать видео в формате webm используя видео-кодек VP8 и аудио-кодек OPUS. После того, как на основе примера выше была реализована запись - отправляем её на сервер.

Важно убрать сериализацию. Передавать именно некодированные данные (multipart/form-data), запретить кеширование запроса. Сформировать данные для отправки:
// recorder = RecordRTC(stream, recorderConfig);
let currentBlob = recorder.getBlob();

let blob = new File([currentBlob], fileName, {
type: mimeType
});

let videoData = new FormData();
videoData.append('video', blob);

Ну и реализовать саму отправку.

Загрузка видео
Чтобы сжимать получаемое видео для экономии пространства на сервере можно использовать ffmpeg. После получения видео на сервер нужно обязательно проверить:

- Тип файла:
mime_content_type($_FILES['video']['tmp_name']);

- Размер файла:
Получив количество байт ($_FILES['video']['size']) можно сделать ограничение (например в 200 мб.).

- Другие сопутствующие проверки.
Если другие проверки отработали хорошо, то файл можно поместить куда-то в директорию, а потом проверить длительность видео запустив ffmpeg таким образом:
shell_exec('ffmpeg -i ' . $videoPath . ' -vstats 2>&1') или же использовать специальные библиотеки для более удобной работы с ffmpeg. Команда отдаст информацию про разрешение видео, кодек, длительность и т.д..
Если длительность будет выше приемлемой - можно просто удалить файл.

Это в принципе всё что нужно для реализации такого функционала. Смотрите примеры, пробуйте их использовать и если есть вопросы - задавайте в комментариях 🕺.
#фронтенд #бэкенд #технологии
#исследуем
🎙 Делаем озвучивание текста на сайте в 3 шага (JS)

Синтезатор речи на данный момент является экспериментальной технологией, но как показывает таблица совместимости - её можно вполне массово использовать. При том она довольно простая 😳.

Как синтезировать речь?
1. Получаем точку входа:
const synth = window.speechSynthesis;

2. Делаем речевой запрос:
const message = new SpeechSynthesisUtterance("Озвучь мне это");

Можем настроить под себя (необязательно) указав:
message.lang = "ru-RU"; // язык
message.pitch = "1"; // тональность звука от 0 до 2
message.rate = "1"; // скорость от 0.1 до 10

3. Воспроизводим звук:
synth.speak(message);

Технология завязана на Web Speech API. Для теста можете использовать онлайн редакторы.

Бонус ⭐️. Голос озвучивания тоже можно выбирать:
let voices = synth.getVoices();
console.log(voices);
message.voice = voices[0];

#фронтенд
Можно ли новичку выдавать более качественное программное решение, при этом экономя время?

Однозначно да!
Казалось бы: "за двумя зайцами погонишься, ни одного не поймаешь", но не тут то было. Просто программисты (особенно начинающие), иногда изобретают велосипед, даже не подозревая про существование паттерна под их задачу.

Шаблоны (паттерны) проектирования - проверенные решения большинства проблем, которые возникают при проектировании архитектуры. Необязательно изучать их досконально. Достаточно знать то, какую проблему решает каждый шаблон. Идеально было бы попробовать реализовать каждый, основываясь на конкретных примерах (так можно ещё: подтянуть знания #ооп; улучшить стиль кода).

📖 Подробное объяснение паттернов с примерами можете почитать тут. Есть псевдокод + реализации на: #php, #typescript, #python, java, c#, c++, ruby, swift, #go.

Другие примеры:
- Паттерны на PHP;
- Паттерны на JS.

#статьи #бэкенд #фронтенд
#исследуем
Делаем генератор кастомного QR кода на JS (часть 1)

Для этого возьмём гибкую библиотеку qr-code-styling

1. Копируем весь пример
Тут важно заменить подключаемую версию с 1.5.0 на 1.6.0-rc.1, потому-что там появился функционал расширений

2. Делаем стартовый дизайн
Чуть ниже примера описано как конфигурировать QRCodeStyling. Благо разработчик библиотеки всё упростил. Достаточно зайти на сайт, быстро настроить всё под свои нужды, после чего выкачать конфигурацию

3. Настраиваем #qr
Многие приложения поддерживают свои URL-схемы. Для телеграма, например, мы можем использовать tg://resolve?domain=deovenk вместо https://tttttt.me/deovenk внутри data для быстрого открытия канала

Обратите внимание на опцию errorCorrectionLevel. Существует 4 уровня исправления ошибок на случай если кьюар код немного сотрётся, загрязнится

4. Скачиваем получившийся код
qrCode.download({name: 'qr', extension: 'png'})
// jpeg; webp; svg

Готово! Детальнее внутри комментариев 💬. Если есть вопросы, задавайте 😳

#фронтенд #статья
#исследуем
Делаем настраиваемую вибрацию на JS

👉🏻 Полная реализация внутри моего codepen проекта. Будет приятно если накините лайков за наглядность 😳

1. Запустим одну вибрацию
let launched = window.navigator.vibrate(200)
Если передать слишком большое количество миллисекунд, то устройство может не выполнить операцию (зависит от конкретной модели). Если действие недопустимо, то метод вернёт false

2. Запускаем несколько вибраций
Давайте, например, сделаем сигнал SOS:
let pattern = [100, 30, 100, 30, 100, 30, 200, 30, 200, 30, 200, 30, 100, 30, 100, 30, 100]
navigator.vibrate(pattern)
Как видите, тут передаётся массив, который помимо длительности содержит интервалы паузы

3. Отменяем вибрацию
Допустим Вы решили отменить вибрацию по нажатию на кнопку. Для этого запускаем:
navigator.vibrate(0) или navigator.vibrate([])

4. Бесконечная вибрация
Решается это setInterval-ом. Нужно только посчитать количество секунд, через которые повторно запускать цикл:
pattern.reduce((a, b) => a + b)

✍️ Код на JS тут

#фронтенд
#исследуем
Делаем всплывающее окно на сайте по стандартам (часть 1)

Вот давайте честно, кого не достали эти насквозь пронизанные div-ами диалоги, которые выглядят как один сплошной костыль 😳? Создатели браузеров подсуетились, поэтому мы наконец-то получили очень простое, нативное решение задачи

Встречайте #html элемент <dialog>
👉🏻 Полный пример внутри моего codepen проекта. На вложенных фото упрощённые вырезки

Этот тег существует уже несколько лет, но глобальную поддержку получил только сейчас

Если внутри диалога мы вставим тег <form>, то в атрибут method мы можем передать значение dialog, при котором событие submit вызовется без отправки данных. При этом форма автоматически закроется и вызовет событие close, которое вернёт dialog.returnValue

При открытии диалогового окна (dialog.showModal()) важно обнулять прошлое значение, потому-что оно не стирается

Псевдо-элемент ::backdrop позволяет стилизовать фон, а для создания анимации можно нацелиться на атрибут open

💬 Пишите вопросы в комментариях

#фронтенд
#совет дня
Псевдо-класс :is() в CSS
- полезный синтаксический сахар, который позволяет записать селекторы в более компактную форму. Поддерживается глобально.

#фронтенд #css
#совет дня
Если поле находится за пределами формы, то вы можете установить input-у атрибут form
(или кнопке), указав id формы. Так-же вместо указания label-у поля через атрибут for желательно завернуть input внутрь label.

☕️ Поддержать канал чашкой кофе
#фронтенд #html
#совет дня
Очистить массив от дубликатов можно всего одной строкой
используя Set. Set - особый вид коллекции, который по своей сути содержит множество уникальных значений. Чтобы сделать из Set объекта массив - мы используем оператор spread (...).

☕️ Поддержать чашечкой кофе
#фронтенд #js
#совет дня по #css
Сделать на всю высоту и ширину элемент с фиксированным/абсолютным позиционированием - можно без top, left, right, bottom
🤔. Достаточно один раз указать inset: 0.

☕️ Поддержать чашечкой кофе
#фронтенд
#создаём
Редактор файлов внутри браузера (часть 1)

Мы будем сохранять изменения файлов/папок сразу на устройстве пользователя. Другими словами - редактировать файловую систему прямиком из браузера 🤯!

Это новая рубрика, где мы будем пошагово создавать интересные #проект(-ы). Можете поддержать мой формат чашечкой кофе ☕️.

Для управления файлами используется File System Access API.

Пошаговый процесс создания редактора с пояснениями показан на скринах. При выборе файлов поставил ограничения на: txt; md форматы. Если хотите открыть другой файл (например, html), то явно укажите это внутри проводника, как показано на втором шаге.

На третьем этапе, при получение хэндлера, возвращается массив, но из-за того что файл один - используется деструктуризация [].

👨‍💻 Посмотреть код: https://stackblitz.com/edit/fs
(Важно: проверить редактор можно используя Chrome, Edge или Opera нажав на кнопку Open In New Window из-за того что доступ к файлам не выдаётся iframe-у, подробнее в комментариях)

#исследуем #фронтенд #js
#блог
Наткнулся сегодня на пикчу про CSS Grid-ы (скрин 1), которая зафорсилась за считаные часы. Со стороны она может показаться полезной, но не всё так однозначно. Меня удивило обилие комментариев, где люди пишут что гуглят это каждый раз 🤔. Удивило потому-что вместо использования этого рисунка можно ввести display: grid внутри DevTools-ов, после чего появится иконка где можно визуально выбрать всё что душа пожелает (вложение 2).

Сейчас готовится второй пост про создание файлового редактора в браузере. Тем, кто хочет полноценно освоить CSS Grid-ы в игровой форме, могу порекомендовать вот этот сайт: https://cssgridgarden.com/.

#совет #фронтенд #css
#исследуем
Пользователь закрыл страницу? Оповестим об этом сервер

Допустим, при закрытии страницы мы хотим на JS отправить какую-то аналитику или ошибки, которые встретил пользователь (или хотим менять статус онлайна, например). Проблема в том, что браузер может запретить отправку XHR запроса.

Beacon API - решение, позволяющее отправить асинхронный неблокирующий POST запрос и не дожидается ответа от сервера:

navigator.sendBeacon('/log', JSON.stringify({ meta: 'info' }));

Поддерживается почти везде. Данные можно отправлять разного типа: FormData, URLSearchParams, даже Blob и др.

События, которые нам нужны:
- visibilitychange
- переключение/открытие/сворачивание вкладки;
- beforeunload - перед закрытием вкладки.

Мы так-же можем использовать fetch, указывая атрибуту keepalive значение true.

#фронтенд
#исследуем
Получаем цвет любого пикселя экрана нативной пипеткой на JS (как в DevTools-ах)

Например, мы хотим дать возможность пользователю по клику вызвать пипетку, которой он сможет выбрать цвет даже вне окна браузера. Для нас избыточен
<input type="color" />

Для этого в браузере существует EyeDropper API:
1. Получаем объект пипетки:
const dropper = new EyeDropper();

2. Вызываем метод open, который возвращает промис. В результате получаем выбранный цвет в HEX формате:
const { sRGBHex } = await dropper.open();

Пользователь может закрыть пипетку нажав на Escape. Чтобы сделать это самостоятельно при долгом бездействии или наступлении какого-то события - используем AbortController.

👨‍💻 Посмотреть код: https://stackblitz.com/edit/eyedropper
(пример с завершением запроса)

#фронтенд
Вы знали что сделать шейр экрана на JS/TS из браузера проще чем сделать модалку?

Вот пример использования Screen Capture API. Тут мы указывает что хотим записывать видео без аудио. На 15 строке указываем что выводим этот стрим внутри html тега:
<video autoplay></video>

#блиц #фронтенд
#блог
Почему большинство считает что фронтенд проще бэкенда? Можете объяснить?

Когда люди это говорят, то они явно не рофлят. Не утверждаю что фронтенд сложнее бэкенда, но утверждать обратное как-то странно. Раньше сам писал бэк, понимаю его сложности.

Давайте просто перечислю пару непростых веб-технологий:
WebGL; WebRTC; WebXR; WebAssembly; WebDriver; Streams Web API; Web Workers.
Это ещё не потолок. Лично у меня после этого язык не поворачивается сказать что фронтенд проще.

#блиц #бэкенд #фронтенд
Про веб-разработку
Вы знали что сделать шейр экрана на JS/TS из браузера проще чем сделать модалку? Вот пример использования Screen Capture API. Тут мы указывает что хотим записывать видео без аудио. На 15 строке указываем что выводим этот стрим внутри html тега: <video autoplay></video>…
Останавливаем шейр экрана на JS.
Проверяем наличие стрима вызывая метод getTracks. Он возвращает массив всех текущих медиа-треков. Если такие есть, то останавливаем каждый вызывая stop().

Кстати свойство srcObject у видео относительно новое. Оно позволяет програмно устанавливать поток данных. Раньше для таких целей использовали обычный src, куда вставляли ссылку созданную из URL.createObjectURL().

Так-как тут используется TypeScript, то можете заметить что на 32 строке (<MediaStream>video.srcObject) явно указан тип. Дело в том что TS считает, что video.srcObject может быть MediaStream, Blob или MediaSource. Только MediaStream имеет метод getTracks, поэтому важно указать это явно.

#блиц #фронтенд
Нашёл годный сайт нацеленный подготовить фронтендеров к собеседованиям в максимально сжатые сроки.

Информации там бесплатная, но всё на английском. Рассматриваются темы начиная от технических вопросов, заканчивая soft-скиллами. Думаю по нему более детально тоже пройдёмся.

#ресурс #фронтенд #блиц
This media is not supported in your browser
VIEW IN TELEGRAM
#исследуем
Делаем голосовые сообщения на JS/TS как в телеграмме (часть 1)

Рабочий прототип вместе с кодом можете потестить по ссылке 👨‍💻 или посмотреть как это работает на видео

Это вводная часть. Первым делом нужно создать звуковую-волну используя библиотеку wavesurfer.js. Не забываем так-же установить типы @types/wavesurfer.js.

1. Создаём элемент для волны
const waveWrapper: HTMLDivElement = document.createElement("div")
После чего вставляем его куда-то на страницу

2. Кастомизируем волну
const waveSurfer: WaveSurfer = WaveSurfer.create({
container: waveWrapper,
waveColor: "#D2D2D2",
responsive: true,
...
});

3. Загружаем аудио и воспроизводим
waveSurfer.load('/voice.mp3')
waveSurfer.play()

Первый шаг на пути реализации функционала голосовых сообщений готов. Напомню что ссылка на прототип сверху 👆🏻

#фронтенд
Что делает void оператор JS/TS? Пример использования

Допустим мы вызываем какую-то async функцию, которая делает запрос к серверу и нам не нужно обрабатывать вернувшийся результат в Promise. Мы можем напрямую это указать. Указывая этот оператор - всегда будет возвращаться undefined, даже если что-то должно вернуться. С оператором void ещё можно использовать самовызывающиеся (IIFE) функции:
void function deovenk() { /* ... */ }()

#блиц #фронтенд