尝试使用更新的src绘制图像时,Canvas闪烁 [英] Canvas flickers when trying to draw image with updated src

查看:1978
本文介绍了尝试使用更新的src绘制图像时,Canvas闪烁的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

当画布发生变化时获取画布的图像

get image of canvas whenever there is change in canvas

var canvas = document.getElementById('myCanvas');

socket.emit('updateCanvasImage', canvas.toDataURL());

在其他地方的新画布上绘制图像

draw image on new canvas somewhere else

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

这里有很多这样的问题,但没有一个解决方案对我有用。

There are lots of questions like these here, but none of the solutions seem to work for me.

如何解决这个问题?

推荐答案

不要使用事件渲染内容



不要使用事件来重绘画布。图像内容以固定速率呈现给显示器,而大多数事件未同步到显示速率,显示速率和事件速率之间的不匹配可能导致闪烁。

Do not use events to render content

Do not use events to redraw the canvas. Image content is presented to the display at a fixed rate, while most events are not synced to the display rate, the mismatch between display rate and event rates can cause flicker.

当您反复更新任何可视内容时,无论是画布还是其他DOM内容,都应该使用 requestAnimationFrame 调用渲染函数。然后,此函数应为下一个显示帧渲染所有内容。

When you repeatedly update any visual content, be that the canvas or other DOM content, you should use requestAnimationFrame to call a render function. This function should then render all the content ready for the next display frame.

当渲染函数返回时,更改将保留在后备缓冲区中,直到显示硬件准备好显示下一帧。

When the render function returns the changes will be held in a backbuffer until the display hardware is ready to display the next frame.

因此,为了解决您的问题,请创建一个绑定到的渲染函数显示率。

Thus to fix your problem create a render function that is tied to the display rate.

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

然后在事件中获取新图像状态并在准备绘制时标记更新

Then in the events just get the new image state and flag update when ready to draw

image.onload = () => update = true;    
socket.on('updateCanvasImage', src => {update = false; image.src = src});

对拖动事件执行相同操作

Do the same with the drag events

这将确保您永远不会有任何闪烁,并且您还可以检查图像更新是否比延迟更快到达,从而降低图像更新率。

This will ensure you never have any flicker, and also you can check to see if the image updates are arriving faster than can be delayed and thus throttle back the image update rate.

有很多时候画布内容是从一个或多个不同的来源,从视频,相机,绘图命令(从鼠标)更新,触摸,代码),或从图像流。

There are many times where the canvas content is updated from one or more different sources, from a video, camera, a draw command (from mouse, touch, code), or from a stream of images.

在这些情况下,最好使用第二个画布,将其保留在屏幕外(在RAM中)并用作显示源。这使得显示画布只是一个视图,与内容无关。

In these cases it is best to use a second canvas that you keep offscreen (in RAM) and use as the source for display. This makes the display canvas just a view, that is independent of the content.

创建第二个画布;

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

然后在渲染功能中你可以显示它

Then in the render function you can display it

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

因此您的图片负载会吸引到屏幕外的画布

Thus your image load draws to the offscreen canvas

image.onload = () => background.ctx.drawImage(0, 0, background.width, background.height);    
socket.on('updateCanvasImage', src => image.src = src);

您的鼠标拖动事件只需要更新画布视图。渲染功能将使用更新的视图渲染下一帧。您还可以添加缩放和旋转。

And your mouse drag events need only update the canvas view. The render function will render the next frame using the updated view. You can also add zoom and rotation.

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

这篇关于尝试使用更新的src绘制图像时,Canvas闪烁的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆