О useRef() Hook
Доступ к элементам DOM является ядром JavaScript в браузере, используя vanilla JavaScript, к элементу с классом можно получить доступ с помощью:
<div class="title"> This is a title of a div </div> <script> const titleDiv = document.querySelector("div.title") </script>
Ссылку на элемент можно использовать для таких интересных вещей, как изменение текстового содержимого или изменение имени класса и многих других операций.
Со временем библиотеки манипулирования DOM, такие как jQuery, сделали этот процесс простым с помощью одного вызова функции с использованием знака . Получить тот же элемент с помощью jQuery можно через , также текстовое содержимое может быть обновлено через API jQuery:
Шаг 1: наложите текст на ваше видео
Я предполагаю, что у вас уже есть видео проект, загруженный в вашу временную шкалу Videoleap, на которую вы накладываете текст. Однако вы также можете запустить пустой проект без видео, что полезно, если вы хотите создать титульную карточку с простым черным фоном, но мы будем использовать реальное видео в качестве примера в этом руководстве.
Не выделив видеоклипы, переместите точку воспроизведения туда, где вы хотите, чтобы текст начинался (конечно, вы всегда можете изменить это позже), затем нажмите кнопку «Текст» на панели инструментов. Это добавит новый слой поверх любого видео под ним (или пустую область, если вы хотите черный фон), и текстовое поле появится в окне предварительного просмотра.
Чтобы добавить текст, сделайте то, что он говорит, и дважды нажмите в поле, чтобы открыть клавиатуру. Введите все, что вам нужно или вы хотите, чтобы наложение было наложено на видео. Используйте клавишу возврата, чтобы создать разрыв строки, если это необходимо. Нажмите на флажок или в любом месте за пределами текстового поля, чтобы сохранить текст.
Теперь вы можете расположить текст там, где он выглядит лучше всего. Нажмите и удерживайте текстовое поле, затем перетащите его туда, куда вы хотите. Чтобы наклонить текст, поверните его вбок или переверните, поместите один палец рядом с внешней стороной коробки, а другой палец — напротив другой на другой стороне, а затем поворачивайте пальцы, пока они не будут расположены так, как вы хотите. Чтобы увеличить или уменьшить размер, вырежьте или ущипните, соответственно.
Наименование Action’ов
Файл user.actions.ts:
Как можно увидеть, в файле создано 3 action’а:
- loadUser
- loadUserSuccess
- loadUserFailure
Важным нюансом тут является наименование action’ов, где используется следующая парадигма:
Действие — Сущность — Состояние/Статус
Данная модель наименования объясняется следующим образом.
Допустим есть множество действий, которые необходимо совершить с сущностью: create, change, update, load, merge, delete, migrate, reset, confirm, …
И если не придерживаться какой-то логики, с помощью которой можно идентифицировать цепочку событий, то поддержка подобного рода state’а превращается в очень сложную задачу.
Возьмём для примера 3 действия для user’а: create, delete, change.
Хоть случай и надуманный, но уже вызывает некоторое отвращение.
Такая же проблема была и раньше в Nx и Ngrx. Если посмотреть примеры action’ов в Ngrx, то можно было увидеть нечто подобное:
Даже если абстрагироваться от стиля написания и переписать в упрощённом виде:
Для избежания описанных выше проблем, предлагается использовать модель действие-сущность-состояние.
Обычно в проекте присутствуют группы action’ов, из которых выстраивается цепочка событий. Например, загрузка авторизированного пользователя.
Опишем всю цепочку:
- Инициировать событие, которые вызовет загрузку пользователя — loadUser
- После успешной загрузки пользователя, вызвать событие сохранения загруженного пользователя в state — loadUserSuccess
- Если при загрузке пользователя произошла ошибка, вызвать событие ошибки загрузки (для того, чтобы показать ошибку клиенту) — loadUserFailure.
Обычно, данной цепочки хватает, для реализации. Но чем сложнее логика, тем больше может становиться цепочка. Например, если вы используете SSR, и не хотите загружать предварительные данные, а оставить это на уровне browser.app. Тогда нужна ещё одна стадия — проверка возможности загрузки:
- Вызов события, которое проверит возможность загрузки пользователя и вызовет событие загрузки — loadUser
- Инициировать событие, которые вызовет загрузку пользователя — loadUserRun
- После успешной загрузки пользователя, вызвать событие сохранения загруженного пользователя в state — loadUserSuccess
- Если при загрузке пользователя произошла ошибка, вызвать событие ошибки загрузки (для того, чтобы показать ошибку клиенту) — loadUserFailure.
Также этот пример, может обрабатывать случай, когда вызвана повторная загрузка пользователя, хотя первая попытка ещё не завершилась.
Для того, чтобы отлавливать подобные случаи, можно добавить состояние cancel:
- Вызов события, которое проверит возможность загрузки пользователя и вызовет событие загрузки — loadUser, если загрузка не возможно вызвать событие loadUserCancel
- Инициировать событие, которые вызовет загрузку пользователя — loadUserRun
- После успешной загрузки пользователя, вызвать событие сохранения загруженного пользователя в state — loadUserSuccess
- Если при загрузке пользователя произошла ошибка, вызвать событие ошибки загрузки (для того, чтобы показать ошибку клиенту) — loadUserFailure.
Приведём реализацию:
Как можно заметить, мы однозначно можем выстроить цепочку, так как все action’ы начинаются одинаково с loadUser, а все состояния различны. Это очень удобно при использовании IDE, где при авто подсказках, все ваши action’ы выстроены друг под другом.
Вынесение данных из action’а в payload
Последним, что можно сказать об организации action’ов, это правила передачи параметров в action.
Так как createAction, формально создаёт новый объект со свойством type и всеми остальными свойствами, переданными в props, то получается непреднамеренное объединение двух разных составляющих Redux.
Рассмотрим проблему на примере.
Допустим необходима загрузка пользователя по id. Для этого в action добавим параметр.
Однако, результатом работы будет action вида:
И в этом нет ничего плохого. Но если вы решите передавать payload между action’ами, то тогда, будет необходимо дублировать и указывать все переданные параметры. В нашем случае это один параметр:
Однако, этого можно избежать, если использовать подход вынесения данных в payload. Вместо того, чтобы передавать набор свойств в action, передавать туда объект, который внутри себя будет хранить все параметры:
Это позволяет в выше описанном случае, просто передать весь payload целиком, без ненужного присваивания каждого из свойств payload’а.
Тогда, согласно описанному подходу, action’ы для пользователя будут выглядеть так:
где ActionPropsPayload простая абстракция для создания объекта со свойством payload.
Анимация текста
Текст должен появляться постепенно, не перетягивать на себя внимание, а как бы мягко сопровождать движение фотографий. Наименование страны будет появляться с помощью эффекта Slow Fade On, который находится на панели Effects & Presets — Animation Presets — Text — Animate In
Слой с цифрами анимируем с помощью эффекта Decoder Fade In, который находится там же
Наименование страны будет появляться с помощью эффекта Slow Fade On, который находится на панели Effects & Presets — Animation Presets — Text — Animate In. Слой с цифрами анимируем с помощью эффекта Decoder Fade In, который находится там же.
Настройки анимации текста
Пример анимации текста для первой фотографии:
Готовая анимация текста
Давайте разберём, как сделать такую анимацию и синхронизировать её с движением фотографии.
Выберите текстовый слой Iceland и перейдите на панель Effects & Presets, найдите эффект Slow Fade On в списке Animation Presets — Text. Перенесите этот эффект мышкой на текстовый слой.
Применяем эффект Slow Fade On на наш текст
Следующим шагом нужно настроить ключевые кадры этого слоя так, чтобы текст появлялся вместе с фотографией. Для этого перейдите в свойства этого слоя, выберите Text — Animator 1 — Range Selector 1 и перенесите первый ключевик на двенадцатый кадр, а второй — на третий кадр второй секунды.
Настраиваем ключевые кадры
Затем выберите слой с цифрой и примените к нему эффект Decoder Fade In, настройте ключевые кадры свойства Text — Animator 1 — Range Selector 1: первый ключевой кадр на втором кадре второй секунды, второй ключевой кадр на девятнадцатом кадре второй секунды.
Применяем эффект Decoder Fade In к слою с цифрой
Когда вы проделаете эти манипуляции со всеми фотографиями, смещая кадры относительно конкретной фотографии, должно получиться так:
Анимация фото и надписей готова
Следующим шагом нужно анимировать боковые фотографии и текст к ним.
Они анимируются по тому же принципу, что и предыдущие фотографии, — с помощью маски. Анимация фотографий начинается по окончании движения последней фотографии основного блока Norway. Левая фотография двигается справа налево, правая — слева направо.
Схема движения анимации крайних фотографий
Выберите фотографии Spain и France, переместите индикатор текущего времени на вторую секунду (на этой секунде заканчивается анимация основной композиции, состоящая из трёх фотографий), нажмите клавишу T (Opacity) на клавиатуре и поставьте ключевой кадр со значением 0%. Затем перейдите на девятнадцатый кадр третьей секунды и установите ключевой кадр там со значением прозрачности 10%. Получилась анимация прозрачности по двум ключевым кадрам от 0% к 10%.
Настройка анимации прозрачности по ключевым кадрам
Анимация прозрачности боковых фотографий готова.
Мы настроили анимацию прозрачности для боковых фотографий
Теперь нужно настроить анимацию маски. Выделите фотографию France, в свойствах выберите Masks — Mask 1 — Mask Patch, перейдите на вторую секунду и установите ключевой кадр. Затем передвиньте индикатор текущего времени на двадцатый кадр третьей секунды и поставьте ещё один ключевой кадр.
Настройка анимации маски по ключевым кадрам
Вернитесь к первому ключевику и передвиньте две крайние вершины маски влево так, чтобы исчезла фотография.
Настраиваем анимацию исчезновения боковой фотографии
Не забудьте настроить Easy Ease: выделите ключевые кадры и нажмите F9, чтобы анимация была более плавной.
Тоже самое проделайте с фотографией Spain. Настройте появление текста самостоятельно, его анимация делается точно так же, как мы делали ранее.
Напомню последовательность действий для анимации текста: перейдите на панель Effects & Presets, раскройте список Animation Presets — Text — Animate In и выберите эффект Slow Fade On. Мышкой перенесите эффект на текст, затем перейдите в свойство текста Text — Animator 1 — Range Selector 1 и настройте ключевые кадры так, чтобы эффект появления текста совпадал с появлением фотографии.
Вот что получилось.
Анимация первого экрана полностью готова
Организация Reducer’а
Шаблон редьюсера:
Из примера видно, что Nx запихнули в schematic’у использование EntityState.
Ngrx/Entity — это отдельная песня, про улучшение и автоматизацию работы с сущностями.
Вы можете ознакомиться с принципами Entity на сайте ngrx и попробовать использовать данные подходы, но в данной статье мы ограничимся стандартным ядром ngrx.
Поэтому удалим все использования entity, а также немного переименуем названия, где State -> UserState.
Наименования свойств в State также важно, как задание нужных имён для action’ов. Для задания имён свойствам предлагается давать максимально приближенные к названиям action’ов
Например для загрузки пользователя это будет:
Для задания имён свойствам предлагается давать максимально приближенные к названиям action’ов. Например для загрузки пользователя это будет:
Также хорошей практикой считается переименование intialState в именованную переменную, связанную со state, где в нашем случае это userInitialState.
И соответственно сами мутации, которые происходят при вызове того или иного action’а:
Выше, мы описывали принцип работы экшенов, в виде цепочки событий. В данном примере приведена её реализация.
- UserLoad вызывает события проверки возможности загрузки. Данный action не изменяет состояние state.
- Если загрузка пользователя возможна, вызывается событие UserLoadRun, которое записывает в state, состояние, что происходит асинхронная загрузка пользователя, а также сбрасывается предыдущая ошибка загрузки, если таковая была.
- При успешной загрузке вызывается событие UserLoadSuccess, где в state записывается загруженный User и флаг асинхронной загрузки ставится false.
- При неуспешной загрузке вызывается событие UserLoadFailure, где в state записывается ошибка загрузки и флаг асинхронной загрузкт ставится false.
В итоге, полный файл user.reducer.ts получит вид:
ЛОКАЛЬНЫЕ ПЕРЕМЕННЫЕ НЕ СОХРАНЯЮТСЯ ПРИ ПОВТОРНОМ РЕНДЕРИНГЕ #
Давайте рассмотрим шаги от первоначального рендеринга до повторного рендеринга компонента React.
- Первоначально компонент инициализирует все переменные значениями по умолчанию, а также сохраняет все состояния и ссылки в уникальном хранилище, как определено алгоритмом React.
- Когда новое обновление доступно для компонента через обновление его props или состояния, React извлекает старое значение для состояний и refs из своего хранилища и повторно инициализирует состояние старым значением, также применяя обновление к состояниям и refs, которые имеют обновление.
- Затем он запускает функцию для компонента, чтобы перерисовать компонент с обновленными состояниями и refs. При повторном рендеринге переменные также будут заново инициализированы, чтобы иметь свои начальные значения, определенные в компоненте, поскольку они не отслеживаются.
- Затем компонент перерисовывается.
Ниже приведен пример, который может это проиллюстрировать:
function Card (props) { let toggled = false; const handleToggleBody = () => { toggled = true; console.log(toggled); }; useEffect(() => { console.log(“Component rendered, the value of toggled is:“, toggled); }, ); return ( <section className=“card”> <h3 className=“card__title” onMouseMove={handleToggleBody}> {props.title} </h3> {toggled && <article className=“card__body”>{props.body}</article>} </section> ); } // Renders the application function App () { const = useState({ title: “Something”, body: “uniquely done”, }); useEffect(() => { setTimeout(() => { setCardDetails({ title: “We”, body: “have updated something nice”, }); }, 5000); // Force an update after 5s }, []); return ( <div> <Card title={cardDetails.title} body={cardDetails.body} /> </div> ); }
В приведенном выше коде отображается как дочерний в компоненте . Компонент полагается на внутренний объект состояния для хранения деталей карты. Кроме того, компонент обновляет состояние через 5 секунд после первоначального рендеринга, чтобы заставить повторно отобразить список компонентов .
имеет несколько измененное поведение; вместо переключения состояния , оно устанавливается в , когда курсор мыши помещается на заголовок карточки. Также используется хук для отслеживания значения переменной после повторного рендеринга.
Использование переменной вместо state (второй тест)
В результате после выполнения этого кода и наведения мыши на заголовок переменная обновляется внутри, но не вызывает повторного рендеринга, в то время как повторный рендеринг запускается родительским компонентом, который повторно инициализирует переменную в начальное состояние , как определено в компоненте. Интересно!
4 ответа
Лучший ответ
Они слишком разные.
запустит функцию внутри при изменении массива зависимостей.
создаст новую функцию при изменении массива зависимостей.
Вы не можете переключить только с , потому что вам также нужна логика для запуска вновь созданной функции. (Я полагаю, вы могли бы реализовать это, если бы вы также использовали ref, но это было бы довольно странно.)
Вы не можете переключить с помощью , потому что очень часто не хотите запускать вновь созданную функцию немедленно — скорее, вы обычно хотите передать ее как опору. к какому-то другому компоненту.
в первую очередь существует в целях оптимизации, чтобы уменьшить количество повторных отрисовок дочернего компонента.
3
CertainPerformance
29 Июн 2021 в 04:44
useEffect будет запускать функцию внутри при изменении массива зависимостей.
useCallback создаст новую функцию при изменении массива зависимостей.
Давайте возьмем пример. Если я запустил приведенный ниже код и нажму первую кнопку, он также всегда будет повторно отображать MemoComponent. Почему, потому что каждый раз, когда мы передаем в this новую функцию onClick. Чтобы избежать повторного рендеринга MemoComponent, мы можем обернуть onClick на useCallback. Всякий раз, когда вы хотите создать новую функцию, передайте состояние в массив зависимостей.
Если вы хотите выполнить какое-либо действие при изменении состояния, вы можете написать внутри useEffect.
1
Rahul Sharma
29 Июн 2021 в 04:52
Нет, они не такие.
— используется для запуска побочных эффектов в компоненте, когда что-то меняется. делает не верну тебе ничего. Он просто запускает фрагмент кода в компоненте.
— возвращает функцию, но на самом деле не выполняет код
Важно понимать, что функции — это объекты в Javascript. Если вы не используете , функция, которую вы определяете внутри компонента, будет воссоздается всякий раз, когда компонент перестраивается
Примере
Рассмотрим этот пример, этот компонент будет работать в бесконечном цикле. Подумайте, почему?
Потому что при каждом рендеринге testFunction будет воссоздаваться, и мы уже знаем, что ueEffect будет запускать код при изменении testFunction. И поскольку testFunction изменяется при каждом рендеринге, useEffect будет продолжать работать, и, следовательно, будет бесконечный цикл.
Чтобы исправить это, мы должны сообщить о реакции: эй, пожалуйста, не воссоздайте testFunction при каждом рендере, создавайте его только при первом рендеринге (или когда что-то меняется, от чего это зависит).
Это не будет бесконечным циклом, поскольку экземпляр testFunction изменится только при первом рендеринге и, следовательно, useEffect будет запускаться только один раз.
2
Archit Garg
29 Июн 2021 в 05:13
useEffect
Это альтернатива для методов жизненного цикла компонентов класса componentDidMount, componentWillUnmount, componentDidUpdate и т. Д. Вы также можете использовать его для создания побочного эффекта при изменении зависимостей, например: «Если изменяется какая-то переменная, сделайте это».
Всякий раз, когда у вас есть какая-то логика, которая выполняется как реакция на изменение состояния или перед тем, как изменение вот-вот произойдет.
useCallback
При каждом рендеринге все, что находится внутри функционального компонента, будет запускаться снова. Если дочерний компонент зависит от функции родительского компонента, дочерний компонент будет повторно визуализировать каждый раз, когда родительский повторно выполняет визуализацию, даже если эта функция «не изменяется» (ссылка изменяется, но то, что функция делает, выиграет » т). Он используется для оптимизации, избегая ненужных рендеров от дочернего элемента, заставляя функцию изменять ссылку только при изменении зависимостей. Вы должны использовать его, когда функция является зависимостью от побочного эффекта, например. useEffect.
Всякий раз, когда у вас есть функция, которая зависит от определенных состояний. Этот хук предназначен для оптимизации производительности и предотвращает переназначение функции внутри вашего компонента, если не изменилось зависимое состояние.
Без useCallback myFunction будет переназначаться при каждом рендеринге. Следовательно, он использует больше времени вычислений, чем при использовании useCallback.
Anu Singh
29 Июн 2021 в 05:26
Создание экшенов (Actions)
Экшены являются одной из главных составляющих Redux. Action формально представляют события, которые происходят в приложении, и при возникновении которых должно произойти изменение состояния (state).
Action представляет собой простой интерфейс, который из обязательных полей имеет только type, который должен быть уникальным во всем проекте. Именно с помощью type, reducer понимает, какой action перед ним и что с ним нужно сделать.
Примером простого экшена может быть объект:
{ type: ' Load User'}
Action может содержать необходимые данные, помимо типа. Например :
{ type: ' Load User', userId: 55}
Обычно, все дополнительные свойства называются payload, который формально объединён с action’ом.
Хорошей практикой считается вынесение всех данных в отдельный объект payload:
{ type: ' Load User', payload: { userId: 55 ... }}
В Ngrx есть 2 пути создания экшенов:
- Использование функции createAction
- Использование класса (подход применяется в версии ngrx 7 или ниже)
Создание экшена с помощью createAction является рекомендуемым*.
Создание экшена с помощью класса выглядит следующим образом:
Как уже не сложно заметить, использование класса существенно увеличивает количество кода при одном action’е.
Если исходить из практики, то нужно отметить пару вещей:Важны правильные названия для Action’ов, так как халатное отношение к наименованию, может привести к замешательству разработчика и породить множество нежелательных side эффектов. Например использование грамматики в наименовании (LoadX, LoadingX, LoadedX) как правило не только мешает, но не даёт чёткого ответа на тип Action’а. Есть множество действий (неправильных глаголов), которые не имеют второй/третьей формы и поэтому, для описания подобного рода экшенов, приводит разработчика к придумыванию решений (костылей), которые могли бы отражать суть экшена. Подобный подход был представлен разработчиками Nx, хотя и возможно они просто использовали примеры Ngrx.
Важны правильные названия для Action’ов, так как посредственное отношение к наименованию, может привести к замешательству разработчика и породить множество нежелательных side эффектов. Например использование грамматики в наименовании (LoadX, LoadingX, LoadedX) как правило не только мешает, но не даёт чёткого ответа на тип Action’а. Есть множество действий (неправильных глаголов), которые не имеют второй/третьей формы и поэтому, для описания подобного рода экшенов, приводит разработчика к придумыванию решений (костылей), которые могли бы отражать суть экшена
Подобный подход был представлен разработчиками Nx, хотя и возможно они просто использовали примеры Ngrx.
Хоть и с первого взгляда, создание action’ов с помощью классов привлекает внимание сторонников ООП, практика показывает, что использование enum будет нужно только для создания action’а и его связи с reducer’ом. Однако, поддержка enum’а и порождение классов существенно увеличивают время и размер store, что как правило, не является оправданным.
Что такое judder эффект и как его увидеть
Одной из неприятных особенностей данной ситуации является так называемый judder-эффект. Если картинка в видео выше понравилась, и вы не заметили ничего необычного, то быстрее закройте данную статью и забудьте про Auto Frame Rate навсегда.
Если же за тестовые 20 секунд глаза сильно напряглись и начали уставать – продолжаем изучать тему.
С judder-эффектом сталкивается любой покупатель нового телевизора или Smart-TV бокса. В рекламном ролике или магазине на тестовых стендах транслируются специальные ролики, который сняты с поддерживаемой для каждой модели частотой кадров и разрешением. Все выглядит максимально плавно, эффектно и реалистично.
Но когда счастливый обладатель нового “телека” приносит его домой и начинает воспроизводить свой контент, его ждёт небольшое разочарование.
У поставщика кабельного телевидения или T2 используется одна частота кадров, вещающие в цифровом формате IPTV-каналы имеют другую частоту, контент в стриминговых видеосервисах настроен на третью частоту. Загруженные вами видео могут как совпадать по частоте с любым из перечисленных вариантов, так и иметь свой уникальный показатель.
Если количество кадров в секунду у контента совпадёт с настройками ТВ (или будет кратно параметрам), пользователь увидит чёткую картинку без рывков и размытия. В противном случае будет наблюдаться тот самый judder-эффект.
Большинство современных телевизоров поддерживают работу на частоте 60 Гц или 120 Гц. При этом они без проблем справляются с контентом, который снят с частотой 30 или 60 кадров в секунду. Всё это кратные значения и, например, панель с частотой 120 Гц при воспроизведение ролика с частотой 30 кадров в секунду будет отображать каждый кадр по четыре раза.
Так же гладко пройдёт воспроизведение 24-кадрового ролика на экране с частотой 120 Гц (по пять повторений каждого кадра). А вот на экране с максимальной частотой 60 Гц 24-кадровое видео уже будет выглядеть неидеально.
Вот так это выглядит на графике:
Трансляция 24-кадрового контента на частоте 60 Гц
Получается так называемый эффект “телесин” в соотношении два к трём. Один кадр видео телевизор будет отображать 2/60 доли секунды, а следующий кадр видео будет длиться 3/60 доли секунды и так далее. Глаз человека очень чётко заметит такой эффект дрожания или подтормаживания картинки. Не будет общего ощущения плавности, любой голливудский шедевр превратится в любительское видео с дешёвой камеры.
Всевозможные системы сглаживания (или так называемые “уплавнялки”) сейчас есть в арсенале любого крупного производителя телевизоров и матриц. Умные системы способны добавлять недостающие кадры и делать частоту фреймов кратной частоте выводимого сигнала. Так в нужных местах появится лишний кадр, и указанного выше эффекта наблюдаться не будет.
Наглядное сравнение картинки можете увидеть на тестовом видео ниже. Все кадры в правом ролике воспроизводятся с одинаковой частотой, а слева каждый второй кадр длится заметно дольше. Некоторые увидят разницу только при замедлении видео, а некоторые смогут разглядеть эффект и в динамике.
К сожалению, работает данная фишка не всегда правильно. При просмотре динамических роликов или спортивных трансляций judder-эффект максимально заметен. Так футбольный мяч после удара превращается в комету или дыню, либо автомобиль во время ускорения резко меняет свою форму и становится смазанным. В эти моменты встроенная в телевизор система помогает добавить недостающие кадры и сделать картинку более чёткой.
Эта же система способна испортить просмотр динамических сцен в кино. Когда, по задумке режиссёра, кадр должен иметь эффект размытия или быть смазан, телевизор делает его слишком резким и появляется эффект съёмки на любительскую камеру.
Чтобы полностью избавиться от judder-эффекта, частота выходного сигнала должна быть равна или кратна показателю fps воспроизводимого видео. Только такой способ трансляции позволить избежать видимых искажений и смотреть контент в таком виде, как задумали его создатели.
Раньше киноиндустрия презирала lens flare
Сейчас lens flare считается еще довольно модным эффектом: разработчики игр, например, делятся друг с другом гайдами и лайфхаками, как лучше всего добавлять блики в игры. А в середине XX века режиссеры, наоборот, считали признаком профессионализма (и хорошего вкуса) фильмы, снятые целиком без неестественного преломления света в объективе. Этого достигали сложными манипуляциями с фокусом и техникой, а также съемкой в полностью контролируемых студийных помещениях.
Посмотрите «Гражданина Кейна» или «Звуки музыки» и обратите внимание, насколько чиста и натуральна там картинка. Это – результат огромных трудов и мастерства съемочной команды
«Гражданин Кейн»
«Звуки музыки»
Таких стандартов придерживались примерно до конца 60-х, а затем в кино случилась очередная революция.