@SEOVirus

Как автоматизировать процесс записи видео (ScreenCapture) для SVG-анимации?

Необходимо автоматизировать процесс записи видео (ScreenCapture) для элемента SVG (анимируется через D3.js), в котором происходит анимация. Но как автоматизировать - понятия не имею. Только в лоб запустить стороннюю записывающую прогу и потом обрезать как по времени, так и вырезать нужный участок.

Примерные мысли: может переводить SVG -> Canvas 60 раз в секунду. Это странно, наверное... справится ли вообще svg->canvas со скоростью 60фпс? Далее каждый кадр отправлять в скрипт по сборке кадров в WebM-видео. И всё это на скорости 60 кадров в секунду. Мне думается притормаживать может начать. А как иначе?

Окружение: Linux.
  • Вопрос задан
  • 79 просмотров
Пригласить эксперта
Ответы на вопрос 2
deepblack
@deepblack
Может это подойдет?
Converting an SVG animation to a video with the MediaRecorder API and a hidden canvas.

https://gist.github.com/veltman/ff864215009174bc5d...
Ответ написан
Комментировать
sergiks
@sergiks Куратор тега JavaScript
♬♬
Вот вариант SVG animation to video:

использовать Media Recorder API и скрытый элемент canvas

Поскольку отрисовка кадров из элементов img может подтормаживать, этот код заранее генерит все кадры и после рендерит в цикле их через requestAnimationFrame()

index.html
<!DOCTYPE html>
<meta charset="utf-8">
<body>
<svg width="960" height="500"></svg>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.2.3/d3.min.js"></script>
<script src="https://d3js.org/topojson.v2.min.js"></script>
<script>
var svg = d3.select("svg"),
    canvas = document.createElement("canvas"),
    width = canvas.width = +svg.attr("width"),
    height = canvas.height = +svg.attr("height"),
    context = canvas.getContext("2d");

var projection = d3.geoOrthographic()
  .scale(195)
  .translate([width / 2, height / 2])
  .precision(0.1);

var path = d3.geoPath().projection(projection);

d3.json("/mbostock/raw/4090846/world-110m.json", function(err, world) {
  var data = [],
    stream = canvas.captureStream(),
    recorder = new MediaRecorder(stream, { mimeType: "video/webm" });

  recorder.ondataavailable = function(event) {
    if (event.data && event.data.size) {
      data.push(event.data);
    }
  };

  recorder.onstop = () => {
    var url = URL.createObjectURL(new Blob(data, { type: "video/webm" }));
    d3.selectAll("canvas, svg").remove();
    d3.select("body")
      .append("video")
      .attr("src", url)
      .attr("controls", true)
      .attr("autoplay", true);
  };

  var background = svg.append("rect")
    .attr("width", width)
    .attr("height", height)
    .attr("fill", "#fff");

  svg.append("path")
    .datum({ type: "Sphere" })
    .attr("stroke", "#222")
    .attr("fill", "none");

  svg.append("path")
    .datum(topojson.feature(world, world.objects.land))
    .attr("fill", "#222")
    .attr("stroke", "none");

  svg.append("path")
    .datum(topojson.mesh(world, world.objects.countries, function(a, b) {
      return a !== b;
    }))
    .attr("fill", "none")
    .attr("stroke", "#fff");

  var queue = d3.queue(1);

  d3.range(120).forEach(function(frame){
    queue.defer(drawFrame, frame / 120);
  });

  queue.awaitAll(function(err, frames){
    recorder.start();
    drawFrame();

    function drawFrame() {
      if (frames.length) {
        context.drawImage(frames.shift(), 0, 0, width, height);
        requestAnimationFrame(drawFrame);
      } else {
        recorder.stop();
      }
    }
  });

  function drawFrame(t, cb) {
    projection.rotate([360 * t]);
    svg.selectAll("path").attr("d", path);

    var img = new Image(),
        serialized = new XMLSerializer().serializeToString(svg.node()),
        url = URL.createObjectURL(new Blob([serialized], {type: "image/svg+xml"}));

    img.onload = function(){
      cb(null, img);
    };

    img.src = url;

  }
});
</script>
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы