Реализуем визуальный эффект из фильма «Матрица»

  • Tutorial


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

На днях пересмотрел «Матрицу». Слушайте, до чего же классный фильм (это касается только первой части). В очередной раз обратил внимание на «падающие» строчки с иероглифами на ихних экранах (намеренно использовал слово «ихних» забавы ради). Задумался о том, как это можно реализовать… максимально простым способом (ибо лень). Вот что у меня получилось.

Welcome to the Matrix


Разметка выглядит так:
<body style="margin: 0; background: #000; overflow: hidden;">
    <canvas></canvas>
</body>

Инициализируем холст, контекст, ширину и высоту холста. Делаем последние равными ширине и высоте окна браузера:
const C = document.querySelector("canvas"),
  $ = C.getContext("2d"),
  W = C.width = innerWidth,
  H = C.height = innerHeight

Создаем строку с символами, которые будут использоваться для визуализации (не хочу иероглифы, хочу кириллицу!). Преобразуем данную строку в массив. Пробелы дают пустоты в колонках, с ними эффект интереснее:
const str = "А+Б0В-Г1Д=Е2Ё Ж3З И4Й К5Л М6Н О7П Р8С Т9У Ф!Х Ц?Ч Ш.ЩЪ,Ы Ь:ЭЮ;Я",
  matrix = str.split('')

Определяем размер шрифта, количество колонок и создаем пустой массив. Этот массив мы будем использовать для определения координаты y:
let font = 11,
  // количество колонок = ширина холста / размер шрифта
  col = W / font,
  arr = []

Заполняем пустой массив единицами по количеству колонок:
for(let i = 0; i < col; i++) arr[i] = 1

К рисованию все готово. Приступаем:
function draw() {
  // определяем цвет фона
  // такой цвет позволяет добиться эффекта постепенного затухания символов
  $.fillStyle = "rgba(0, 0, 0, .05)"

  // заливаем холст выбранный цветом
  $.fillRect(0, 0, W, H)

  // меняем цвет для шрифта
  $.fillStyle = "#0f0"

  // устанавливаем параметры шрифта
  $.font = font + "px system-ui"

  // рисуем символы
  for (let i = 0; i < arr.length; i++) {
    // выбираем случайный набор символов
    let txt = matrix[Math.floor(Math.random() * matrix.length)]

    // рисуем символы
    // двигаемся вправо и вниз
    // fillText(набор символов, координата x = значение i, умноженное на размер шрифта, координата y = значение arr, умноженное на размер шрифта)
    $.fillText(txt, i * font, arr[i] * font)
    
    // если "y" больше высоты холста или Math.random() выдает больше 0.975 (чем это значение меньше, тем больше будет пустот на экране), то поднимаемся наверх (обнуляем "y")
    // это позволяет обеспечить разницу отрисовки отдельных колонок
    if (arr[i] * font > H && Math.random() > 0.975) arr[i] = 0
    
    // увеличиваем значение y
    arr[i]++
    }
}

Запускаем функцию рисования через каждые 123 миллисекунды (число произвольное):
setInterval(draw, 123)

Наконец, при изменении размеров окна делаем перезагрузку страницы (ибо лень):
window.addEventListener('resize', () => location.reload())

Результат можно посмотреть здесь.

Существует небольшая проблемка: время от времени колонки слипаются, отрисовываются почти вровень по горизонтали. Это влияет на восприятие: глаз цепляется за упорядоченность. Ощущается недостаток хаоса, что ли. Достойного решения пока не найдено.

Благодарю за внимание.
AdBlock похитил этот баннер, но баннеры не зубы — отрастут

Подробнее
Реклама

Комментарии 40

    –3
      +13

      Каждый уважающий себя программист обязан написать:


      • калькулятор
      • интерпретатор брейнфака
      • скринсейвер в стиле матрицы.

      Справедливости ради, у автора поста итоговая картинка красивее, чем cmatrix.

        0
        Справедливости ради, у автора поста итоговая картинка красивее, чем cmatrix.
        Наверное, из-за отсутствия поддержки полупрозрачных шрифтов в стандартных терминалах.
          0
          Ну шрифты не полупрозрачные. Просто цвет меняется от зелёного до чёрного. В cmatrix этого не реализовано. Хотя возможность есть.
            0
            Хотя возможность есть.
            Количество цветов в терминалах обычно ограничено.
        0
        Раньше амбиции были выше и обычно замахивались сразу на написание операционной системы «операционной системы».
          0
          Значит мне осталось написать скринсейвер.
            0
            Когда-то я благодаря статье о реализации эффекта из «Матрицы» на PHP и попал на Хабр.
          +1
          «Максимально простым способом» это заполнить пространство символами, определить градиенты для столбцов и «крутить палитру». Заодно и «слипание» не будет мешать.
            +1
            Демку забыли воткнуть :)
              0
              Мне кажется в строке
              $.fillStyle = "rgba(0, 0, 0, .5)"
              нужно заменить прозрачность на .25 и сделать заполнение чуть плотнее, тогда выходит куда более похоже на оригинал.
                +1
                немного ошибся, должно быть так $.fillStyle = «rgba(0, 0, 0, .05)»
                0
                а где гифка, чтобы результат посмотреть?
                  0
                  вкладка Result
                    0
                    добавил ссылку на Codepen
                    0
                    Получилось здорово. Раньше на C# делал реализацию.
                    Одно но:
                    строчки с иероглифами на ихних экранах

                      0
                      Такие «но» лучше отправлять через ctrl+enter.
                      0

                      Ждём реализацию на x86 в 128 байт.

                        +1
                        и под DOS 3.1 на C# :)
                        –3
                        Я по названию статьи ожидал, что речь о том как произвольное видео в реальном режиме времени преобразуется в матричный код.
                        image

                        В идеале, приложение под Андроид, которое картинку с камеры смартфона преобразует в реальном времени в матричный код для виртуальной реальности.
                        image
                          0
                          Читал, что самая и притом живая старая в мире торрент-раздача — это как раз раздача фильма «Матрица», целиком прогнанного через библиотеку, преобразующую видео в такой matrix-код.
                          0
                          Перенес весь код на локальную машину — не работает.
                          В jsFiddle.net всё работает.
                          :(
                            0
                            добавил ссылку на Codepen. запустил «на локальной машине», все ок
                              0
                              Заработало.
                              Блок
                              script
                              надо было поместить ниже секции
                              body


                              Итоговая программа
                              <!DOCTYPE html>
                              <html lang="en" >
                              <head>
                                <meta charset="utf-8" />
                                <title>Matrix</title>  
                              </head>
                              <body style="margin: 0; background: #000; overflow: hidden;">
                                <canvas></canvas>  
                              </body>
                              <script>
                              const C = document.querySelector("canvas"),
                                $ = C.getContext("2d"),
                                W = (C.width = window.innerWidth),
                                H = (C.height = window.innerHeight);
                              
                              const str = "А+Б0В-Г1Д=Е2Ё Ж3З И4Й К5Л М6Н О7П Р8С Т9У Ф!Х Ц?Ч Ш.ЩЪ,Ы Ь:ЭЮ;Я",
                                matrix = str.split("");
                              
                              let font = 11,
                                col = W / font,
                                arr = [];
                              
                              for (let i = 0; i < col; i++) arr[i] = 1;
                              
                              function draw() {
                                $.fillStyle = "rgba(0,0,0,.05)";
                                $.fillRect(0, 0, W, H);
                                $.fillStyle = "#0f0";
                                $.font = font + "px system-ui";
                                for (let i = 0; i < arr.length; i++) {
                                  let txt = matrix[Math.floor(Math.random() * matrix.length)];
                                  $.fillText(txt, i * font, arr[i] * font);
                                  if (arr[i] * font > H && Math.random() > 0.975) arr[i] = 0;
                                  arr[i]++;
                                }
                              }
                              
                              setInterval(draw, 123);
                              
                              window.addEventListener("resize", () => location.reload());
                              </script>
                              </html>
                              
                              
                                0
                                либо <script src=«script.js» defer></script>, либо обернуть скрипт в window.onload = ( ) => { }, в противном случае скрипт не видит canvas
                            +1
                            Здорово и очень просто — как надо! Благодарю!
                              –1
                              пожалуйста
                              0
                              Что-то подобное недавно сделал для Flutter Clock Challenge
                              Глянуть небольшое видео можно тут bitrainclock.kroppli.com
                              Там же ссылка на гитхаб с кодами
                                0
                                Году в 2004-м, когда учился на первом курсе ССУЗа, тоже захотел такой скринсейвер, и нагуглил несколько вариантов. Вот самый на мой взгляд удачный, судя по свойствам файла — его написали ещё в 1999 году. Во времена XP она стояла у меня везде, потом как-то забил. Вот нашёл у себя на диске:
                                yadi.sk/d/4SaiFGcicgFJ1g
                                В Win7 работает, но довольно капризно, может сам собой отключиться (возможно, дело в дребезге мыши).

                                Позже на дисках Игромании мне попался .exe-установщик скринсэйвера от UselessCreation, который имел уже кучу настроек и фич: камера могла летать во все стороны и под всеми углами сквозь падающий код, настраивался цвет, скорость, размер, интенсивность падающего кода, был 3D-эффект, а самое крутое — эта заставка умела этим бегущим 3D-кодом как псевдографикой в цвете рисовать скриншоты сцен из фильмов, причём скриншот появлялся и исчезал плавно. Кому интересно — найдите эту заставку, не пожалеете, это ИМХО лучшее, что было сделано на эту тему. У себя найти сходу не могу, и не уверен, что оно запустится на Win7 и новее.
                                  +1
                                  Поисковик находит в раз, в Windows 7 SP1 работает отлично.
                                  0
                                  Вы поставили мне интересную задачу на вечер :)

                                  Я захотел улучшить Ваш код, и вот что я сделал:

                                  • Добавил чуть больше настроек матрицы через константы;
                                  • Заменил русские буквы на латинскую письменность;
                                  • Уменьшил значение альфы для цвета, заполняющего бэкграунд при каждой отрисовке (по совету комментатора выше);
                                  • Добавил шанс появления символа на экране, убрав необходимость добавления пробелов в алфавит матрицы;
                                  • Всё таки придумал решение проблемы с этим странным горизонтальным «слипанием» :)


                                  В моём решении этой проблемы я использовал функцию, которая создаёт кривую из случайных координат, по которой в канвасе и будут рисоваться буквы. Чтобы символы не всегда рисовались на координатах одной и той же кривой, я создал ещё одну константу — UPDATE_CURVE_INTERVAL. Каждый раз, когда вызывается функция отрисовки, она инкрементирует счётчик, тем самым заставляя кривую продолжать спускаться по координате Y. Если значение счётчика доходит до UPDATE_CURVE_INTERVAL, то счётчик обнуляется и создаётся новая кривая из случайных координат. Чем меньше значение этой константы, тем чаще будет создаваться новой кривая.

                                  А вот результат всех этих изменений:

                                    –1
                                    Кстати. Успел додумать следующую штуку: символы теперь рисуются, следуя за нажатым курсором.
                                    Вот реализация:



                                    P.S.: Извините за ошибки с отступами. Я трезв, просто редачил код одновременно и в блокноте, и на codepen, и на jsfiddle.
                                      +2
                                      Не думал, что кого-то так сильно зацепит. Что сказать, потрудились на славу. Однако у Вас, на мой взгляд, проблема «недостатка хаоса» остается: все колонки обновляются одновременно, это заметно. В оригинале символы начинаются с самого верха и заканчиваются в самом низу, у Вас же «потоки символов» начинаются и заканчиваются в произвольных местах. Имеет место наложение символов друг на друга. В целом, реализация кажется мне слишком сложной для такой простой задачи)
                                        +1
                                        Успел додумать следующую штуку: символы теперь рисуются, следуя за нажатым курсором.

                                        Офигенно — я аж залип на несколько минут, рисуя зелёные снежные заряды. Спасибо.
                                      +1

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

                                        0
                                        Думаю, что во избежании «слипания» нужно использовать requestAnimationFrame.

                                        codepen.io/sswebcoder/pen/WNbmKZv
                                          +1
                                          Чуть-чуть поменял код и теперь можно выводить не просто случайные символы, а слова из заданного словаря. В качестве словаря добавил ники моих друзей.
                                          codepen.io/mozgosteb/pen/qBEwEbQ
                                          Автору спасибо за исходники!
                                            0
                                            Когда то делал «хаккерский» инструмент для игр. Само собой не мог обойти стороной экран из матрицы, где любой более-менее проверенный программист увидит блондинку в красном.

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

                                            Если кому интерсно, вот ссылка: tentaculus.ru/hackerman
                                            Вход в матрицу по нажатию [Caps Lock].
                                              –1
                                              Помнится, первый символ был более светло-зелёным.

                                              Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                                              Самое читаемое