แก้ปัญหารูปโหลดจากเน็ตของ Masonry

จาก workshop ที่แล้ว ทำเว็บสไตล์ Pinterest ง่าย ๆ ด้วย Masonry หากใครที่ลองไป copy url รูปจากอินเทอร์เน็ตมาใส่แทน แล้วลองเปิดเว็บดู จะพบว่า Masonry มีปัญหาการแสดงผล grid ที่เพี้ยน คือ รูปมันจะมาซ้อน ๆ กัน ทำให้การเรียงรูปใน grid ดูไม่สวยงาม บทความนี้ผมจะมาวิเคราะห์ปัญหา รวมถึงเสนอวิธีแก้ปัญหานี้ครับ

วิเคราะห์ปัญหา

สาเหตุที่รูปซ้อนกันนั้น เกิดจากการโหลดรูปจากอินเทอร์เน็ตมาแสดงนั้นต้องอาศัยระยะเวลา ยิ่งรูปมีขนาดใหญ่ก็ยิ่งใช้เวลามาก ดังนั้นเมื่อรูปยังโหลดมาไม่เสร็จ grid ของ Masonry มันไม่รู้ขนาด width และ height ของรูปครับ ทำให้การคำนวณเพื่อแสดงผลของ grid แต่ละตัวจะอิงกับค่า default ของ library ผลก็เลยเป็นอย่างที่เห็น grid ทุกตัวมีขนาดเท่ากันหมด พอรูปโหลดเสร็จรูปก็จะซ้อนกันตามขนาด grid เริ่มต้นนั่นเอง

วิธีแก้ปัญหา

วิธีแก้ปัญหานี้ คือ รอให้รูปโหลดเสร็จก่อนเพื่อให้ Masonry รู้ขนาดของรูปแล้วค่อยแสดงผล ง่ายไหมครับ ทีนี้เราจะรู้ได้อย่างไรว่ารูปจากเน็ตนั้นโหลดเสร็จแล้ว งานนี้ต้องหาตัวช่วยครับ คือ imagesloaded.pkgd.min.js ซึ่งเป็น js library ที่ช่วยให้บอกเราได้ว่ารูปโหลดเสร็จแล้วนะ

การใช้งาน imagesloaded.pkgd.min.js ต้องใช้งานร่วมกับ jquery ด้วยนะ เพราะฉะนั้นอย่าลืมไปโหลด jquery.min.js มาใส่ใน Folder เว็บของคุณด้วย เอาล่ะถ้าพร้อมแล้ว เรามาดู code กันเลย

<!DOCTYPE html>
<html lang="en">
<head>
 <meta charset="utf-8">
 <style type="text/css">
 body {
 background: #EEE;
 }

 img {
 margin-bottom: 10px;
 max-width: 200px;
 }

 .item {
 width: 200px;
 margin: 10px;
 padding: 10px;
 background: #FFF;
 }
 </style>
</head>
<body>
 <div id="container">
    <div class="item"><img src="http://ericulous.com/ipin/files/2012/07/Blog-26jul_Tiongbahru_Cafe1-300x300.jpg">Title</div>
    <div class="item"><img src="http://ericulous.com/ipin/files/2012/08/Screen-Shot-2012-07-11-at-11.01.52-PM-285x300.png">Title</div>
    <div class="item"><img src="http://ericulous.com/ipin/files/2012/07/rilakkumastickymemo-298x300.jpg">Title</div>
    <div class="item"><img src="http://ericulous.com/ipin/files/2012/07/7613167088_eb094c5a6f1-300x300.jpg">Title</div>
    <div class="item"><img src="http://ericulous.com/ipin/files/2012/08/011-edited-200x300.jpg">Title</div>
    <div class="item"><img src="http://ericulous.com/ipin/files/2012/07/tangledtop-black21-300x168.jpg">Title</div>
    <div class="item"><img src="http://ericulous.com/ipin/files/2012/08/il_570xN.3034563661-300x300.jpg">Title</div>
    <div class="item"><img src="http://ericulous.com/ipin/files/2012/08/Babyliss-32mm-hair-300x286.jpg">Title</div>
    <div class="item"><img src="http://ericulous.com/ipin/files/2012/08/487930_385777741476466_116782895_n1-300x300.jpg">Title</div>
    <div class="item"><img src="http://ericulous.com/ipin/files/2012/08/camera-fashion-hair-photography-polaroid-Favim.com-1299271-300x199.jpg">Title</div>
    <div class="item"><img src="http://ericulous.com/ipin/files/2012/07/P10901301-300x199.jpg">Title</div>
    <div class="item"><img src="http://ericulous.com/ipin/files/2012/07/mayuki291-300x225.jpg">Title</div>
 </div>
 <script src="jquery.min.js"></script>
 <script src="masonry.pkgd.min.js"></script>
 <script src="imagesloaded.pkgd.min.js"></script>
 <script type="text/javascript">
 $(document).ready(function() {
    var $items = $('.item');
    var $container = $('#container'); 
    $items.hide();
    $container.imagesLoaded(function(){
       $items.fadeIn();
       $container.masonry({
          columnWidth: 240,
          itemSelector: '.item'
       });
    });
 });//end document ready
 </script>
</body>
</html>

อธิบายการทำงานของ Code

code ส่วน css และ html เหมือนเดิมเปลี่ยนแค่ url รูปของแต่ละ item มาใช้รูปจากอินเทอร์เน็ตแทน ส่วนที่ต้องมาโฟกัสจริง ๆ คือ code javascript ครับ

 <script src="jquery.min.js"></script> <!--include jquery-->
 <script src="masonry.pkgd.min.js"></script> <!--include masonry-->
 <script src="imagesloaded.pkgd.min.js"></script> <!--include imageloaded-->
 <script type="text/javascript">
 $(document).ready(function() {
    var $items = $('.item'); //get element ทุกตัวที่ใช้ css class item
    var $container = $('#container'); //get element ตัว container

    $items.hide(); //ซ่อน element item ไว้ก่อน ยังไม่ให้แสดงเพราะรูปยังโหลดไม่เสร็จ
    $container.imagesLoaded(function(){ //ตรงนี้ใช้เช็คและทำงานเมื่อรูปโหลดเสร็จ
       $items.fadeIn(); //element item ค่อย fade แสดงขึ้นมา
       $container.masonry({ //ตัวแปร container เรียกใช้ instance masonry
          columnWidth: 240,
          itemSelector: '.item'
       });
    });
 });//end document ready
 </script>

สรุป

ปัญหารูปซ้อนกันเกิดจากรูปที่ยังโหลดไม่เสร็จ Masonry ไม่รู้ขนาดรูปเลยแสดงผล grid ด้วยค่าเริ่มต้น การแก้ไขปัญหาทำได้โดยใช้ js library ที่ชื่อ imagesloaded.pkgd.min.js ทั้งนี้ต้องใช้งานร่วมกัน jquery ซึ่งแน่นอนว่าวิธีการแก้ปัญหานี้สามารถนำไปประยุกต์ใช้งานกับ web application ได้จริงเพราะเวลาที่เราต้องเอารูปมาแสดง เรา query รูปมาจาก server ผ่านอินเทอร์เน็ตครับ

สำหรับใครที่ต้องการตัวอย่าง workshop ในบทความนี้สามารถดาวน์โหลดกันได้ที่ Google Drive

ปล. หวังว่าบทความนี้จะเป็นประโยชน์แก่นักพัฒนาเว็บที่ต้องการนำ Masonry ไปต่อยอดทำ Web Application ตามที่ต้องการนะครับ โดยส่วนตัวผมได้ลองเอาไปเล่นกับรูปที่ query มาจาก Instagram ซึ่งก็สนุกสนานทีเดียวใครอยากดูตัวอย่างไปดูได้ที่ http://bit.ly/1K6qh7v

Comments

comments