Is there a way to clear an element from a canvas without eliminating the others?

雨燕双飞 提交于 2021-02-05 11:27:25

问题


I' m building a page loader with canvas, and using the es6 classes... ALthough at the moment I can't make it work properly.. one of the reason is that I can't find a way to clear the canvas in progression.

This is my code so far :


class Loader {

 constructor (width, height) {
    this.width = width
    this.height = height
}


_init() {
    this._createCanvas()
    this._bindEvents()
}
// create canvas
_createCanvas () {
    this.canvas = document.createElement("canvas")
    document.body.appendChild(this.canvas)
    this.canvas.id = "canvas"
    this.canvas.width = loader.width
    this.canvas.height = loader.height
}

_throttle (callback, delay) {
    let last
    let timer
    return function () {
        let context = this
        let now = +new Date()
        let args = arguments
        if (last && now < last + delay) {
            clearTimeout(timer)
            timer = setTimeout(function () {
                last = now
                callback.apply(context, args)
            }, delay)
        } else {
            last = now
            callback.apply(context, args)
        }
    }
}

// resize canvas
_resizeCanvas () {
    // resize canvas
    var canvasRatio = this.canvas.height / this.canvas.width;
    var windowRatio = window.innerHeight / window.innerWidth;
    var width;
    var height;

    if (windowRatio < canvasRatio) {
        height = window.innerHeight;
        width = height / canvasRatio;
    } else {
        width = window.innerWidth;
        height = width * canvasRatio;
    }

    this.canvas.width = width
    this.canvas.height = height
}

_bindEvents () {
// create events listeners
    this.resizeCanvas = this._throttle(function (event) {
        this._resizeCanvas()
    }.bind(this), 250)

    window.addEventListener('resize', this.resizeCanvas, false)
}

_unbindEvents () {
// remove events listeners
    window.removeEventListener('resize', this.resizeCanvas, false)
}


}



 class Snake {
   constructor( options = {} ) {
    this.options = {
        x: options.x,
        y: options.y,
        height: options.height,
        width: options.width,
        isMoving: options.isMoving || false,
        hasDispatched: options.hasDispatched || false,
        nextSnakeCallback: options.nextSnakeCallback || null,
        speed: options.speed || 4
    }
}

_init() {
    this._drawSnake()
}
start () {
    this.options.isMoving = true;
}

reset () {
    this.options.hasDispatched = false;
}

setNextSnakeCallback (callback) {
    this.options.nextSnakeCallback = callback;
}

_drawSnake() {
    this.canvas = document.getElementById("canvas")
    this.ctx = this.canvas.getContext("2d")
    this.ctx.beginPath()
    this.ctx.rect(
        this.options.x,
        this.options.y,
        this.options.width,
        this.options.height)
    this.ctx.fillStyle = "#f44242"
    this.ctx.fill()


}

_clearSnake () {
    this.ctx.clearRect(this.options.x, this.options.y, this.options.width, this.options.height);

}

}

 class SnakeTop extends Snake {

   constructor (options) {
    super(options)
  }

_init () {
    super._drawSnake()
    this._moveSnakeToRight()
    super._clearSnake()
}

reset () {
    this.options.x = this.options.height;
}

_moveSnakeToRight () {
    if(this.options.isMoving){

        this._clearSnake()
        this._drawSnake()

        if(this.options.x > loader.width - this.options.width && !this.options.hasDispatched){
            this.options.hasDispatched = true;
            if(this.options.nextSnakeCallback) {
                this.setNextSnakeCallback()
            }
        } else if(this.options.x >= loader.width){
            this.options.isMoving = false;
        }

        this.options.x += this.options.speed


    }

    window.requestAnimationFrame(this._moveSnakeToRight.bind(this));
   }

  }


class SnakeRight extends Snake {

   constructor (options = {}) {
       super(options)
   }

_init() {
    super._drawSnake()
    this._moveSnakeDown()
    super._clearSnake()
}

_moveSnakeDown () {
    if(this.options.isMoving) {
        this._clearSnake()
        this._drawSnake()

        if(this.options.y > loader.height - this.options.height && !this.options.hasDispatched){
            this.options.hasDispatched = true;
            if(this.options.nextSnakeCallback) {
                this.setNextSnakeCallback()
            }

        } else if (this.options.y > loader.height) {
            this.options.isMoving = false
        }
            this.options.y += this.options.speed
    }
    window.requestAnimationFrame(this._moveSnakeDown.bind(this));

     }
  }


class SnakeBottom extends Snake {

    constructor (options = {} ) {
      super(options)
}
_init() {
    super._drawSnake()
    this._moveSnakeToLeft()
    super._clearSnake()
}

_moveSnakeToLeft () {
    if (this.options.isMoving) {

        this._clearSnake()
        this._drawSnake()

        if(this.options.x < 0 && !this.options.hasDispatched){
            this.options.hasDispatched = true
            if(this.options.nextSnakeCallback) {
                this.setNextSnakeCallback()
            }
        } else if (this.options.x < this.options.width) {
            this.options.isMoving = false
        }
        this.options.x -= this.options.speed

    }

        window.requestAnimationFrame(this._moveSnakeToLeft.bind(this));
     }

  }


  class SnakeLeft extends Snake {
     constructor(options = {}) {
        super(options)
  }

_init() {
    super._drawSnake()
    this._moveSnakeUp()
    super._clearSnake()
}

_moveSnakeUp () {
    if(this.options.isMoving) {

        this._clearSnake()
        this._drawSnake()

        if(this.options.y < 0 && !this.options.hasDispatched) {
            this.options.hasDispatched = true
            if(this.options.nextSnakeCallback) {
                this.setNextSnakeCallback()
            }

        } else if ( this.options.y >  - this.canvas.height) {
            this.options.isMoving = false
        }
        this.options.y -= this.options.speed
    }
        window.requestAnimationFrame(this._moveSnakeUp.bind(this));
     }
  }




 // defining the elements on the DOM

   let loader = new Loader (600, 600)
    loader._init()

 //CREATE SNAKES
    let snakeT = new SnakeTop ({
     x: - 300,
     y: 0,
     height: 20,
     width: 300
    })
  snakeT._init()


 //ASSIGN NEXT SNAKE callback
   snakeT.setNextSnakeCallback (()=>{
    snakeR.reset();
    snakeR.start();
   });

  //START FIRST SNAKE
   snakeT.start();

and so on building the other 3 elements.

As you can see from the jsfiddle, my function drawSnake() keeps drawning a rectangle after the other

     https://jsfiddle.net/ombqfzjq/

During my research I found that to clear a rectangle, instead of using

ctx.clearRect(0,0,canvasWidth,canvasHeight);

that cleans the canvas, I can use

ctx.clearRect(square.x,square.y,square.w,square.h);

that should clean instead each single rectangle... So in my class Snake I built the _clearSnake() function, and then call it to my subclasses when needed it.

What's wrong in my logic? How can I improve my code?


回答1:


Once you've painted something onto a canvas you cannot simply remove that object.

Canvas' aren't layered like HTML elements, so removing an object wouldn't show the objects beneath.

The cleanest way would be to re-paint the state before.



来源:https://stackoverflow.com/questions/40016705/is-there-a-way-to-clear-an-element-from-a-canvas-without-eliminating-the-others

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!