Periodically, someone tells me about the magic of PNG, how it's the ideal image format for the web, and that someday we'll all be using it on our sites instead of GIF. People have been saying this for years, and by now most of us have stopped listening. Sadly, flaky browser support has made PNG impractical for almost everything; but now, with a few simple workarounds, we can finally put one of its most compelling features to use.
The Portable Network Graphics, or PNG (pronounced “ping”), image format has been around since 1995, having cropped up during the now long-forgotten GIF scare, when Compuserve and Unisys announced they would begin charging royalties for the use of the GIF image format.
To provide GIF support in their applications, software makers like Adobe and Macromedia must pay royalty fees – fees which are passed down to the end user in the selling cost of the software.
When PNG appeared on the scene, web designers were ready to make the switch to the free, superior format and shun GIF forever. But over time, browsers continually failed to support PNG, and eventually most people started to forget about it. Today, nearly everyone still uses GIF habitually.
Which is a shame, because PNG makes GIF look pretty pathetic: it supports gamma correction, (sometimes) smaller file sizes, loss-less compression, up to 48-bit color, and, best of all, true alpha transparency.
To get why alpha transparency is a big deal, we must first understand one of the most annoying limitations of GIF.
When it comes to transparency, GIF doesn't cut it. Whereas PNG supports alpha transparency, GIF only supports binary transparency, which is a big limitation and has a couple of important implications.
For one, a GIF image can either use no transparent colors at all or have one color that's completely transparent – there are no degrees of transparency.
And if a complex GIF does contain a transparent color, the background color of the web page must match the transparent color, or else the anti-aliased area around the transparent color will be surrounded by ugly haloing and fringing. If you've spent more than five minutes as a web designer, you know what I'm talking about.
The result is that any anti-aliased transparent GIF is inextricably tied to the background color of the web page on which it lives. If you ever decide to change that color, you must also change the GIF.
Miraculously, PNG doesn't behave that way. A PNG can be transparent in varying degrees – in other words, it can be of variable opacity. And a transparent PNG is background-independent: it can live on any background color or image. Say you want your navigation on monkeys-run-amuck.com to be 65% opaque so you can see through it to your orangutan background image. You can do that. A transparent anti-aliased “Gorillas, Chimps, Gibbons, et al” title that can sit on top of any background color or image? You can do that, too.
By now, of course, we'd all be up to our ears in PNGs if browsers supported them reliably. But seven years after the format's inception, you still can't slap a PNG onto a web page like you can a GIF or JPG. It's disgraceful, but not as bad as it sounds.
It turns out that most of the latest versions of the major browsers fully support alpha transparency with PNG – namely, Netscape 6, Opera 6, and recently-released Mozilla 1, all on Windows; and, for the Mac, Internet Explorer 5, Netscape 6, Opera 5, Mozilla 1, OmniWeb 3.1, and ICab 1.9. Incredibly, PNG even works on Opera 6 for Linux, on WebTV, and on Sega Dreamcast.
IE5.5+/Win, bless its heart, will, in fact, display a PNG, but it doesn't natively support alpha transparency. In IE5.5+/Win, the transparent area of your PNG will display at 100% opacity – that is, it won't be transparent at all.
Bugger. So what do we do now?
AlphaImageLoader
FilterIE4+/Win supports a variety of non-standard, largely ridiculous visual filters that you can apply to any image's style. You can, for instance, fade in an image with a gradient wipe, or make it stretch from nothing to full size, or even make it swipe into place circularly, like a scene change in Star Wars.
A non-pointless gem among these is the AlphaImageLoader
filter, which is supported in IE5.5+/Win. When used to display a PNG, it allows for full alpha transparency support. All you have to do is this:
<DIV ID="myDiv" STYLE="position:relative; height:250px; width:250px; filter:progid:DXImageTransform.Microsoft.AlphaImageLoader<SPAN class=linewrap>»</SPAN> (src='myimage.png',sizingMethod='scale');"></DIV>
(Line wraps are marked ». –Ed.)
And you're in business. Perfect alpha transparency. This code works great, with only the small drawback that it's not part of any accepted web standard, and no other browser on the planet understands it.
So the trick is to determine the user's browser and serve up the images appropriately: if IE5.5+/Win, then we use AlphaImageLoader
; if a browser with native PNG support, then we display PNGs the normal way; if anything else, then we display alternate GIFs, because we can't be sure that a PNG will display correctly or at all.
Using a slightly tweaked version of Chris Nott's Browser Detect Lite, we set some global variables to this effect that we can use later on.
// if IE5.5+ on Win32, then display PNGs with AlphaImageLoader if ((browser.isIE55 || browser.isIE6up) && browser.isWin32) { var pngAlpha = true; // else, if the browser can display PNGs normally, then do that } else if ((browser.isGecko) |<span class="linewrap">»</span> | (browser.isIE5up && browser.isMac) |<span class="linewrap">»</span> | (browser.isOpera && browser.isWin <span class="linewrap">»</span> && browser.versionMajor >= 6) |<span class="linewrap">»</span> | (browser.isOpera && browser.isUnix <span class="linewrap">»</span> && browser.versionMajor >= 6) |<span class="linewrap">»</span> | (browser.isOpera && browser.isMac <span class="linewrap">»</span> && browser.versionMajor >= 5) |<span class="linewrap">»</span> | (browser.isOmniweb && <span class="linewrap">»</span> browser.versionMinor >= 3.1) |<span class="linewrap">»</span> | (browser.isIcab && <span class="linewrap">»</span> browser.versionMinor >= 1.9) |<span class="linewrap">»</span> | (browser.isWebtv) |<span class="linewrap">»</span> | (browser.isDreamcast)) { var pngNormal = true; }
(Note for the faint of heart: complete source code for all the examples we cover is available at the end of the article.)
The simplest, most reliable way to spit out PNGs is using inline document.writes based on the above detection. So we use a function like this:
function od_displayImage(strId, strPath, intWidth, <span class="linewrap">»</span> intHeight, strClass, strAlt) { if (pngAlpha) { document.write('<div style="height:'+intHeight+'px;<SPAN class=linewrap>»</SPAN> width:'+intWidth+'px;<SPAN class=linewrap>»</SPAN> filter:progid:DXImageTransform.Microsoft.AlphaImageLoader<SPAN class=linewrap>»</SPAN> (src=\''+strPath+'.png\', sizingMethod=\'scale\')" <SPAN class=linewrap>»</SPAN> id="'+strId+'" class="'+strClass+'"></div>'); } else if (pngNormal) { document.write('<img src="'+strPath+'.png" <SPAN class=linewrap>»</SPAN> width="'+intWidth+'"<SPAN class=linewrap>»</SPAN> height="'+intHeight+'" name="'+strId+'" <SPAN class=linewrap>»</SPAN> border="0" class="'+strClass+'" alt="'+strAlt+'" />'); } else { document.write('<img src="'+strPath+'.gif" <SPAN class=linewrap>»</SPAN> width="'+intWidth+'"<SPAN class=linewrap>»</SPAN> height="'+intHeight+'" name="'+strId+'" <SPAN class=linewrap>»</SPAN> border="0" class="'+strClass+'" alt="'+strAlt+'" />'); } }
Now we can call the od_displayImage function from anywhere on the page. Any JavaScript-capable browser will display an image, and, if we want to be really careful, we can accompany each call with a
It's a time-tested method, but what if we want more control over our PNGs?
When I told the programmer in the office next door that I was writing this article, he took one look at my code, glowered at me, and said, “Fool. Where's the abstraction? You need to use objects.”
So now we have a JavaScript object to display PNGs. Here's how we use it:
<html><head> <script language="javascript" src="browserdetect_lite.js" type="text/javascript"> </script> <script language="javascript" src="opacity.js" type="text/javascript"></script> <script type="text/javascript"> var objMyImg = null; function init() { objMyImg = new OpacityObject('myimg','/images/myimage'); objMyImg.setBackground(); } </script> <style type="text/css"> #myimg { background: url('back.png') repeat; position:absolute; left: 10px; top: 10px; width: 200px; height: 200px; } </style> </head> <body onload="init()" background="back.jpg"> <div id="myimg"></div> </body> </html>
That's it. The cool thing about the OpacityObject is that we just pass it a DIV ID and an image path and we're done. Using the appropriate technique, it applies the image as a background of the DIV, and from there we can do whatever we want with it. Fill it with text, move it across the screen, resize it dynamically, whatever – just like any other DIV.
The object works in any CSS 1-capable browser that can dynamically apply a background image to a DIV with JavaScript. It's completely flexible, and we could even use it in place of the above function.
The trade-off is that it doesn't degrade as nicely. Netscape 4.7/Win/Mac and Opera 5/Mac, for instance, won't display an image at all. And it has another significant problem, which is this:
IE5/Mac only supports alpha transparency when the PNG resides in an <img>
tag, not when it's set as the background property of a DIV. So PNGs displayed with the OpacityObject will appear 100% opaque in IE5/Mac. This problem is especially frustrating because IE5/Mac is the only browser which natively supports PNG and behaves this way. We've notified Microsoft about this apparent bug and hope for it to be fixed in an upcoming release.
But for now, these issues are the trade-off for flexibility. Obviously, choose the right tactic based on the particular needs of your project. Between them both, you can do pretty much anything with PNGs – like, for instance...
In this simple example, we see how the same 80% opaque PNG can be displayed on any kind of background: Translucent Image on a Photo.
What a beautiful thing it would be, I'm sure you've thought from time to time, to create translucent anti-aliased images that work on any background. Well, check it out: Anti-Aliased Translucent Navigation with Rollovers.
Mouse over the images, observe the behavior of the rollovers, and click “change background” to see how the images behave on different backgrounds. Then view the source. There are a few things worth noting here:
strExt
, yang mengandungi sama ada ".png" atau ".gif." Selagi PNG dan GIF ganti kami menggunakan nama yang sama kecuali sambungan fail, penyemak imbas hanya akan pramuat imej yang sebenarnya akan digunakan.
pngLink
dan menetapkan sifat kursor kepada "penunjuk". Kami menghantar nama kelas itu kepada fungsi apabila kami memanggilnya, dan fungsi itu menggunakan kelas ke PNG. Hasilnya ialah penuding pengguna bertukar menjadi kursor apabila dia melancarkan pautan imej, walaupun, dalam IE5.5+/Win, ia sebenarnya hanyalah DIV. (Anda juga mungkin mahu menambah "display:block"
atau "display:inline"
pada kelas PNG anda, bergantung pada cara anda menggunakan imej, untuk menjadikannya dipaparkan dengan betul dalam Netscape 6. (Untuk butiran, lihat Better Living Through XHTML .)
AlphaImageLoader
, IE5.5+/Win mempunyai masa yang sukar; ia sangat perlahan, terlalu lambat untuk peralihan yang berkesan. Apa yang berfungsi dengan lebih baik ialah menggunakan warna latar belakang pada DIV yang mengandungi PNG - warna akan bersinar melalui bahagian lutsinar imej dan melakukannya dengan pantas juga. Apabila kami memanggil fungsi tersebut, kami menghantar bersama nama imej yang akan dipaparkan dan warna HTML - IE5.5+/Win akan memaparkan warna, dan yang lain akan memaparkan imej.
Dalam dua contoh pertama, kami menggunakan fungsi cepat dan kotor daripada taktik satu. Sekarang, kami mahu PNG kami berinteraksi dengan kod lain pada halaman, jadi kali ini kami memaparkannya dengan OpacityObject.
Tetapi ingat – terdapat kelemahan pada pendekatan ini (lihat di atas), yang paling menyayat hati ialah contoh ini tidak berfungsi dengan sempurna pada IE5/Mac. Jika itu menyebabkan anda sakit, maka sentiasa ada fungsi cepat dan kotor. Jika tidak, baca terus.
Mula-mula kami mencipta DIV, memberikannya ID dan menetapkan sebarang sifat gaya yang kami mahu kepadanya – ketinggian, lebar, keluarga fon, dsb.
Kemudian kami menyampaikan ID DIV itu apabila kami membuat instantiat OpacityObject. Kami juga melalui laluan imej, dan kini kami mempunyai DIV dengan latar belakang lut sinar. Sejuk!
Seterusnya kami meletakkan beberapa teks HTML dalam DIV dan menggunakan kaedah objek lain yang tidak berkaitan padanya (objek ini tidak ada kena mengena dengan OpacityObject – ia boleh jadi sebarang kod yang anda ada). Sekarang kita boleh menggerakkan DIV lut sinar di sekeliling skrin. Wheee! DIV Lutsinar Terapung dengan Teks HTML Di Dalam.
Jadi, terdapat gambaran tentang perkara yang mungkin dengan OpacityObject. Anda CSS/DOM tegar, gila.
Muat turun kod sumber untuk objek, fungsi dan contoh yang kami bincangkan. Semua kod tersebut bergantung pada versi penyemak imbas kami yang telah diubah suai, yang turut disertakan. Kod Sumber Kelegapan Pembolehubah.
Ini semua sangat mengujakan, tetapi, seperti banyak pencapaian yang menggembirakan pembangun web, membuat PNG berfungsi dalam penyemak imbas hari ini tidak sepatutnya sesukar ini. Anda mungkin mempertimbangkan untuk menandatangani petisyen untuk memujuk Microsoft supaya memberikan sokongan PNG penuh dalam Internet Explorer. Jika bernasib baik, artikel ini akan menjadi usang tidak lama lagi.
Sementara itu, siarkan sebarang idea untuk penambahbaikan pada kod ini dalam forum perbincangan untuk artikel ini. Tapak utama PNG, misalnya, bercakap tentang beberapa penyemak imbas kabur lain yang sepatutnya menyokong ketelusan alfa, tetapi masih belum disahkan. Jika anda boleh mengesahkan mana-mana tuntutan ini, atau mempunyai sebarang input berharga lain, beritahu kami dan kami akan mengemas kini kod tersebut dengan sewajarnya.
AlphaImageLoader
Tapis halaman pada MSDN
<img>
, bukan jika ia imej latar belakang CSS