Единственный способ - перерисовать исходное изображение.Вы не можете придумать данные, которые были отброшены.
Но учтите, что ваша проблема с мерцанием, вероятно, была вызвана тем фактом, что событие изменения размера может запускаться с более высокой частотой, чем ваша частота обновления экрана.
Таким образом, вы завершили сбросвесь контекст + перерисовка всего этого + масштабирование изображения много раз в одном кадре.
Таким образом, вполне возможно, что вы действительно испытываете мерцание.
Чтобы избежать этого, ограничьте ваше событие requestAnimationFrame , чтобы обрабатывать событие только один раз за кадр.
function throttle(callback) {
if (typeof callback !== 'function')
throw new TypeError('A callback function must be passed');
var active = false; // a simple flag
var evt; // to keep track of the last event
function handler() { // fired only when screen has refreshed
active = false; // release our flag
callback(evt);
}
return function handleEvent(e) { // the actual event handler
evt = e; // save our event at each call
if (!active) { // only if we weren't already doing it
active = true; // raise the flag
requestAnimationFrame(handler); // wait for next screen refresh
}
};
}
const ctx = canvas.getContext('2d');
const img = new Image();
img.src = 'https://upload.wikimedia.org/wikipedia/commons/5/55/John_William_Waterhouse_A_Mermaid.jpg';
img.onload = start;
const grad = ctx.createRadialGradient(50, 50, 0, 50, 50, 50);
grad.addColorStop(0.2, 'gold');
grad.addColorStop(1, 'transparent');
const bokeh = [];
for(let i=0; i<30; i++) {
bokeh.push({
x: Math.random(),
y: Math.random(),
s: Math.random()
});
}
function start() {
// our resize handler will fire only once per frame
window.onresize = throttle(resizeHandler);
resizeHandler();
}
function resizeHandler() {
canvas.width = innerWidth;
canvas.height = img.height* (innerWidth / img.width);
draw();
}
function draw() {
ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
ctx.fillStyle = grad;
ctx.globalCompositeOperation = 'lighter';
bokeh.forEach(function(b) {
const size = b.s*canvas.width / img.width;
const x = b.x * canvas.width;
const y = b.y * canvas.height;
ctx.setTransform(size, 0, 0, size, x, y);
ctx.fillRect(0,0,100, 100);
});
}
body{margin:0}
<canvas id="canvas"></canvas>
Но будьте осторожны, , хотя такое регулирование работает хорошо для статического контента, если вы запускаете цикл анимации, он закончитсяна самом деле вызывает больше мерцания.
Действительно, поскольку цикл анимации должен работать на * requestAnimationFrame¨, а следующий тик запланирован с момента последнего кадра, ваш код анимации будет выполняться до того, как мы удушимобработчик событий.
Это означает, что когда браузер завершит выполнение всех сложенных обратных вызовов rAF, последним действием будет наш обработчик изменения размера, и он закрасит пустой холст.
Так что в случаеанимированный контент, вам нужно обрабатывать этот случай непосредственно из цикла анимации.
Вы устанавливаете обработчик изменения размера так, чтобы просто поднимать флаг, сообщая основному циклу, что он должен изменять размер холста перед любыми другими действиями.Если с момента последнего кадра не произошло никакого события изменения размера, просто проигнорируйте это обновление и продолжайте работу с остальным циклом анимации.
Таким образом, вы обязательно запустите свой код только при необходимости и только один раз за кадр.
// a simple 'size' object
const size = {
dirty: true,
update: () => {
// will get called from the mainLoop, if dirty
canvas.width = innerWidth;
canvas.height = img.height * (innerWidth / img.width);
size.dirty = false;
}
};
// the resize handler only rises the size.dirty flag
window.onresize = e => size.dirty = true;
// the main anim loop, called every frame
function mainLoop() {
// only if it did change
if (size.dirty) {
// resizing the canvas is the first step
size.update();
}
// now we can update our objects
flakes.forEach((flake) => flake.update());
// and finally draw
draw();
// we are a loop
requestAnimationFrame(mainLoop);
}
function draw() {
ctx.setTransform(1, 0, 0, 1, 0, 0)
ctx.globalCompositeOperation = 'source-over';
ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
ctx.fillStyle = grad;
ctx.globalCompositeOperation = 'lighter';
flakes.forEach((flake) => flake.draw());
}
const ctx = canvas.getContext('2d');
const img = new Image();
const grad = ctx.createRadialGradient(50, 50, 0, 50, 50, 50);
grad.addColorStop(0.2, 'gold');
grad.addColorStop(1, 'transparent');
class Flake {
constructor() {
this.x = Math.random();
this.y = Math.random();
this.s = Math.random();
this.wind = this.weight = this.s * 0.001;
this.dx = Math.random() * this.s * 0.1;
}
update() {
let dx = this.dx += this.wind;
this.x += Math.sin(dx * 0.01);
if (Math.abs(dx) > .1 * this.s) this.wind *= -1;
this.y += this.weight;
if (this.y > 1) this.y = ((this.s * 100) / canvas.height) * -1;
}
draw() {
const size = this.s * canvas.width / img.width;
const y = this.y * canvas.height;
const rad = size * 50;
const area = canvas.width + (rad * 2);
const x = ((this.x * canvas.width) % area) - (rad * 2);
ctx.setTransform(size, 0, 0, size, x, y);
ctx.fillRect(0, 0, 100, 100);
}
}
const flakes = [];
for (let i = 0; i < 30; i++) {
flakes.push(new Flake());
}
img.src = 'https://upload.wikimedia.org/wikipedia/commons/5/55/John_William_Waterhouse_A_Mermaid.jpg';
img.onload = mainLoop;
body {
margin: 0
}
<canvas id="canvas"></canvas>