IE 中图片大小的误判

IE8 以下的浏览器都会出现这个问题,不致命,但非常影响页面观感。幸运的是,你不一定能遇上。

情景是,图片需要动态加载,根据给出的缩放比例调整显示大小。图片是普通 PNG,只是存放点是多样化的,同样的图片,一个以文件形式存在,另一个是保存于缓存(内存)中,前面都是用 Apache,一个读文件返回,一个做转发,用 IE 访问它们,获取其大小信息,结果竟然是不一样的。

装载图片的代码片段:

function loadImg(url, callback) {
    var img = new Image();
    img.src = url;
    
    if (img.complete) {
        console.log("complete");
        return callback.call(img, url);
    }
    img.onload = function() {
        img.onload = null;
        callback.call(img, url);
    }
    return img;
}

要完成缩放,需要获取图片的原始大小。当然也可以用百分比数值,方案比较不是本文重点。下面写一个函数可以打印出图像对象上所有的数值属性,当然包括宽和高。

function getParam() {
    var arr = [];
    for(var i in this) {
        try {
            if (typeof this[i] !== 'number') continue;
            arr.push(i + ' : ' + this[i] + '\n');
        } catch(e) {} // this[i] 在 IE 下有可能会报错,不影响观察,暂且忽略
    }
    alert(arr.join('')); // 使用 alert 方便观看结果
};

然后都执行下面的,其中 testPngURL 分别为两者的 URL,这也是唯一的区别了。

preload(testPngURL, getParam);

两次执行的结果显示的 width 和 height 是不一样的,而且不成比例,而且结果是可以稳定重复的。我最纳闷的是 IE 凭什么得出另外那个错误结果的。我初时觉得可能是两次请求的 header 不一样,主要差异是存放于内存的,其响应的 Content-Type 是 application/octet-stream,文件形式的则是 image/png,内容长度是完全一样的。于是我使用 Fiddler 对内存图像的请求进行拦截,并修改其 Content-Type,结果是无效的。甚至将两者的响应头弄成完全一致,也得不到一致的结果。

暂时还没想到有什么方法可以绕过。而且最奇怪的是,有少数几个 png 的大小是正确的。难道问题出在 png 的数据上,有些隐藏的信息,但触发点到底是什么,可是敲爆头也不知道了。

虽然生产不会这么使用,但为了开发调试方便,我们会使用缓存来存放各种资源,其返回头都是非常简单而且统一的,无任何多余 header,浏览器不会自作主张做缓存。更新也方便,往同一个地址 PUT 内容即可。现在由于 IE 存在这样一个问题,其显示大小是不正确的,非常影响观感,不得不需要做些调整了。

Chrome,为什么第二张图片看不见呢?

在测试的时候,建立一个最小测试集对于测试调试是很有帮助的。

为了测试与图片装载相关的内容,建立了下面的页面。内容很简单,就是动态添加两个 IMG。

<html>
<head>
    <title>Test Loading IMG</title>
    <style>body { background-color: #00F; }</style>
</head>
<body>
<script>
function show() {
    for (var i = 0; i < 2; i++) {
        var img = new Image();
        img.src = "chrome.png";
        img.title = "Image here";
        document.body.appendChild(img);
    }
}
setTimeout(show, 1000);
</script>
</body>
</html>

用各种浏览器(IE6 除外)打开,理论上应该不会出什么乱子。

但是竟然发现在 chrome 里第二个图不见了。实际上图是在那里的,只是看不见,鼠标移上去,title 是能正确显示出来,进行全选操作之后,图就出现了。

当然,实际页面中不会只有两个 IMG,只要在 body 内加一个字符如 a,chrome 就可以正常显示了,-_-!!

当前我的 chrome 版本是 7.0.517.41 beta,估计会有人跟我说 chrome 更新这么快,beta 嘛,难免的。

希望 chrome 越办越好啦~

Img装载完毕后自动调整大小

在show图片的时候经常需要用到它,大概是大家都比较懒去为每个图调整大小,用Photoshop的批处理是可以省掉很多功夫了,但为求更省事,用脚本啦。

<img id="inu" src="your-image-path.jpg" onload = "resize(this)" />

在页面里插一行就可以了(resize函数体在下面),如果图像是动态装载的

var img = document.createElement('img');
img.onload = resize;
img.src = "http://farm4.static.flickr.com/3575/3343323074_499a81709c_o.png";

var MAX_IMAGE_SIZE = {
	x: 400,
	y: 400
};

function resize(obj) {
	var img = obj || this;
	var size = MAX_IMAGE_SIZE;
	var rate = img.offsetWidth / img.offsetHeight;
	if (img.offsetWidth > size.x) {
		img.style.width = size.x + "px";
		img.style.height = size.x / rate + "px";
	}
	if (img.offsetHeight > size.y) {
		img.style.width = size.y * rate + "px";
		img.style.height = size.y + "px";
	}
	// 如果有需要居中的话,用CSS也可以实现
	reCenter(img, size);
}

function reCenter(img, size) {
	if (img.offsetHeight < size.y) {
		img.style.marginTop = (size.y - img.offsetHeight)/2 + "px";
	}
	if (img.offsetWidth < size.x) {
		img.style.marginLeft = (size.x - img.offsetWidth)/2 + "px";
	}
}

网上也有不少说onload事件在不同浏览器里的不同现象,Netscape(国内没什么人用了吧) 和 IE不是每次都触发onload事件,只有从网上下载时才执行,从缓存里装载后不执行。

那应该是因为设置onload在设置src之后,设置src后浏览器随即从缓存里装载好了,那时再指定onload为时已晚了,但在Firefox里在前在后都始终如一。最稳是在src之前设置onload属性。