Изображение размыто при копировании с холста на холст - PullRequest
0 голосов
/ 12 сентября 2018

У меня есть холст с изображением и линиями, которые я хочу сделать ответными.Сначала я перерисовывал изображение, но оно мерцало, поэтому я нашел чье-то решение скопировать холст на временный холст, а затем обратно.Но при этом качество изображения становится крайне низким и размытым.Есть ли способы получить исходное качество изображения?

function resize() {         
    var tempCanvas = document.createElement('canvas');
    tempCanvas.width = ctx.canvas.width;
    tempCanvas.height = ctx.canvas.height;
    var tempContext = tempCanvas.getContext("2d");
    tempContext.drawImage(ctx.canvas, 0, 0, tempCanvas.width, tempCanvas.height);       
    canvas.width = tempCanvas.width
    canvas.height = 600 * canvas.width / 1400;  
    ctx.drawImage(tempContext.canvas, 0, 0, canvas.width, canvas.height);
}
window.addEventListener("resize", resize, false);
function drawLines(canvas, context){
    var width = canvas.width;
    var offset = 100 * canvas.height / 600;
    context.beginPath();
    context.moveTo(0, 0);
    context.lineTo(width, 0);
    context.lineTo(width, offset);
    context.fill();

    context.beginPath();
    context.moveTo(0, canvas.height - offset);
    context.lineTo(width, canvas.height);
    context.lineTo(0, canvas.height);
    context.fill();
}
var canvas = document.getElementById("new-canvas");
var ctx = canvas.getContext("2d");
canvas.width = window.innerWidth; 
canvas.height = 600 * window.innerWidth / 1400;
var img = new Image();
img.onload = () => {
    document.getElementById("canvas").style.height = canvas.height;
    ctx.globalCompositeOperation = 'xor';   
    ctx.drawImage(img, 0, 0, canvas.width, canvas.height); 
    drawLines( canvas, ctx );   
};
img.src = 'image.jpg';

1 Ответ

0 голосов
/ 12 сентября 2018

Единственный способ - перерисовать исходное изображение.Вы не можете придумать данные, которые были отброшены.

Но учтите, что ваша проблема с мерцанием, вероятно, была вызвана тем фактом, что событие изменения размера может запускаться с более высокой частотой, чем ваша частота обновления экрана.
Таким образом, вы завершили сбросвесь контекст + перерисовка всего этого + масштабирование изображения много раз в одном кадре.

Таким образом, вполне возможно, что вы действительно испытываете мерцание.

Чтобы избежать этого, ограничьте ваше событие 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>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...