Захватывая каждый кадр HTML5-холста - PullRequest
20 голосов
/ 27 июля 2010

Эти изображения цикла палитры захватывают дух: http://www.effectgames.com/demos/canvascycle/?sound=0

Я бы хотел сделать некоторые (или все) из них фоном рабочего стола.

Я мог бы использовать анимированную версию GIF, но я понятия не имею, как получить это из холста "анимация". Есть ли еще что-нибудь, что может сделать что-то подобное (специально для этой ссылки и вообще говоря).

Ответы [ 5 ]

24 голосов
/ 18 августа 2010

У меня есть решение, но оно зависит от того, знакомы ли вы с Javascript Console в Firefox (установите плагин Firebug ), Chrome или Safari.

Если это не так, зайдите в Google или попробуйте щелкнуть правой кнопкой мыши в любом месте страницы, выберите « Проверка элемента » и найдите « Консоль »....

Что делает код:

Позволяет снимать 10 скриншотов элемента CANVAS каждую 1/10 секунды.Оба эти значения легко изменить, так как вы можете настроить количество итераций, которое вы хотите получить.В приведенном вами примере идентификатор элемента canvas равен ' mycanvas '.

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

Выполнение кода

Вставьте следующий код в Javascript Console :

var canvas = document.getElementById("mycanvas");
var shots  = [];
var grabLimit = 10;  // Number of screenshots to take
var grabRate  = 100; // Miliseconds. 500 = half a second

var count     = 0;

function showResults() {
    //console.log(shots);
    for (var i=0; i<shots.length; i++) {
      document.write('<img src="' + shots[i] + '"/>\n');
    }
}

var grabber = setInterval(function(){
  count++;

  if (count>grabLimit) {
    clearInterval(grabber);
    showResults();
  }

  var img     = canvas.toDataURL("image/png");
  shots.push(img);
}, grabRate);

и нажмите CTRL-Enter , чтобы выполнить его.

Это займет несколько секунд, поэтому наберитесь терпения.

После этого у вас должны быть все необходимые кадры (любые, может быть, больше) для создания анимированного GIF через ImageMagick, этот веб-сайт MakeAGif.com , или другое приложение.

Side Note

Если по какой-то причине вам нужно вывести как GIF из JPG вместо PNG, просто обновитепри необходимости это:

var img     = canvas.toDataURL("image/png");

для одного из них:

var img     = canvas.toDataURL("image/gif");
var img     = canvas.toDataURL("image/jpg");

Поддержка вывода как gif или jpg может небыть во всех браузерах (должно быть больше).

(БОЛЬШОЙ) ОБНОВЛЕНИЕ # 1

Во-первых, я сохраняю приведенный выше код, а не обновляю его, потому что обаподходы могут быть полезны для других.

Во-вторых, этот новый код НЕ РЕШАЕТ проблему.Это вроде как делает, но один главный недостаток.Он создает анимированный GIF ( Yipee! ), но в различных оттенках зеленого ( Boooo! ).Не знаю, как / почему, но, возможно, кто-то может взять его отсюда и посмотреть, что я пропустил.

Итак, мы идем ... применяются те же правила - скопируйте и вставьте его в консоль Javascript браузера(он отстает в Firefox, но Google Chrome работает довольно быстро ... 10 секунд или около того).

var jsf  = ["/Demos/b64.js", "LZWEncoder.js", "NeuQuant.js", "GIFEncoder.js"];
var head = document.getElementsByTagName("head")[0];

for (var i=0;i<jsf.length;i++) {
  var newJS = document.createElement('script');
  newJS.type = 'text/javascript';
  newJS.src = 'http://github.com/antimatter15/jsgif/raw/master/' + jsf[i];
  head.appendChild(newJS);
}

// This post was very helpful!
// http://antimatter15.com/wp/2010/07/javascript-to-animated-gif/

var w = setTimeout(function() { // give external JS 1 second of time to load

    console.log('Starting');

    var canvas = document.getElementById("mycanvas");
    var context = canvas.getContext('2d');
    var shots  = [];
    var grabLimit = 10;  // Number of screenshots to take
    var grabRate  = 100; // Miliseconds. 500 = half a second
    var count     = 0;

    function showResults() {
        console.log('Finishing');
        encoder.finish();
        var binary_gif = encoder.stream().getData();
        var data_url = 'data:image/gif;base64,'+encode64(binary_gif);
        document.write('<img src="' +data_url + '"/>\n');
    }

    var encoder = new GIFEncoder();
    encoder.setRepeat(0);  //0  -> loop forever, 1+ -> loop n times then stop
    encoder.setDelay(500); //go to next frame every n milliseconds
    encoder.start();

    var grabber = setInterval(function(){
      console.log('Grabbing '+count);
      count++;

      if (count>grabLimit) {
        clearInterval(grabber);
        showResults();
      }

      var imdata = context.getImageData(0,0,canvas.width,canvas.height);
      encoder.addFrame(context);

    }, grabRate);

}, 1000);

Используется некоторый полезный код, указатели и файлы JS, упомянутые в этом сообщении в блоге JavaScriptв (анимированный) GIF .Я использую некоторые файлы JS напрямую, но вы должны скопировать их локально, если вы собираетесь их часто использовать.

Вывод для меня был такой GIF: alt text

Так что это что-то,но не то, что вам нужно ...

12 голосов
/ 18 августа 2010

РЕДАКТИРОВАТЬ : наконец, используйте этот код, вот результат:

alt text

Imagick сгенерировал большое изображение, поэтому я пошел дальше и оптимизировал с помощью gimp.

Код на стороне клиента является модифицированной версией кода Майкла:

var canvas = document.getElementById("mycanvas");
var shots  = [];
var grabLimit = 30;  // Number of screenshots to take
var grabRate  = 100; // Miliseconds. 500 = half a second

var count     = 0;

function postResults() {
   console.log("START---------");
   for (var i = 0; i < shots.length; i++) {
      document.write(shots[i]+"<br />");
   }    
   console.log("END-----------");
}

var grabber = setInterval(function(){
  count++;

  if (count>grabLimit) {
    clearInterval(grabber);
    postResults();
  }

  var img     = canvas.toDataURL("image/png");
  shots.push(img.replace("data:image/png;base64,",""));
}, grabRate);

Он выведет на экран связку строк base64. Скопируйте их и сохраните в текстовом файле, а затем загрузите на свой веб-сервер. Затем запустите другой скрипт (см. Ниже), и он запишет изображение на ваш веб-сервер. Полученное изображение будет большим и, возможно, нестабильным, поэтому откройте GIMP и оптимизируйте для разницы и GIF. При сохранении заставьте его использовать одинаковую задержку для всех кадров, чтобы анимация была плавной.


Может быть, не слишком сложно использовать PHP.

  1. Захватите dataURL (строку в кодировке base64) для каждого кадра с JS и отправьте его на сервер.
  2. Декодируйте строки base64 и конвертируйте их в объекты Imagick.
  3. Добавьте эти объекты Imagick в качестве фреймов к новому объекту Imagick.
  4. Сохранение объекта Imagick в файловой системе в формате GIF.

Поскольку Майкл уже опубликовал хорошее решение JS, я добавлю код на стороне сервера, если вы хотите его автоматизировать:

<?php
$imageData = array_map('rtrim', file('data.txt', FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES)); //base 64 strings separated by newlines
$delay = 100;
$filename = 'coolmoviebro.gif';
$gif = new Imagick();

for($i = 0; $i < count($imageData); $i++) {
   $tempImg = new Imagick();
   $tempImg->readimageblob(base64_decode($imageData[$i]));
   $gif->addImage($tempImg);
}

$gif->setFormat('gif');
$gif->setImageDelay($delay);
$gif->writeImages($filename, true);

?>

Я не написал много PHP за год или два, поэтому обязательно перепроверьте все и так далее.

2 голосов
/ 19 августа 2010

Я взглянул на код, и, похоже, это возможно сделать с небольшим взломом.

Посмотрев на список объектов циклов в Palette.Cycles, вы сможете определить длину цикла каждого цикла с помощью полей cycle.high, cycle.low и cycle.rate. Тем не менее, вам потребуется отдельный алгоритм для каждого из шести возможных значений для cycle.reverse.

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

Затем подключите функцию animate в main.js к параметру tickCount и передайте его в palette.cycle вместо использования чего-либо, основанного на реальном времени. Увеличивайте количество тиков на период выборки с каждой итерацией.

Оттуда вы сможете изменить метод рендеринга класса Bitmap, добавив необходимую логику для копирования холста в файл. Кажется, есть библиотеки, которые могут управлять этим последним битом для вас. Я бы порекомендовал сохранить файлы, используя количество тиков в качестве имени файла (с достаточным количеством нулей, чтобы привести их в порядок). Объединение всех изображений в анимированный GIF-файл может быть выполнено как пакетное задание с использованием подходящего программного обеспечения.

Конечно, я на самом деле не пробовал это. Например, вы можете поставить чеки, чтобы убедиться, что вы не наткнетесь на анимацию с эпическим циклом и создадите миллионы изображений на жестком диске.

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

1 голос
/ 31 марта 2015

Попробуйте PhantomJS

Этот скрипт сохраняет 100 кадров.

var webPage = require('webpage');
var fs = require('fs');
var page = webPage.create();

var NB_FRAME = 100;
var current = 0;

page.open('http://www.effectgames.com/demos/canvascycle/?sound=0',
function(status) {
  if (status === "success") {
     var current = 0;
     var grabber = setInterval(function () {
      var frame = page.evaluate(function() {
       return document.getElementById('mycanvas').toDataURL("image/png").split(",")[1];
      });
      fs.write("./frame-" + current + ".png",atob(frame), 'wb');
  if (++current === NB_FRAME) {
     window.clearInterval(grabber);
     phantom.exit(0);
  }
   }, 1000);
}
});

Запустите его:

 phantomjs SaveCanvasFrame.js

Затем используйте ImageMagick

convert *.png animated.gif

Вот и мы: animated

1 голос
/ 18 августа 2010

К сожалению, по словам создателя искусства , преобразовать его в анимацию GIF не представляется возможным из-за разных частей изображения, имеющих разные циклы.

...