Есть
сайт, в нем канвас (самолет), который лагает при прокрутке, появляеются артефакты, причем по разному, в зависимости от того, чем скроллить - колесиком мыши или скроллбаром. Если закомментить параллакс или видео в начале, то ничего не меняется. Во время скролла обращений к ДОМ не происходит. Лаги либо из за скрипта самолета, либо из-за clearRect. Как это можно оптимизировать и избавиться от артефактов?
Код на кодпене.
P.S. На
кодпене не появляются артефакты, они лишь на
сайте!
UPD: Что еще странно, артефакты появляются лишь в Chrome и Opera.
Вот JS-код:
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++;
}
}