javascript - img - table html
Cargando imágenes bpg: técnicas compatibles, sencillas y eficientes. (2)
Recientemente ha habido algunos problemas con el formato de imagen BPG de Fabrice Bellard ( http://bellard.org/bpg/ ), que (basado en las demostraciones proporcionadas en su sitio) proporciona una mejor compresión que jpeg, webp y algunos otros. La decodificación de la imagen se realiza en el navegador con JS, lo que significa que se puede usar de inmediato, sin esperar la adopción del navegador. En general, esto parece ser una buena idea y cambiar un poco de tiempo de CPU para una descarga más rápida es a menudo un compromiso viable.
La técnica que se usa aquí para intercambiar imágenes es, en window.load, iterar sobre document.images, encontrar cualquier lugar donde el atributo src contenga una URL que termine con ".bpg" y reemplazarla con un lienzo.
Sin embargo, definitivamente esta no es la única forma de abordar el problema, y veo algunos aspectos negativos de esta técnica, que incluyen: a) los lienzos no tienen exactamente las mismas reglas de diseño que las imágenes, por ejemplo, configurar el atributo de ancho significa algo diferente en un img vs un lienzo, b) también parece que, al menos en Chrome, la forma en que se realiza el escalado para las imágenes que se reducen frente a los lienzos es diferente.
Una mejor solución ideal sería cumplir con estos requisitos:
- Intente no duplicar los datos de imagen en la memoria más de lo necesario (y tampoco utilizar innecesariamente más CPU de la necesaria; la decodificación en JS ya requiere mucho en comparación con el manejo de imágenes nativas)
- Tener tanta compatibilidad de navegador como sea posible
- Use etiquetas <img> en lugar de <canvas> (no es un requisito, pero parece ser mejor)
- Proporcione una manera fácil no solo de procesar imágenes en la carga de documentos, sino también de imágenes que se agreguen al documento más adelante (por ejemplo, en respuesta a la actividad del usuario)
- Sigue siendo fácil de usar (la técnica existente en bellard.org es ciertamente fácil de integrar)
- EDITAR: usar trabajadores web para decodificar la imagen sin bloquear la página también es potencialmente una buena forma de proceder.
Algunas herramientas relevantes que vienen a la mente incluyen data: y blob: urls.
¿Alguien tiene ejemplos de códigos de trabajo que carguen BPG usando tales técnicas "mejores"? (La forma en que Fabrice lo tiene en sus ejemplos no es mala, y ciertamente los enfoques tienen concesiones, pero creo que puede haber algo mejor para el uso generalizado).
Agregue un controlador de eventos de error a las imágenes. En Chrome 40, se activa cuando falta la imagen o cuando el navegador no sabe cómo mostrarla:
<script>
var errors = [];
function fallback(elt) {
if(errors[elt.src]) return;
// you should also extract the extension in order to test lena.bpg, lena.png, lena.jpg and not lena.bpg.png, lena.bpg.png.jpg
errors[elt.src] = true;
console.error(''could not load image, falling back'');
elt.src = elt.src + ''.png'';
// stop trying
elt.onerror= function(){};
}
</script>
<img src="test.html" onerror="fallback(this);" width=300 height=300 />
Pros: no uso de memoria / CPU en la parte del cliente. Contras: necesidad de proporcionar imágenes alternativas en el servidor.
Creo que podría declarar un detector de errores global en todas las etiquetas img
para que esto también funcione para futuras imágenes.
BPG se ve prometedor. Si desea detectar la adición de elementos <img>
en cualquier momento, desde cualquier fuente, puede usar MutationObservers . En caso de que no los conozca, son asíncronos, registran un subconjunto de todas las mutaciones de DOM en un documento y permiten que la devolución de llamada maneje esas mutaciones a la vez en lugar de sincronizarse como ocurre con los eventos de DOM. Entonces, si creas o cambias la fuente de muchas imágenes en un script, el observador esperará hasta que tu script termine, y luego procesará todas las nuevas imágenes de una sola vez (por lo tanto, los bucles en las devoluciones de llamada).
Lo siguiente asume que tiene una función llamada doSomethingToDecode(img)
(lo siento, no voy a ayudar con eso ahora) que reemplaza el src
del img con (probablemente) un PNG generado. Puede hacerlo de forma asíncrona, eso no es un problema. Además, no es necesario que deje de observar la imagen mientras intercambia su src
, siempre que la sustitución generada no termine en ".bpg".
Este primer observador reaccionará cuando agregue <img>
descendientes en cualquier lugar (y otros elementos, pero los ignora); desafortunadamente no es posible optimizarlo mucho en el caso general. Tiene que iterar sobre la lista de mutaciones, y luego la lista de nuevos nodos para cada mutación, por lo tanto, aquellos anidados for
bucles. Pero
imgObs=new MutationObserver(
function(mutations){
for(var i=0, m; m=mutations[i]; i++) if(m.addedNodes.length)
for(var j=0, img; img=m.addedNodes[j]; j++) if(img.localName=="img"){
if(img.src && //.bpg$/.test(img.src))
doSomethingToDecode(img)
srcObs.observe(img, srcOptions)
}
}
)
Este otro observador reacciona cuando cambia el atributo src
de un elemento, y debería observar solo los elementos <img>
(para un rendimiento superior, no tiene fallos en caso de que lo haga observar otros elementos, así que no lo haga).
srcObs=new MutationObserver(
function(mutations){
for(var i=0, m; m=mutations[i]; i++){
var img=m.target
if(img.src && //.bpg$/.test(img.src))
doSomethingToDecode(img)
}
}
)
También tenga esto a mano, lo necesitaremos cada vez que comencemos a observar una nueva imagen:
var srcOptions={childList:false, attributes:true, attributeFilter:["src"]}
También puede hacer que un observador que reacciona a la eliminación de los elementos <img>
para dejar de observarlos, y libere cualquier recurso relacionado con la descodificación, pero es de esperar que el navegador sea al menos lo suficientemente inteligente como para dejar de observar los elementos que deben recolectarse en la basura (no probado!).
Ejecute esto después de cargar el HTML (no espere toda la página con imágenes y CSS, etc.). Nota: esto está utilizando la colección de document.images
DOM nivel 0. Muy anticuado, pero está haciendo exactamente lo que queremos de manera muy eficiente y concisa, así que, ¿por qué diablos no?
Entonces, primero decodifica los <img>
s existentes con fuentes bpg, y los observas para los cambios de src
más adelante:
for(var i=0, img; img=document.images[i]; i++){
if(img.src && //.bpg$/.test(img.src))
doSomethingToDecode(img)
srcObs.observe(img, srcOptions)
}
Luego, esto le dice al primer observador que reaccione a las cosas en el <body>
del documento completo; desafortunadamente, no hay tagNameFilter
parámetro tagNameFilter
para filtrar de forma nativa las mutaciones que no sean <img>
childList.
imgObs.observe(document.body, {subtree:true, childList:true, attributes:false})