开发者

JavaScript event for canvas resize

开发者 https://www.devze.com 2023-03-01 15:43 出处:网络
It ap开发者_开发技巧pears that altering the dimensions of a canvas clears any drawing already done on that canvas.

It ap开发者_开发技巧pears 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?


Update 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.

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.


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

 (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.


David Mulder's answer is an improvement, but it looks like it will trigger after waiting timeout milliseconds after the first resize 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 last one. The following code does that.

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.

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


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:

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!"); } );


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'] });
  1. 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.

  2. 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.


Have you tried ResizeObserver (caniuse.com) in addition to the debounce logic mentioned in the other answers like Jemenake's?

For example:

const pleasantText = 'There is a handle! Let\'s try relaxing while resizing the awesome canvas! ->';

onResize = (entries) => {
    document.getElementsByTagName('span')[0].innerHTML = 
        `${pleasantText} <b>${entries[0].target.offsetWidth}x${entries[0].target.offsetHeight}</b>`;
};

const canvas = document.getElementsByTagName('canvas')[0];
const observer = new ResizeObserver(onResize);
observer.observe(canvas);
body > div:first-of-type {
    display: grid;
    place-items: center;
    width: 100vw;
    height: 100vh;
    user-select: none;
}

body > div:first-of-type div {
    display: grid;
    min-width: 12em;
    min-height: 6em;
    width: 16rem;
    height: 6rem;
    background: #ccc;
    overflow: auto;
    place-items: center;
    text-align: center;
    resize: both;
    grid-auto-columns: 20vw 1fr;
    grid-auto-flow: column;
    grid-gap: 20px;
    padding: 0.4rem 0.8rem;
    border-radius: 4px;
}

canvas {
    width: 100%;
    height: 100%;
    min-width: 0;
    min-height: 0;
    background: #ff9;
}
<div>
    <div>
        <span></span>
        <canvas></canvas>
    </div>
</div>


save the canvas state as imageData and then redraw it after resizing

var imgDat=ctx.getImageData(0,0,ctx.canvas.width,ctx.canvas.height)

after resize

ctx.putImageData(imgDat,0,0)
0

精彩评论

暂无评论...
验证码 换一张
取 消