【问题标题】:images generated to canvas from svg, animation result, looks slow从 svg 生成到画布的图像,动画结果,看起来很慢
【发布时间】:2020-10-07 22:58:50
【问题描述】:

使用此代码,我设法制作了一个视频。我不是这方面的专家,但我认为我的问题是因为我不知道应该使用多少帧。例如我希望我的视频持续 15 秒(我不知道如何设置它,我不知道我应该指示多少帧)

    var queue = d3.queue(1);

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

    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();
            }
        }
    });

如何改进我的代码以获得流畅的视频?

const progressbar = document.getElementById('progressbar');

d3.select("#visualization").append('svg');
const vis = d3.select("svg").attr("width", 800).attr("height", 150).attr("xmlns", "http://www.w3.org/2000/svg").style("border", "1px solid red").style("fill", "white").style("background", "white");

const rectangle = vis.append("rect")
const circle = vis.append("circle");

let second = 0;
let interval;

const rectValues = {
  "delay": 0000,
  "duration": 5000,
  "begin": {
    "x": 0,
    "y": 0,
    "height": 70,
    "width": 100,
    "opacity": 1,
    "fill": "red"
  },
  "destiny": {
    "x": 250,
    "y": 1,
    "height": 100,
    "width": 120,
    "opacity": 0.8,
    "fill": "green"
  }
};

const circleValues = {
  "delay": 4000,
  "duration": 3000,
  "begin": {
    "cx": 250,
    "r": 20,
    "fill": "blue"
  },
  "destiny": {
    "cx": 0,
    "fill": "orange"
  }
}

function startAnimation() {
  //rectangle properties
  startShapeAnimation(rectangle, rectValues);
  //circle properties  
  startShapeAnimation(circle, circleValues);
}

function startShapeAnimation(shape, shapeValues) {
  shape.attrs(shapeValues.begin)
    .transition()
    .duration(0)
    .attrs(shapeValues.begin)
    .transition()
    .delay(shapeValues.delay)
    .duration(shapeValues.duration)
    .ease(d3.easeLinear)
    .attrs(shapeValues.destiny);
}

startAnimation(0);

/***** CREATION OF VIDEO *******/
var canvas = document.createElement("canvas"),
  width = 800,
  height = 150,
  context = canvas.getContext("2d");


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 queue = d3.queue(1);

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

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) {
  var img = new Image(),
    serialized = new XMLSerializer().serializeToString(vis.node()),
    url = URL.createObjectURL(new Blob([serialized], {
      type: "image/svg+xml"
    }));

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

  img.src = url;

}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.2.3/d3.js"></script>
<script src="https://d3js.org/d3-selection-multi.v1.min.js"></script>
<div id="visualization"></div>
<div id="contenedor_canvas"></div>

【问题讨论】:

  • 你的意思是如何决定帧数?那将是 $framesPerSecond * $seconds

标签: javascript d3.js


【解决方案1】:

不要将自己限制在 240 帧,而是尝试使用requestAnimationFrame 获得尽可能多的帧。以this answer 为灵感,我能够生成更流畅的视频。

const progressbar = document.getElementById('progressbar');

d3.select("#visualization").append('svg');
const vis = d3.select("svg").attr("width", 800).attr("height", 150).attr("xmlns", "http://www.w3.org/2000/svg").style("border", "1px solid red").style("fill", "white").style("background", "white");

const rectangle = vis.append("rect")
const circle = vis.append("circle");

let second = 0;
let interval;

const rectValues = {
  "delay": 0000,
  "duration": 5000,
  "begin": {
    "x": 0,
    "y": 0,
    "height": 70,
    "width": 100,
    "opacity": 1,
    "fill": "red"
  },
  "destiny": {
    "x": 250,
    "y": 1,
    "height": 100,
    "width": 120,
    "opacity": 0.8,
    "fill": "green"
  }
};

const circleValues = {
  "delay": 4000,
  "duration": 3000,
  "begin": {
    "cx": 250,
    "r": 20,
    "fill": "blue"
  },
  "destiny": {
    "cx": 0,
    "fill": "orange"
  }
}

function startAnimation() {
  //rectangle properties
  startShapeAnimation(rectangle, rectValues);
  //circle properties  
  startShapeAnimation(circle, circleValues);
}

function startShapeAnimation(shape, shapeValues) {
  shape.attrs(shapeValues.begin)
    .transition()
    .duration(0)
    .attrs(shapeValues.begin)
    .transition()
    .delay(shapeValues.delay)
    .duration(shapeValues.duration)
    .ease(d3.easeLinear)
    .attrs(shapeValues.destiny);
}

startAnimation(0);

/***** CREATION OF VIDEO *******/
var canvas = document.createElement("canvas"),
  width = 800,
  height = 150,
  context = canvas.getContext("2d");

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 lengthInSeconds = 15;
var lengthInMS = lengthInSeconds * 1000;
var startTime = Date.now();

startRecording();
drawFrame();

function startRecording() {
  recorder.start();
  drawFrame();
}

function drawFrame() {
  var elapsedTime = Date.now() - startTime;
  if (elapsedTime > lengthInMS) {
    stopRecording();
    return;
  }

  console.log(((elapsedTime / lengthInMS) * 100).toFixed(2) + '%');
  requestAnimationFrame(drawFrame);
  var img = new Image(),
    serialized = new XMLSerializer().serializeToString(vis.node()),
    url = URL.createObjectURL(new Blob([serialized], {
      type: "image/svg+xml"
    }));

  img.onload = function() {
    context.drawImage(img, 0, 0, width, height);
  };

  img.src = url;
}

function stopRecording() {
  recorder.stop();
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.2.3/d3.js"></script>
<script src="https://d3js.org/d3-selection-multi.v1.min.js"></script>
<div id="visualization"></div>
<div id="contenedor_canvas"></div>

【讨论】:

  • 哇哦,它有效!但是,你知道我为什么会收到这个错误吗?渲染视频时发生:i.imgur.com/UOipr5G.jpg。借你的知识我想问你一件事,如果我从手机上运行这段代码,性能会一样吗?我打算做一个45秒的动画。我也想知道是否可以添加音频
  • 我没有制作视频的经验,所以我不知道音频是否可以工作。在新手机上,我认为性能应该相似。您收到错误是因为,由于某种原因,stopRecording() 被调用了两次,而您无法停止已经停止的录制。您可以对其进行调查或仅停止录制if(recording.state !== 'inactive')
猜你喜欢
  • 2019-09-03
  • 2014-03-27
  • 1970-01-01
  • 1970-01-01
  • 2016-10-25
  • 1970-01-01
  • 1970-01-01
  • 2017-10-20
  • 2018-06-14
相关资源
最近更新 更多