2011-10-25 6 views
6

Me tropecé con un problema extraño. El siguiente código hace que la imagen se desvanezca porque está sobregirada por un rectificador semiopaco una y otra vez.rgba fillStyle con alfa no se vuelve completamente opaco si se aplica varias veces

Pero al menos en la 10 ª iteración de draw(); la imagen debe estar completamente sobregirado, porque el rect debe ser completamente opaco para entonces, ¿no? Pero en realidad nunca desaparece por completo.

Este efecto es peor en Chrome que en Firefox. Pero cuidado: las malas pantallas pueden ocultar este comportamiento defectuoso =)

También hice un demo en jsFiddle.

$(function() { 
var canvas = $("#mycanvas"), 
    ctx = canvas[0].getContext("2d"), 
    imgUrl = "http://it-runde.de/dateien/2009/august/14/25.png"; 


var image = new Image(); 
image.src = imgUrl ; 
$(image).load(function() { 
    ctx.drawImage(image, 0, 0, canvas.width(), canvas.height()); 
    draw(); 
}); 

function draw() {   
    ctx.fillStyle = "rgba(255, 255, 255, 0.1)"; 
    ctx.fillRect(0,0,canvas.width(),canvas.height()); 
    setTimeout(draw, 100); 

}  
}); 

El efecto uno puede desear alcanzar es que, digamos que un objeto se mueve por todo el lienzo, y las posiciones ya dibujadas obtener descubierto sólo ligeramente después de iluminarse de efecto después de decoloración. Pero este resultado es simplemente fugly.

¿Hay alguna solución para esto?

+1

Recuerde que funciona sin lienzo "recordar" al estado anterior. Por lo tanto, un alfa de '0.1' no significa que después de 10 veces lo haya hecho completamente opaco, sino que la opacidad será '(1 - 0.1) * (1 - 0.1) * ...' que nunca alcanzará '0'. – pimvdb

Respuesta

4

Dado que el rectángulo tiene solo un 10% de opacidad, el resultado de dibujarlo sobre la imagen es un compuesto del 90% de la imagen y el 10% de blanco. Cada vez que lo dibuja, pierde el 10% de la iteración anterior de la imagen; el rectángulo en sí no se vuelve más opaco. (Para obtener ese efecto, necesitaría colocar otro objeto sobre la imagen y animar su opacidad). Entonces, después de 10 iteraciones, todavía tiene (0.9^10) o aproximadamente el 35% de la imagen original. Tenga en cuenta que los errores de redondeo probablemente se establecerán después de aproximadamente 30 iteraciones.

+0

¡me ganaste! – gthmb

+0

Todavía no lo entiendo. ¿Es esto un cálculo interno de procesamiento de imagen? Es ilógico para mí. Si digo en Photoshop posiciones 10 capas con cada 10% de opacidad sobre cierto contenido, las capas lo cubrirán por completo. Esto también se aplicaría en la vida real, ¿no? Quiero decir, entiendo tu respuesta, pero ¿por qué es así? De acuerdo con su explicación, colocaría prácticamente una imagen con 100% de opacidad y sustraería el 10% de su opacidad actual por iteración, pero eso no es lo que está sucediendo aquí ... = (y: No, la imagen no desaparece después de 30 iteraciones. allí, en gris. – owzim

+0

piensa que reduce la cantidad de transparencia en un 10% con cada nueva capa. Después de una capa, has reducido del 100% al 90%. La segunda capa reduce el 90% restante en un 10%, por lo que solo el 9%. Ahora estás en alrededor del 81%. La tercera capa reduciría el 81% en un 8.1%, dejándote con un 72.9%, etc .... – gthmb

3

Sé que esto es antiguo, pero no creo que la respuesta previamente aceptada sea correcta. Creo que esto está sucediendo como resultado de que los valores de píxel se truncan de float a byte. En Windows 7 con la versión de Chrome 39.0.2171.95m, después de ejecutar su violín por un tiempo, la imagen sigue siendo visible, pero solo ligeramente, y no parece estar cambiando más. Si tomo una captura de pantalla que veo los siguientes valores de los píxeles en la imagen:

(246, 246, 246)

Cuando se dibuja un rectángulo sobre él con RGBA de:

(255, 255, 255, 0,1)

y aplicar la mezcla alfa utilizando el modo de composición por defecto de la fuente en off, antes de convertir a un byte que se obtiene:

(255 * 0,1 + 246 * 0,9) = 246,9

Así se puede ver que, suponiendo que el navegador simplemente trunca el valor de punto flotante a un byte, se escribirá un valor de 246 , y cada vez que repites la operación de dibujo siempre terminarás con el mismo valor.

Hay una gran discusión sobre el tema en esta publicación de blog here.

Como solución alternativa, puede borrar el lienzo de forma continua y volver a dibujar la imagen con un valor GlobalAlpha cada vez menor.Por ejemplo:

// Clear the canvas 
    ctx.globalAlpha = 1.0; 
    ctx.fillStyle = "rgb(255, 255, 255)"; 
    ctx.fillRect(0,0,canvas.width(),canvas.height()); 

    // Decrement the alpha and draw the image 
    alpha -= 0.1; 
    if (alpha < 0) alpha = 0; 
    ctx.globalAlpha = alpha; 
    console.log(alpha); 
    ctx.drawImage(image, 0, 0, 256, 256); 
    setTimeout(draw, 100); 

Fiddle es here.

0

La razón fue expresada perfectamente antes. Es no es posible deshacerse de él sin borrarlo y volver a dibujarlo como @Sam ya dijo.

Lo que puede hacer para compensarlo un poco es establecer globalCompositeOperation.

Hay varias operaciones que ayudan. De mis pruebas puedo decir que hard-light funciona mejor para fondos oscuros y lighter funcionan mejor para fondos brillantes. Pero esto depende mucho de tu escena.

Un ejemplo haciendo senderos en "cerca" negro

ctx.globalCompositeOperation = 'hard-light' 
ctx.fillStyle = 'rgba(20,20,20,0.2)' // The closer to black the better 
ctx.fillRect(0, 0, width, height) 

ctx.globalCompositeOperation = 'source-over' // reset to default value 
Cuestiones relacionadas