用于画布调整大小的 JavaScript 事件
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/5825447/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
JavaScript event for canvas resize
提问by rampion
It appears that altering the dimensions of a canvas clears any drawing already done on that canvas.
似乎改变画布的尺寸会清除该画布上已经完成的任何绘图。
Is there an event that fires on canvas resize, so I can hook it and redraw when it occurs?
是否有在画布调整大小时触发的事件,以便我可以在它发生时钩住它并重新绘制?
采纳答案by Kit Sunde
You usually don't want to strictly check for a resize event because they fire a lot when you do a dynamic resize, like $(window).resize in jQuery and as far I'm aware there is no native resize event on elements (there is on window). I would check it on an interval instead:
您通常不想严格检查调整大小事件,因为当您进行动态调整大小时,它们会触发很多,例如 $(window).resize 在 jQuery 中,据我所知,元素上没有本机调整大小事件(窗户上有)。我会在一个时间间隔内检查它:
function onResize( element, callback ){
var elementHeight = element.height,
elementWidth = element.width;
setInterval(function(){
if( element.height !== elementHeight || element.width !== elementWidth ){
elementHeight = element.height;
elementWidth = element.width;
callback();
}
}, 300);
}
var element = document.getElementsByTagName("canvas")[0];
onResize( element, function(){ alert("Woo!"); } );
回答by David Mulder
Update 2020
2020 年更新
Jemenake's answer looks good, but in general I would recommend loading in a library which provides debounce
functionality, as that's what this is called.
Jemenake 的答案看起来不错,但总的来说,我建议加载一个提供debounce
功能的库,因为这就是所谓的。
For example with the lodash one, you can do window.addEventListner('resize', _.debounce(onResize, 500)
. Note that there is a third argument where you can specify the behavior you want (e.g. window.addEventListner('resize', _.debounce(onResize, 500, {leading: true, trailing true})
), although the default should be pretty good.
例如,使用 lodash one,您可以执行window.addEventListner('resize', _.debounce(onResize, 500)
. 请注意,您可以在第三个参数中指定所需的行为(例如window.addEventListner('resize', _.debounce(onResize, 500, {leading: true, trailing true})
),尽管默认值应该很好。
Kit Sunde's answer will do a lot of unnecessary work whilst the browser window is not resized. It is better to check whether the event got resized in response to a browser event and next ignore resize events for a given amount of time after which you do a check again (this will cause two checks after eachother in quick succession and the code could be improved to prevent this, but you get the idea probably).
Kit Sunde 的回答会在不调整浏览器窗口大小的情况下做很多不必要的工作。最好检查事件是否因浏览器事件而调整大小,然后在给定的时间内忽略调整大小事件,然后再次检查(这将导致快速连续两次检查,代码可能是改进以防止这种情况发生,但您可能会明白)。
(function(){
var doCheck = true;
var check = function(){
//do the check here and call some external event function or something.
};
window.addEventListener("resize",function(){
if(doCheck){
check();
doCheck = false;
setTimeout(function(){
doCheck = true;
check();
},500)
}
});
})();
Please note, the code above was typed blindly and not checked.
请注意,上面的代码是盲目输入的,没有经过检查。
回答by Jemenake
David Mulder's answer is an improvement, but it looks like it will trigger after waiting timeoutmilliseconds after the firstresize event. In other words, if more resizes happen before the timeout, it doesn't reset the timer. I was looking for the opposite; I wanted something which would wait a little bit of time to let the resize events stop, and then fire after a certain amount of time after the lastone. The following code does that.
David Mulder 的回答是一个改进,但看起来它会在第一个调整大小事件后等待超时毫秒后触发。换句话说,如果在超时之前发生更多调整大小,则不会重置计时器。我正在寻找相反的东西;我想要一些可以等待一点时间让调整大小事件停止的东西,然后在最后一个事件之后的一定时间后触发。下面的代码就是这样做的。
The ID of any currently-running timer will be in timer_id. So, whenever there's a resize, it checks to see if there's already a time running. If so, it cancels that one and starts a new one.
任何当前运行的计时器的 ID 将在 timer_id 中。因此,无论何时调整大小,它都会检查是否已经有时间在运行。如果是这样,它会取消那个并开始一个新的。
function setResizeHandler(callback, timeout) {
var timer_id = undefined;
window.addEventListener("resize", function() {
if(timer_id != undefined) {
clearTimeout(timer_id);
timer_id = undefined;
}
timer_id = setTimeout(function() {
timer_id = undefined;
callback();
}, timeout);
});
}
function callback() {
// Here's where you fire-after-resize code goes.
alert("Got called!");
}
setResizeHandler(callback, 1500);
回答by Martin Wantke
I didn't find any build-in events to detect new canvas dimensions, so I tried to find a workaround for two scenarios.
我没有找到任何内置事件来检测新的画布尺寸,因此我尝试为两种情况找到解决方法。
function onresize()
{
// handle canvas resize event here
}
var _savedWidth = canvas.clientWidth;
var _savedHeight = canvas.clientHeight;
function isResized()
{
if(_savedWidth != canvas.clientWidth || _savedHeight != canvas.clientHeight)
{
_savedWidth = canvas.clientWidth;
_savedHeight = canvas.clientHeight;
onresize();
}
}
window.addEventListener("resize", isResized);
(new MutationObserver(function(mutations)
{
if(mutations.length > 0) { isResized(); }
})).observe(canvas, { attributes : true, attributeFilter : ['style'] });
For detecting any JavaScript canvas.style changes, the MutationObserver API is good for. It doesn't detect a specific style property, but in this case it is sufficient to check if canvas.clientWidth and canvas.clientHeight has changed.
If the canvas size is declared in a style-tag with a percent unit, we have a problem to interpret the css selectors correctly (>, *, ::before, ...) by using JavaScript. I think in this case the best way is to set a window.resize handler and also check the canvas client size.
为了检测任何 JavaScript canvas.style 更改,MutationObserver API 非常适合。它不会检测特定的样式属性,但在这种情况下,检查 canvas.clientWidth 和 canvas.clientHeight 是否已更改就足够了。
如果画布大小在样式标签中以百分比为单位声明,我们在使用 JavaScript 正确解释 css 选择器(>、*、::before、...)时会遇到问题。我认为在这种情况下,最好的方法是设置 window.resize 处理程序并检查画布客户端大小。
回答by canvastag
save the canvas state as imageData and then redraw it after resizing
将画布状态保存为 imageData,然后在调整大小后重新绘制
var imgDat=ctx.getImageData(0,0,ctx.canvas.width,ctx.canvas.height)
after resize
调整大小后
ctx.putImageData(imgDat,0,0)