我有一个画布,上面有图像和线条,我想做出回应。最初我是在重画图像,但是它在闪烁,所以我找到了将画布复制到临时画布然后再复制的解决方案。但是这样做时,图像质量会变得非常低且模糊。有什么方法可以获取初始图像质量?
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';
答案 0 :(得分:1)
唯一的方法是从原始图像重绘。您无法发明已丢弃的数据。
但是请注意,您的闪烁问题可能是由于resize事件的触发频率可能高于屏幕刷新率。
因此,您最终要重置整个上下文+全部重绘+在单个帧中多次缩放图像。
因此,您很有可能确实会发生闪烁。
为避免这种情况,请使用 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>