- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我在 canvas 元素中有一个完美运行的演示动画,我可以使用 MediaRecorder 和 captureStream()
将其录制为 webm 视频文件。来自<canvas>
元素。
来自 2d context api 的动画在生成的视频中表现得很好,但是当我尝试使用 drawImage()
时为了将图像添加到 Canvas 上,我似乎无法使其正常工作。在后一种情况下 MediaRecorder.ondataavailable
处理程序未收到有效数据,生成的视频文件是 0 字节文件。
我什至实现了一个演示,可以在其中切换是否 drawImage()
执行调用。在下面的代码中,如果 drawImage = false
视频生成没有问题,但如果 drawImage
切换为true
,它将生成一个 0 字节的文件。
为了演示,我把这个jsfiddle放在一起https://jsfiddle.net/keyboardsamurai/3tkm0dp6/16/
我在 MacOS 上的“Chrome 版本 75.0.3770.100(官方版本)(64 位)”上运行此代码 - 甚至不确定它是否应该在 Firefox 等上运行,因为 MediaRecorder API 会抛出看似不相关的错误FF。
另请参阅此处的完整代码:
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<canvas id="drawing_canvas" width="1280" height="720"></canvas>
<script>
const image = new Image();
image.src = 'https://66.media.tumblr.com/84d332cafeb1052c477c979281e5713b/tumblr_owe3l0tkCj1wxdq3zo1_1280.jpg';
window.requestAnimationFrame(animation);
const drawImage = false; // toggle to 'true' to make this example fail
const canvas = document.getElementById('drawing_canvas');
const allChunks = [];
const recorder = initMediaRecorder(canvas);
recorder.start();
setTimeout(function (e) {
console.log("Video ended");
recorder.stop();
}, 5000);
function initMediaRecorder(canvasElement) {
const stream = canvasElement.captureStream(60);
const recorder = new MediaRecorder(stream, {mimeType: 'video/webm'});
recorder.ondataavailable = function (e) {
console.log("data handler called");
if (e.data) {
console.log("data available: " + e.data.size)
if (e.data.size > 0) {
console.log("data added");
allChunks.push(e.data);
}
} else {
console.error("Data handler received no data in event: " + JSON.stringify(e))
}
};
recorder.onstop = function (e) {
const fullBlob = new Blob(allChunks);
const link = document.createElement('a');
link.style.display = 'none';
link.href = window.URL.createObjectURL(fullBlob);
link.download = 'media.webm';
document.body.appendChild(link);
link.click();
link.remove();
};
return recorder;
}
function animation() {
const now = new Date();
const ctx = document.getElementById('drawing_canvas').getContext('2d');
if (drawImage) {
ctx.drawImage(image, 0, 0);
}
ctx.clearRect(0, 0, 150, 150);
ctx.strokeStyle = 'white';
ctx.fillStyle = 'white';
ctx.rect(0, 0, 1280, 720);
ctx.stroke();
ctx.save();
ctx.translate(75, 75);
ctx.scale(0.4, 0.4);
ctx.rotate(-Math.PI / 2);
ctx.strokeStyle = 'black';
ctx.fillStyle = 'white';
ctx.lineWidth = 8;
ctx.lineCap = 'round';
// Hour marks
ctx.save();
for (var i = 0; i < 12; i++) {
ctx.beginPath();
ctx.rotate(Math.PI / 6);
ctx.moveTo(100, 0);
ctx.lineTo(120, 0);
ctx.stroke();
}
ctx.restore();
// Minute marks
ctx.save();
ctx.lineWidth = 5;
for (i = 0; i < 60; i++) {
if (i % 5 != 0) {
ctx.beginPath();
ctx.moveTo(117, 0);
ctx.lineTo(120, 0);
ctx.stroke();
}
ctx.rotate(Math.PI / 30);
}
ctx.restore();
const sec = now.getSeconds();
const min = now.getMinutes();
let hr = now.getHours();
hr = hr >= 12 ? hr - 12 : hr;
ctx.fillStyle = 'black';
// write Hours
ctx.save();
ctx.rotate(hr * (Math.PI / 6) + (Math.PI / 360) * min + (Math.PI / 21600) * sec);
ctx.lineWidth = 14;
ctx.beginPath();
ctx.moveTo(-20, 0);
ctx.lineTo(80, 0);
ctx.stroke();
ctx.restore();
// write Minutes
ctx.save();
ctx.rotate((Math.PI / 30) * min + (Math.PI / 1800) * sec);
ctx.lineWidth = 10;
ctx.beginPath();
ctx.moveTo(-28, 0);
ctx.lineTo(112, 0);
ctx.stroke();
ctx.restore();
// Write seconds
ctx.save();
ctx.rotate(sec * Math.PI / 30);
ctx.strokeStyle = '#D40000';
ctx.fillStyle = '#D40000';
ctx.lineWidth = 6;
ctx.beginPath();
ctx.moveTo(-30, 0);
ctx.lineTo(83, 0);
ctx.stroke();
ctx.beginPath();
ctx.arc(0, 0, 10, 0, Math.PI * 2, true);
ctx.fill();
ctx.beginPath();
ctx.arc(95, 0, 10, 0, Math.PI * 2, true);
ctx.stroke();
ctx.fillStyle = 'rgba(0, 0, 0, 0)';
ctx.arc(0, 0, 3, 0, Math.PI * 2, true);
ctx.fill();
ctx.restore();
ctx.beginPath();
ctx.lineWidth = 14;
ctx.strokeStyle = '#325FA2';
ctx.arc(0, 0, 142, 0, Math.PI * 2, true);
ctx.stroke();
ctx.restore();
window.requestAnimationFrame(animation);
}
</script>
</body>
</html>
更新:以上行为被确认可复制至少在以下 Chrom(e/ium) 版本上:
MacOS Mojave 10.14.5 上的版本 75.0.3770.100(官方版本)(64 位)
MacOS Mojave 10.14.5 上的版本 77.0.3849.0(官方版本)canary(64 位)
Ubuntu 19.04 Disco Dingo 上的版本 77.0.3770.100(官方版本)snap(64 位)
最佳答案
发生这种情况是因为您的图像来自跨域资源并且污染了您的 Canvas 。
污染正在捕获 MediaStream 的 Canvas 将阻止所述 MediaStream 捕获任何新图像。
此外,尝试从此类受污染的 Canvas 中捕获 MediaStream 将引发 SecurityError。
const ctx = canvas.getContext('2d');
const stream = canvas.captureStream();
vid.srcObject = stream;
const img = new Image();
img.onload = e => {
console.log('will taint the canvas')
ctx.drawImage(img, 0, 0);
// and if we try now to capture a new stream, we have a clear error
const stream2 = canvas.captureStream();
}
img.src = "https://66.media.tumblr.com/84d332cafeb1052c477c979281e5713b/tumblr_owe3l0tkCj1wxdq3zo1_1280.jpg";
ctx.fillRect(0,0,20,20);
<canvas id="canvas"></canvas>
<video id="vid" controls autoplay muted></video>
要规避它,您需要服务器以跨域兼容的方式发送图像,方法是正确设置 Access-control-origin header 以接受您自己的域,然后使用 crossorigin< 请求此图像
属性。您加载此特定图像的服务器确实允许任何人以这种跨域兼容的方式访问其数据,因此我们可以演示前端部分:
const ctx = canvas.getContext('2d');
const stream = canvas.captureStream();
vid.srcObject = stream;
const img = new Image();
img.crossOrigin = 'anonymous'; // add this to request the image as cross-origin allowed
img.onload = e => {
console.log('will not taint the canvas anymore')
ctx.drawImage(img, 0, 0);
// and if we try now to capture a new stream, we have a clear error
const stream2 = canvas.captureStream();
}
img.src = "https://66.media.tumblr.com/84d332cafeb1052c477c979281e5713b/tumblr_owe3l0tkCj1wxdq3zo1_1280.jpg";
ctx.fillRect(0,0,20,20);
<canvas id="canvas"></canvas>
<video id="vid" controls autoplay muted></video>
关于javascript - 为什么将 drawImage 与 MediaRecorder 一起使用时,canvas.captureStream 中的视频为空,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56970491/
我在网上搜索但没有找到任何合适的文章解释如何使用 javascript 使用 WCF 服务,尤其是 WebScriptEndpoint。 任何人都可以对此给出任何指导吗? 谢谢 最佳答案 这是一篇关于
我正在编写一个将运行 Linux 命令的 C 程序,例如: cat/etc/passwd | grep 列表 |剪切-c 1-5 我没有任何结果 *这里 parent 等待第一个 child (chi
所以我正在尝试处理文件上传,然后将该文件作为二进制文件存储到数据库中。在我存储它之后,我尝试在给定的 URL 上提供文件。我似乎找不到适合这里的方法。我需要使用数据库,因为我使用 Google 应用引
我正在尝试制作一个宏,将下面的公式添加到单元格中,然后将其拖到整个列中并在 H 列中复制相同的公式 我想在 F 和 H 列中输入公式的数据 Range("F1").formula = "=IF(ISE
问题类似于this one ,但我想使用 OperatorPrecedenceParser 解析带有函数应用程序的表达式在 FParsec . 这是我的 AST: type Expression =
我想通过使用 sequelize 和 node.js 将这个查询更改为代码取决于在哪里 select COUNT(gender) as genderCount from customers where
我正在使用GNU bash,版本5.0.3(1)-发行版(x86_64-pc-linux-gnu),我想知道为什么简单的赋值语句会出现语法错误: #/bin/bash var1=/tmp
这里,为什么我的代码在 IE 中不起作用。我的代码适用于所有浏览器。没有问题。但是当我在 IE 上运行我的项目时,它发现错误。 而且我的 jquery 类和 insertadjacentHTMl 也不
我正在尝试更改标签的innerHTML。我无权访问该表单,因此无法编辑 HTML。标签具有的唯一标识符是“for”属性。 这是输入和标签的结构:
我有一个页面,我可以在其中返回用户帖子,可以使用一些 jquery 代码对这些帖子进行即时评论,在发布新评论后,我在帖子下插入新评论以及删除 按钮。问题是 Delete 按钮在新插入的元素上不起作用,
我有一个大约有 20 列的“管道分隔”文件。我只想使用 sha1sum 散列第一列,它是一个数字,如帐号,并按原样返回其余列。 使用 awk 或 sed 执行此操作的最佳方法是什么? Accounti
我需要将以下内容插入到我的表中...我的用户表有五列 id、用户名、密码、名称、条目。 (我还没有提交任何东西到条目中,我稍后会使用 php 来做)但由于某种原因我不断收到这个错误:#1054 - U
所以我试图有一个输入字段,我可以在其中输入任何字符,但然后将输入的值小写,删除任何非字母数字字符,留下“。”而不是空格。 例如,如果我输入: 地球的 70% 是水,-!*#$^^ & 30% 土地 输
我正在尝试做一些我认为非常简单的事情,但出于某种原因我没有得到想要的结果?我是 javascript 的新手,但对 java 有经验,所以我相信我没有使用某种正确的规则。 这是一个获取输入值、检查选择
我想使用 angularjs 从 mysql 数据库加载数据。 这就是应用程序的工作原理;用户登录,他们的用户名存储在 cookie 中。该用户名显示在主页上 我想获取这个值并通过 angularjs
我正在使用 autoLayout,我想在 UITableViewCell 上放置一个 UIlabel,它应该始终位于单元格的右侧和右侧的中心。 这就是我想要实现的目标 所以在这里你可以看到我正在谈论的
我需要与 MySql 等效的 elasticsearch 查询。我的 sql 查询: SELECT DISTINCT t.product_id AS id FROM tbl_sup_price t
我正在实现代码以使用 JSON。 func setup() { if let flickrURL = NSURL(string: "https://api.flickr.com/
我尝试使用for循环声明变量,然后测试cols和rols是否相同。如果是,它将运行递归函数。但是,我在 javascript 中执行 do 时遇到问题。有人可以帮忙吗? 现在,在比较 col.1 和
我举了一个我正在处理的问题的简短示例。 HTML代码: 1 2 3 CSS 代码: .BB a:hover{ color: #000; } .BB > li:after {
我是一名优秀的程序员,十分优秀!