M
M
Maxim2017-08-29 12:46:49
css
Maxim, 2017-08-29 12:46:49

Canvas. How can you optimize and get rid of artifacts?

There is a site with a canvas (airplane), which lags when scrolling, artifacts appear, and in different ways, depending on how you scroll - with a mouse wheel or a scrollbar. If you comment out the parallax or video at the beginning, nothing changes. During the scroll, there are no calls to the DOM. Lags are either due to the aircraft script, or due to clearRect. How can this be optimized and get rid of artifacts? Code on codepen .

PS Artifacts do not appear on the code pen, they are only on the site !

UPD: Strangely enough, artifacts only appear in Chrome and Opera.

Here is the JS code:

var scrolled;
  var positionsArr = [];
  var commonHeight = 0;
  var canvas = document.querySelector('#canvas');
  var canvasB = document.querySelector('#canvasback');
  var ctx = canvas.getContext('2d');
  var ctxB = canvasB.getContext('2d');
  var centerX, centerY, radius, pointsOffset, radiusRatio, centerXRatio,
    firstPoint, lastPoint, jet, blocksPosition, jetPositionY, jetTop, tabletOffset, headerHeight, canvasClearWidth;
  var wWidth, mediaMobile, mediaTablet, mediaDesktop1, mediaDesktop2, mediaDesktop3;

  jet = new Image();
  jet.src = 'http://silencer.website/alkor/images/jet.png';
  jet.onload = function () {
    adaptive();
  }

  $(window).on('resize', function () {
    adaptive();
  });

  function adaptive() {
    mediaMobile = mediaTablet = mediaDesktop1 = mediaDesktop2 = mediaDesktop3 = false;
    wWidth = $(window).width();

    // Параметры для дуги по умолчанию
    tabletOffset = 0;
    radiusRatio = .95;
    centerXRatio = 1.25;

    // Параметры для дуги на разных разрешениях (перезаписывают параметры по умолчанию)
    if (wWidth < 768) {
      mediaMobile = true;
    }
    if (wWidth >= 768 && wWidth < 1024) {
      mediaTablet = true;
      tabletOffset = 120;
    }
    if (wWidth >= 1024 && wWidth < 1280) {
      mediaDesktop1 = true;
      tabletOffset = -40;
      radiusRatio = 1.1;
      centerXRatio = 1.03;
    }
    if (wWidth >= 1280 && wWidth < 1440) {
      mediaDesktop2 = true;
      tabletOffset = -20;
    }
    if (wWidth >= 1440) {
      mediaDesktop3 = true;
      tabletOffset = -20;
    }

    if (!mediaMobile) {
      setTimeout(function () {
        doCanvas();
      }, 500);
    } else {
      ctx.clearRect(0, 0, canvas.width, canvas.height);
      $(window).off('scroll', onScroll);
    }
  }

  function doCanvas() {
    $(window).on('scroll', onScroll);

    var marginBottom = 120;
    commonHeight = 0;

    $('.history__item').each(function () {
      commonHeight = commonHeight + $(this).height() + marginBottom;
    });
    commonHeight = commonHeight - marginBottom;

    canvasWidth = $('.history').width() * .3;
    canvasHeight = $('.history__blocks').height();
    canvas.width = canvasWidth;
    canvas.height = canvasHeight;

    canvasB.width = canvas.width;
    canvasB.height = canvas.height;

    offsetLeft = $('.history__blocks')[0].offsetLeft;
    $('#canvas, #canvasback').css('marginLeft', -offsetLeft);

    radius = commonHeight * radiusRatio;
    centerX = -commonHeight / centerXRatio + offsetLeft + tabletOffset;
    centerY = commonHeight / 2;
    pointsOffset = 100;

    canvasClearWidth = centerX + radius + 110;

    blocksPosition = $('.history__blocks')[0].offsetTop;
    jetPositionY = $('.history')[0].offsetTop;
    headerHeight = $('.history__title').height();
    jetTop = $(window).height() / 2;

    jetOnScroll = jetTop - headerHeight - jetPositionY;

    positionsArr = [];

    lastIndex = $('.history__item').length - 1;
    darkened = $('.history__item');

    for (var i = 0; i < $('.history__item').length; i++) {
      var position = $('.history__item').eq(i)[0].offsetTop - blocksPosition + pointsOffset;
      positionsArr.push(position);

      if (i == 0) {
        firstPoint = position;
      }

      if (i == $('.history__item').length - 1) {
        lastPoint = position;
      }
    }

    draw();
    drawBackground();
    setTimeout(function () {
      if (mediaTablet) {
        jetPositionLine(firstPoint);
      } else {
        jetPosition(firstPoint);
      }

    }, 100);
  }

  function drawBackground() {
    if (mediaTablet) {
      drawLine();
    } else {
      drawArc();
    }
  }

  function draw(currentY) {
    for (var i = 0; i < positionsArr.length; i++) {
      addPoint(positionsArr[i], currentY, i);
    }
  }

  function drawArc() {
    var firstPointRad = Math.asin((centerY - firstPoint) / radius);
    var lastPointRad = Math.asin((centerY - lastPoint) / radius);

    ctxB.beginPath();
    ctxB.lineWidth = 3;
    ctxB.save();
    ctxB.arc(centerX, centerY, radius, 1.5, lastPointRad, true);
    ctxB.strokeStyle = '#99daf0';
    ctxB.setLineDash([8, 5]);
    ctxB.lineDashOffset = 1;
    ctxB.globalCompositeOperation = 'destination-over';
    ctxB.stroke();
    ctxB.restore();
  }

  function drawLine() {
    ctxB.beginPath();
    ctxB.lineWidth = 3;
    ctxB.save();
    ctxB.lineTo(tabletOffset, firstPoint);
    ctxB.lineTo(tabletOffset, lastPoint);
    ctxB.strokeStyle = '#99daf0';
    ctxB.setLineDash([8, 5]);
    ctxB.lineDashOffset = 1;
    ctxB.globalCompositeOperation = 'destination-over';
    ctxB.stroke();
    ctxB.restore();
  }

  function addPoint(y, currentY, i) {
    if (mediaTablet) {
      var currentX = tabletOffset;
    } else {
      var angle = Math.asin((centerY - y) / radius);
      var currentX = centerX + radius * (Math.cos(angle));
    }

    ctx.beginPath();
    ctx.arc(currentX, y, 8, 0, 2 * Math.PI, false);
    ctx.lineWidth = 3;
    ctx.fillStyle = '#00a3da';
    ctx.globalCompositeOperation = 'source-over';
    if (currentY + 10 >= y) {
      ctx.strokeStyle = '#fff';
      darkened.eq(i).removeClass('darkened');
    } else {
      ctx.strokeStyle = '#99daf0';
      darkened.eq(i).addClass('darkened');
    }

    ctx.fill();
    ctx.stroke();
  }

  function jetPosition(y) {
    var angle = Math.asin((centerY - y) / radius);
    var currentX = centerX + radius * (Math.cos(angle) - 1);

    var firstPointRad = Math.asin((centerY - firstPoint) / radius);

    // if (toUp) {   // Самолетик вверх-вниз
    var jetAngle = Math.acos((centerY - y) / radius);
    // } else {
    // 	var jetAngle = Math.acos((centerY - y) / radius) + Math.PI;
    // }

    ctx.beginPath();
    ctx.arc(centerX, centerY, radius, -angle, -firstPointRad, true);
    ctx.globalCompositeOperation = 'source-over';
    ctx.lineWidth = 3;
    ctx.strokeStyle = '#fff';
    ctx.stroke();

    draw(y);

    ctx.save();
    ctx.translate(currentX + radius, y)
    ctx.rotate(jetAngle - 1.6);
    ctx.globalCompositeOperation = 'source-over';
    ctx.drawImage(jet, -106, -80, 212, 160);
    ctx.restore();
  }

  function jetPositionLine(y) {
    ctx.beginPath();
    ctx.lineTo(tabletOffset, firstPoint);
    ctx.lineTo(tabletOffset, y);
    ctx.lineWidth = 3;
    ctx.strokeStyle = '#fff';
    ctx.stroke();

    draw(y);

    ctx.beginPath();
    ctx.save();
    ctx.globalCompositeOperation = 'source-over';
    ctx.drawImage(jet, tabletOffset - 106, y - 80, 212, 160);
    ctx.restore();
  }

  function onScroll() {
    requestAnimationFrame(scrollCanvas);
  };

  function scrollCanvas() {
    var prevScroll = scrolled;
    scrolled = window.pageYOffset;

    toUp = prevScroll < scrolled;
    var positionY = scrolled + jetOnScroll;
    if (positionY >= firstPoint) {
      if (positionY <= lastPoint) {
        ctx.clearRect(0, 0, canvasClearWidth, canvasHeight);

        if (mediaTablet) {
          jetPositionLine(positionY);
        } else {
          jetPosition(positionY);
        }
      } else {
        $('.history__item').eq(lastIndex).removeClass('darkened');
      }
    }
    if (!mediaMobile && !mediaTablet) {
      parallaxScroll();
    }


    if (prevScroll > scrolled) {
      paraCounter--;
    } else {
      paraCounter++;
    }

  }

Answer the question

In order to leave comments, you need to log in

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question