Canvas анимация чрезмерно нагружает ноутбук

164
30 октября 2019, 02:20

Есть паттерн как задний фон, который медленно отклоняется при движении мыши. Чтобы не рисовать каждый раз паттерн я сделал некий буфер, в котором его храню. Выглядит так: первый холст находится в DOM-дереве, на нём и будет анимация, второй холст находится в памяти и содержит в себе паттерн. При помощи drawImage() я отклоняю задний фон.

Возможно проблема заключается в моём подходе к реализации самой анимации, т.к. каждый раз при движении мыши я создаю setInterval с таймингом 1000/60 и удаляю предыдущий. Пример кода прикладываю.

// Скрипт заточен под 45 градусов 
function timing(x1, x2) { 
  return Math.sqrt(Math.abs(x1 - x2)) / 2; 
} 
function draw() { 
  // Background 
  // bufferContext.fillStyle = "rgba(13,10,21,1)"; 
  // bufferContext.fillRect(0, 0, w, h); 
  // End Background 
 
  // Lines 
  bufferContext.strokeStyle = "rgba(20,17,27,1)"; 
  bufferContext.lineWidth = 10; 
  bufferContext.lineCap = "square"; 
  for ( 
    lineNumber = -numberOfLinesY; 
    lineNumber < totalNumberOfLines; 
    lineNumber++ 
  ) { 
    let currentX = 0, 
      currentY = lineNumber * spaceBetweenLines; 
    bufferContext.moveTo(currentX, currentY); 
    for (pieceNumber = 0; pieceNumber < pieces; pieceNumber++) { 
      currentX += pieceLengthX; 
      currentY += pieceLengthY; 
      bufferContext.lineTo(currentX, currentY); 
      let random = Math.random() > 0.3; 
      if (random) { 
        bufferContext.lineTo(currentX - pieceLengthX, currentY + pieceLengthY); 
        bufferContext.moveTo(currentX, currentY); 
      } 
    } 
  } 
  bufferContext.stroke(); 
  bufferContext.closePath(); 
  // End Lines 
} 
function handleDrawInterval() { 
  if (lastClientX < clientX) { 
    lastClientX += timing(lastClientX, clientX); 
  } else if (lastClientX > clientX) { 
    lastClientX -= timing(lastClientX, clientX); 
  } 
  context.clearRect(0, 0, w, h); 
  context.drawImage(bufferCanvas, -250 + lastClientX, 0); 
 
  if (Math.abs(lastClientX - clientX) < 0.1 || lastClientX == clientX) { 
    clearInterval(drawInterval); 
  } 
} 
function handleMouseMove(e) { 
  clientX = e.clientX / 20; 
  clearInterval(window.drawInerval); 
  const drawInterval = setInterval(handleDrawInterval, 1000 / 60); 
  window.drawInterval = drawInterval; 
} 
 
const canvas = document.querySelector("canvas.background"), 
  w = (canvas.width = window.innerWidth + 1000), 
  h = (canvas.height = document.body.clientHeight +1000), 
  context = canvas.getContext("2d"), 
  margin = 250, 
  bufferCanvas = document.createElement("canvas"), 
  bufferW = (bufferCanvas.width = w + margin * 2), 
  bufferH = (bufferCanvas.height = h + margin * 2), 
  bufferContext = bufferCanvas.getContext("2d"); 
 
const lineLength = bufferW * Math.sqrt(2), 
  pieces = 20, 
  pieceLength = lineLength / pieces, 
  spaceBetweenLines = pieceLength * Math.sqrt(2), 
  pieceLengthX = spaceBetweenLines / 2, 
  pieceLengthY = pieceLengthX, 
  numberOfLinesX = bufferW / spaceBetweenLines, 
  numberOfLinesY = bufferH / spaceBetweenLines, 
  totalNumberOfLines = numberOfLinesX + numberOfLinesY, 
  startX = 0, 
  startY = -bufferW; 
 
draw(); 
context.drawImage(bufferCanvas, -250, 0); 
 
let clientX = 0, 
  lastClientX = 0; 
window.addEventListener("mousemove", handleMouseMove);
<!DOCTYPE html> 
<html lang="en"> 
  <head> 
    <meta charset="UTF-8" /> 
  </head> 
  <body> 
    <canvas class="background"></canvas> 
  </body> 
</html>

Upd. Добавил прорисовку только области окна, а не всего холста, поменял setInterval на requestAnimationFrame, нагрузка сильно снизилась.

class Pattern { 
  constructor(DOMCanvas, bufferCanvas) { 
    // DOM canvas 
    this.DOMCanvas = DOMCanvas; 
    this.DOMContext = this.DOMCanvas.getContext("2d"); 
    this.DOMWidth = this.DOMCanvas.width = window.innerWidth; 
    this.DOMHeight = this.DOMCanvas.height = window.innerWidth; 
    // Buffer Canvas 
    this.bufferCanvas = bufferCanvas; 
    this.bufferWidth = this.bufferCanvas.width = this.DOMWidth + 500; 
    this.bufferHeigth = this.bufferCanvas.height = 
      document.body.clientHeight + 500; 
    this.bufferContext = this.bufferCanvas.getContext("2d"); 
    // Options 
    this.lineLength = this.bufferWidth * Math.sqrt(2); 
    this.pieces = 20; 
    this.pieceLength = this.lineLength / this.pieces; 
    this.spaceBetweenLines = this.pieceLength * Math.sqrt(2); 
    this.pieceLengthX = this.spaceBetweenLines / 2; 
    this.pieceLengthY = this.pieceLengthX; 
    this.numberOfLinesX = this.bufferWidth / this.spaceBetweenLines; 
    this.numberOfLinesY = this.bufferHeigth / this.spaceBetweenLines; 
    this.totalNumberOfLines = this.numberOfLinesX + this.numberOfLinesY; 
    this.startX = 0; 
    this.startY = -this.bufferWidth; 
    // 
    this.clientX = 0; 
    this.currentX = 0; 
    this.penetrate = true; 
    this.pageY = window.pageYOffset; 
  } 
 
  renderBuffer() { 
    // console.log(this.bufferCanvas.width); 
    // Background 
    this.bufferContext.fillStyle = "rgba(13,10,21,1)"; 
    this.bufferContext.fillRect(0, 0, this.bufferWidth, this.bufferHeigth); 
    // End Background 
 
    // Lines 
    // this.bufferContext.strokeStyle = "rgba(20,17,27,1)"; 
    this.bufferContext.strokeStyle = "rgba(255,255,255,1)"; 
    this.bufferContext.lineWidth = 1; 
    this.bufferContext.lineCap = "square"; 
    for ( 
      let lineNumber = -this.numberOfLinesY; 
      lineNumber < this.totalNumberOfLines; 
      lineNumber++ 
    ) { 
      let currentX = 0, 
        currentY = lineNumber * this.spaceBetweenLines; 
      this.bufferContext.moveTo(currentX, currentY); 
      for (let pieceNumber = 0; pieceNumber < this.pieces; pieceNumber++) { 
        currentX += this.pieceLengthX; 
        currentY += this.pieceLengthY; 
        this.bufferContext.lineTo(currentX, currentY); 
        let random = Math.random() > 0.3; 
        if (random) { 
          this.bufferContext.lineTo( 
            currentX - this.pieceLengthX, 
            currentY + this.pieceLengthY 
          ); 
          this.bufferContext.moveTo(currentX, currentY); 
        } 
      } 
    } 
    this.bufferContext.stroke(); 
    this.bufferContext.closePath(); 
    // End Lines 
  } 
  renderDOM() { 
    this.currentX += (this.clientX - this.currentX) / 40; 
    if (Math.abs(this.clientX - this.currentX) < 0.5) { 
      this.currentX = this.clientX; 
    } 
    console.log(this.currentX, this.clientX); 
    this.DOMContext.clearRect(0, 0, this.DOMWidth, window.innerHeight); 
    // void ctx.drawImage(image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight); 
    // console.log(this.pageY); 
    this.DOMContext.drawImage( 
      this.bufferCanvas, 
      250 + this.currentX, // sx 
      this.pageY, // sy 
      this.DOMWidth, // sW 
      this.DOMHeight, // sH 
      0, // dx 
      0, // dy 
      this.DOMWidth, // dW 
      this.DOMHeight // sH 
    ); 
    this.penetrate = false; 
  } 
 
  handleMouseMove(e) { 
    this.clientX = (e.clientX - this.DOMWidth / 2) / 10; 
  } 
  handleScroll(e) { 
    // console.log("handleScroll"); 
    this.pageY = window.pageYOffset; 
  } 
  listen() { 
    window.addEventListener("mousemove", this.handleMouseMove.bind(this)); 
    window.addEventListener("scroll", this.handleScroll.bind(this)); 
  } 
} 
 
const pattern = new Pattern( 
  document.querySelector("canvas.background"), 
  document.createElement("canvas") 
); 
pattern.renderBuffer(); 
pattern.listen(); 
 
function animate() { 
  pattern.renderDOM(); 
  requestAnimationFrame(animate); 
} 
animate();
body { 
	margin: 0; 
	padding: 0; 
	height: 4000px; 
	max-width: 1440px; 
} 
div.canvas { 
	overflow: visible; 
	height: 4000px; 
}
<!DOCTYPE html> 
<html lang="en"> 
  <head> 
    <meta charset="UTF-8" /> 
  </head> 
  <body> 
    <canvas style="position: fixed; top: 0;" class="background"></canvas> 
  </body> 
</html>

READ ALSO
Один код javascript для двух одинаковых id

Один код javascript для двух одинаковых id

Не подскажете, как сделать так, что бы javascript применялcя к двум блокам (gentags)? А то, на данный момент, все работает только на первом блокеЗаранее...

167
как сделать красивее код?

как сделать красивее код?

Красивее - коротко и ясно, а не с кучей макарон

168
Захватить разные скриншоты

Захватить разные скриншоты

Пытаюсь написать такой тест:

145
JavaScript | node.js | Discord bot | Помогите разобраться с Массивом

JavaScript | node.js | Discord bot | Помогите разобраться с Массивом

Такая проблема, у меня есть 2 массива, к примеру эти:

161