在画布发生变化时获取画布的图像
var canvas = document.getElementById('myCanvas');
socket.emit('updateCanvasImage', canvas.toDataURL());
在其他地方的新画布上绘制图像
var canvas = document.getElementById('myCanvasImg');
var context = canvas.getContext('2d');
var image = new Image();
image.onload = function() {
context.drawImage(this, 0, 0, canvas.width, canvas.height);
};
socket.on('updateCanvasImage', function (img) {
image.src = img;
});
当套接字更改image.src
这里有很多这样的问题,但没有一个解决方案对我有用。
如何解决这个问题?
答案 0 :(得分:0)
不要使用事件重绘画布。图像内容以固定速率呈现给显示器,而大多数事件未同步到显示速率,显示速率和事件速率之间的不匹配可能导致闪烁。
requestAnimationFrame
当您反复更新任何可视内容时,无论是画布还是其他DOM内容,都应使用requestAnimationFrame
来调用渲染功能。然后,此功能应该为下一个显示帧准备好所有内容。
当渲染函数返回时,更改将保留在后备缓冲区中,直到显示硬件准备好显示下一帧。
因此,为了解决您的问题,请创建一个与显示速率相关联的渲染功能。
var image = new Image();
var update = true; // if true redraw
function renderFunction(){
if(update){ // only raw if needed
update = false;
context.drawImage(image, 0, 0, canvas.width, canvas.height);
}
requestAnimationFrame(renderFunction);
}
requestAnimationFrame(renderFunction);
然后在事件中,只需获取新图像状态并在准备绘制时标记更新
image.onload = () => update = true;
socket.on('updateCanvasImage', src => {update = false; image.src = src});
对拖动事件执行相同操作
这将确保您永远不会有任何闪烁,并且您还可以检查图像更新是否比延迟更快到达,从而降低图像更新速率。
有很多时候,从一个或多个不同的来源,视频,相机,绘图命令(来自鼠标,触摸,代码)或图像流更新画布内容。
在这些情况下,最好使用第二个画布,将其保留在屏幕外(在RAM中)并用作显示源。这使得显示画布只是一个视图,与内容无关。
创建第二个画布;
function createCanvas(width, height){
const myOffScreenCanvas = document.createElement("canvas");
myOffScreenCanvas.width = width;
myOffScreenCanvas.height = height;
// attach the context to the canvas for easy access and to reduce complexity.
myOffScreenCanvas.ctx = myOffScreenCanvas.getContext("2d");
return myOffScreenCanvas;
}
然后在渲染功能中你可以显示它
var background = createCanvas(1024,1024);
var scale = 1; // the current scale
var origin = {x : 0, y : 0}; // the current origin
function renderFunction(){
// set default transform
ctx.setTransform(1,0,0,1,0,0);
// clear
ctx.clearRect(0,0,canvas.width,canvas.height);
// set the current view
ctx.setTransform(scale,0,0,scale,origin.x,origin.y);
// draw the offscreen canvas
ctx.drawImage(background, 0, 0);
requestAnimationFrame(renderFunction);
}
requestAnimationFrame(renderFunction);
因此,您的图像加载会吸引到屏幕外的画布
image.onload = () => background.ctx.drawImage(0, 0, background.width, background.height);
socket.on('updateCanvasImage', src => image.src = src);
您的鼠标拖动事件只需要更新画布视图。渲染功能将使用更新的视图渲染下一帧。您还可以添加缩放和旋转。
const mouse = {x : 0, y : 0, oldX : 0, oldY : 0, button : false}
function mouseEvents(e){
mouse.oldX = mouse.x;
mouse.oldY = mouse.y;
mouse.x = e.pageX;
mouse.y = e.pageY;
mouse.button = e.type === "mousedown" ? true : e.type === "mouseup" ? false : mouse.button;
if(mouse.button){
origin.x += mouse.x - mouse.oldX;
origin.y += mouse.y - mouse.oldY;
}
}
["down","up","move"].forEach(name => document.addEventListener("mouse" + name, mouseEvents));
答案 1 :(得分:0)
每当你更改HTMLImageElement的src时,它的内容都会被清除,当画布尝试渲染它时,它不能。
因此,您将体验没有任何图像的帧(闪烁),直到新设置的媒体被加载并解析(fiddle reproducing the issue)。
如果没有看到您的代码,很难为您提供正确的解决方案,但简单的结构可能是:
通过这种简单的结构,您可以避免闪烁。
var ctx = canvas.getContext('2d');
function animLoop(time){ // draws continously
ctx.clearRect(0,0,canvas.width,canvas.height);
ctx.drawImage(current, 0,0, canvas.width, canvas.height);
ctx.fillText(time, 20,20);
requestAnimationFrame(animLoop);
}
var current = new Image();
function loadImage(){
var img = new Image(); // if you really want to optimize your code for memory impact, you could declare it only once out of the function...
img.onload = function(){
current = this; // update the image to be rendered with the new & loaded one
setTimeout(loadImage, 2000); // start loading a new one in 2 sec (will be rendered even later)
}
img.onerror = loadImage;
img.src = 'https://upload.wikimedia.org/wikipedia/commons' + urls[++url_index % urls.length]+'?'+Math.random();
}
var url_index = 0;
var urls = [
//Martin Falbisoner [CC BY-SA 4.0 (http://creativecommons.org/licenses/by-sa/4.0)], via Wikimedia Commons
'/2/2d/Okayama_Castle%2C_November_2016_-02.jpg',
//Diego Delso [CC BY-SA 4.0 (http://creativecommons.org/licenses/by-sa/4.0)], via Wikimedia Commons
'/9/9b/Gran_Mezquita_de_Isfah%C3%A1n%2C_Isfah%C3%A1n%2C_Ir%C3%A1n%2C_2016-09-20%2C_DD_34-36_HDR.jpg',
//Dietmar Rabich / Wikimedia Commons / “Münster, LVM, Skulptur -Körper und Seele- -- 2016 -- 5920-6” / CC BY-SA 4.0, via Wikimedia Commons
'/5/53/M%C3%BCnster%2C_LVM%2C_Skulptur_-K%C3%B6rper_und_Seele-_--_2016_--_5920-6.jpg',
//By Charlesjsharp (Own work, from Sharp Photography, sharpphotography) [CC BY-SA 4.0 (http://creativecommons.org/licenses/by-sa/4.0)], via Wikimedia Commons
'/4/4b/Campo_flicker_(Colaptes_campestris)_female.JPG'
];
loadImage();
animLoop();
<canvas id="canvas" width="500" height="500"></canvas>
修改强>
这仅适用于chrome ,Firefox不会表现得那样,实际上只在我们调用drawImage
时才开始解析图像。这将在此期间保持画布的绘图。如果这是一个问题,您可以尝试使用ImageBitmap对象降低此值,但是使用我在演示中使用的大图像时,此停止仍然存在...
var ctx = canvas.getContext('2d');
function animLoop(time){ // draws continously
ctx.clearRect(0,0,canvas.width,canvas.height);
ctx.drawImage(current, 0,0, canvas.width, canvas.height);
ctx.fillText(time, 20,20);
requestAnimationFrame(animLoop);
}
var current = new Image();
function loadImage(){
var img = new Image();
img.crossOrigin = 'anonymous';
img.onload = function(){
createImageBitmap(this, 0,0,this.width, this.height).then(function(bmp){
current = bmp; // update the image to be rendered with an ImageBitmap
}).catch(e=>console.log(e))
setTimeout(loadImage, 2000); // start loading a new one in 2 sec (will be rendered even later)
}
img.onerror = loadImage;
img.src = 'https://upload.wikimedia.org/wikipedia/commons' + urls[++url_index % urls.length]+'?'+Math.random();
}
var url_index = 0;
var urls = [
//Martin Falbisoner [CC BY-SA 4.0 (http://creativecommons.org/licenses/by-sa/4.0)], via Wikimedia Commons
'/2/2d/Okayama_Castle%2C_November_2016_-02.jpg',
//Diego Delso [CC BY-SA 4.0 (http://creativecommons.org/licenses/by-sa/4.0)], via Wikimedia Commons
'/9/9b/Gran_Mezquita_de_Isfah%C3%A1n%2C_Isfah%C3%A1n%2C_Ir%C3%A1n%2C_2016-09-20%2C_DD_34-36_HDR.jpg',
//Dietmar Rabich / Wikimedia Commons / “Münster, LVM, Skulptur -Körper und Seele- -- 2016 -- 5920-6” / CC BY-SA 4.0, via Wikimedia Commons
'/5/53/M%C3%BCnster%2C_LVM%2C_Skulptur_-K%C3%B6rper_und_Seele-_--_2016_--_5920-6.jpg',
//By Charlesjsharp (Own work, from Sharp Photography, sharpphotography) [CC BY-SA 4.0 (http://creativecommons.org/licenses/by-sa/4.0)], via Wikimedia Commons
'/4/4b/Campo_flicker_(Colaptes_campestris)_female.JPG'
];
loadImage();
animLoop();
<canvas id="canvas" width="500" height="500"></canvas>
<强>重新编辑:强>
由于您所做的是屏幕共享,因此您可能还需要考虑WebRTC以及canvas.captureStream
而不是发送静止图像。