[javascript] viewer.js 를 이용하여 이미지 뷰어 만들기(feat. Masonry Layout)

관련지식
javascript, gallery, masonry layout

지금까지 갤러리 기능을 구현해주는 다양한 종류의 이미지 관련 라이브러리가 있었지만 viwer.js 는 그 중에서도 가장 완벽한 기능을 제공하고 있다고 생각합니다. 사용하기 간단하고 매우 강력한 기능을 제공합니다. 이전에 작업했던 Masonry Layout 소스에 viewer.js를 붙여 갤러리 기능을 완성해보겠습니다.

테스트 소스

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <title></title>
  5. <meta charset="utf-8">
  6. <meta name="viewport" content="width=device-width, initial-scale=1">
  7. <script src="https://unpkg.com/masonry-layout@4/dist/masonry.pkgd.min.js"></script>
  8. <script src="https://unpkg.com/imagesloaded@4/imagesloaded.pkgd.min.js"></script>
  9. <style>
  10. .grid { width:80% }
  11. .grid-sizer, .grid-item { width: 23%; }
  12. .grid-item {border:1px solid #8f99f3; margin-bottom:15px; border-radius: 20px; overflow:hidden;}
  13. .grid-item img {display: block; min-width:100%; max-width: 100%; }
  14. </style>
  15. </head>
  16. <body>
  17. <div class="grid">
  18. <div class="grid-sizer"></div>
  19. <div class="grid-item"><img src="https://i.postimg.cc/gkQkrcf3/4.png" /></div>
  20. <div class="grid-item"><img src="https://i.postimg.cc/zvyyDzqP/5.png" /></div>
  21. <div class="grid-item"><img src="https://i.postimg.cc/BZz3Jy9S/custom1.png" /></div>
  22. <div class="grid-item"><img src="https://i.postimg.cc/XYTgXByp/calendar1.png" /></div>
  23. <div class="grid-item"><img src="https://i.postimg.cc/T1jKdLr8/design2.png" /></div>
  24. <div class="grid-item"><img src="https://i.postimg.cc/Hs2pxzXv/button1.png" /></div>
  25. <div class="grid-item"><img src="https://i.postimg.cc/xTwC6ChB/part2.png" /></div>
  26. <div class="grid-item"><img src="https://i.postimg.cc/9Qc7S16s/canvas1.png" /></div>
  27. <div class="grid-item"><img src="https://i.postimg.cc/ncT3N5BM/extapp1.png" /></div>
  28. <div class="grid-item"><img src="https://i.postimg.cc/D0XZbr4D/scrolllock1.jpg" /></div>
  29. <div class="grid-item"><img src="https://i.postimg.cc/NFdy16gk/smsbackup3.jpg" /></div>
  30. <div class="grid-item"><img src="https://i.postimg.cc/vHc0VhBL/cisco3.jpg" /></div>
  31. </div>
  32. <script>
  33. var msnry = new Masonry( '.grid', {
  34. itemSelector: '.grid-item',
  35. columnWidth: '.grid-sizer',
  36. percentPosition: true,
  37. gutter : 20,
  38. });
  39. imagesLoaded( '.grid' ).on( 'progress', function() {
  40. msnry.layout();
  41. });
  42. </script>
  43. </body>
  44. </html>

api : https://github.com/fengyuanchen/viewerjs
데모 : https://fengyuanchen.github.io/viewerjs/
설치 : npm install viewerjs 또는 CDN 다운로드
버전 : 1.3.7

기본 적용

먼저 CSS와 스크립트를 로딩해야 합니다.

  1. <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/viewerjs/1.3.7/viewer.min.css" />
  2. <script src="https://cdnjs.cloudflare.com/ajax/libs/viewerjs/1.3.7/viewer.min.js"></script>

그리고 함수를 호출하면 적용됩니다. Masonry 라이브러리와 사용법이 비슷한데 다른점은, Viewer를 생성할때 Selector String은 사용할수 없다는것입니다. 만드시 DOM Element 를 넘겨줘야 합니다.

  1. var viewer = new Viewer(document.querySelector('.grid'), {
  2. });

이제 테스트를 하면 됩니다. 화면상에 바뀐것이 없어보이지만 이미지를 클릭하면 아래와 같이 뷰어가 보일 것입니다.

여러 옵션으로 기능을 활성화/비활성화 할수 있으므로 화면의 구성요소와 기능을 확인해두는것이 좋습니다.

이미지의 우측 상단에는 닫기 버튼이 있습니다만 라이브러리에서 옵션으로 사용되는 명칭은 그냥 button 입니다. 그리고 닫기 버튼을 클릭하지 않고 이미지 주변의 음영 영역을 클릭해도 이미지는 닫힙니다. 키보드의 좌우 커서로 다음 이미지를 볼 수도 있습니다.

이미지의 하단에는 이미지 타이틀, 툴바, 네비게이션바가 있습니다. 툴바 안에는 이미지 재생 버튼외에 확대/축소, 회전, 상하좌우반전 기능을 하는 버튼들이 있습니다. 디폴트 설정은 모두 활성화 상태입니다.

싱글 이미지 뷰어 형태로 적용

만약 단순이미지 뷰어 형태, 클릭한 이미지만 크게 보이도록 하겠다면 toolbar, navbar 등은 별로 필요하지 않을것 같습니다. 아래와 같이 옵션을 추가합니다.

  1. var viewer = new Viewer(document.querySelector('.grid'), {
  2. navbar : false,
  3. toolbar : false
  4. });

타이틀이 파일명으로 보입니다. 타이틀명에 다른 텍스트를 넣고 싶다면 이미지 태그에 alt 속성을 넣어주면 됩니다. 첫번째 이미지에만 alt 속성을 추가해보겠습니다.

  1. <div class="grid-item"><img src="https://i.postimg.cc/gkQkrcf3/4.png" alt="파일이름 보이기 싫어"/></div>

타이틀 커스텀

장차법에서는 alt 속성에 이미지에 대한 설명을 입력하도록 가이드하고 있습니다. 만약 가이드대로 텍스트를 넣는다면 타이틀에 생뚱맞은 텍스트가 보이게 될 것입니다. 따라서 alt 태그는 이미지의 대체 텍스트를 입력하고 title 속성값을 이미지의 하단에 보이도록 수정하겠습니다.

  1. <div class="grid-item"><img src="https://i.postimg.cc/gkQkrcf3/4.png" title="이것이 진짜 타이틀" alt="파일이름 보이기 싫어"/></div>
  1. var viewer = new Viewer(document.querySelector('.grid'), {
  2. navbar : false,
  3. toolbar : false,
  4. title : function(image, imageData) { return image.title; } //추가
  5. });

하지만 저렇게하면 타이틀엔 아무것도 보이지 않게 됩니다. 콜백함수에 변수 image 로 넘어는 HtmlElement는 원본 이미지가 아니라, 뷰어에서 보이게 될 클론 이미지 입니다. 클론 이미지에 title 속성이 복제가 안되었기 때문에 직접 복제해주어야 합니다.

  1. var viewer = new Viewer(document.querySelector('.grid'), {
  2. navbar : false,
  3. toolbar : false,
  4. title : function(image, imageData) { return image.title; },
  5. view(event) {
  6. event.detail.image.title = event.detail.originalImage.title; //추가
  7. }
  8. });

이젠 title 속성에 있는값이 잘 보일 것입니다.

썸네일 적용

원본 이미지가 아무리 고화질에 큰 해상도를 가지고 있더라도 갤러리의 리스트 화면에서 필요한 이미지는 썸네일 사이즈의 작은 이미지면 충분합니다. 용량이 작은만큼 로딩속도도 빠르겠죠. viewer.js는 썸네일 기능을 지원합니다. <img> 태그의 src 속성에는 썸네일 url을 입력하고 실제 url은 다른 속성으로 정의하면 됩니다.

먼저 html 을 아래와 같이 수정하겠습니다. 썸네일엔 구글 로고 url을 사용하겠습니다. 구글 정도의 회사가 로고를 핫링크 걸었다고 화내진 않겠죠? 원래 src에 있는 url은 data-original로 바꿨습니다.

  1. <div class="grid-item"><img src="https://www.google.com/images/branding/googlelogo/2x/googlelogo_color_92x30dp.png" data-original="https://i.postimg.cc/gkQkrcf3/4.png" title="이것이 진짜 타이틀" alt="파일이름 보이기 싫어"/></div>
  2. <div class="grid-item"><img src="https://www.google.com/images/branding/googlelogo/2x/googlelogo_color_92x30dp.png" data-original="https://i.postimg.cc/zvyyDzqP/5.png"/></div>
  3. <div class="grid-item"><img src="https://www.google.com/images/branding/googlelogo/2x/googlelogo_color_92x30dp.png" data-original="https://i.postimg.cc/BZz3Jy9S/custom1.png" /></div>
  4. <div class="grid-item"><img src="https://www.google.com/images/branding/googlelogo/2x/googlelogo_color_92x30dp.png" data-original="https://i.postimg.cc/XYTgXByp/calendar1.png" /></div>
  5. <div class="grid-item"><img src="https://www.google.com/images/branding/googlelogo/2x/googlelogo_color_92x30dp.png" data-original="https://i.postimg.cc/T1jKdLr8/design2.png" /></div>
  6. <div class="grid-item"><img src="https://www.google.com/images/branding/googlelogo/2x/googlelogo_color_92x30dp.png" data-original="https://i.postimg.cc/Hs2pxzXv/button1.png" /></div>
  7. <div class="grid-item"><img src="https://www.google.com/images/branding/googlelogo/2x/googlelogo_color_92x30dp.png" data-original="https://i.postimg.cc/xTwC6ChB/part2.png" /></div>
  8. <div class="grid-item"><img src="https://www.google.com/images/branding/googlelogo/2x/googlelogo_color_92x30dp.png" data-original="https://i.postimg.cc/9Qc7S16s/canvas1.png" /></div>
  9. <div class="grid-item"><img src="https://www.google.com/images/branding/googlelogo/2x/googlelogo_color_92x30dp.png" data-original="https://i.postimg.cc/ncT3N5BM/extapp1.png" /></div>
  10. <div class="grid-item"><img src="https://www.google.com/images/branding/googlelogo/2x/googlelogo_color_92x30dp.png" data-original="https://i.postimg.cc/D0XZbr4D/scrolllock1.jpg" /></div>
  11. <div class="grid-item"><img src="https://www.google.com/images/branding/googlelogo/2x/googlelogo_color_92x30dp.png" data-original="https://i.postimg.cc/NFdy16gk/smsbackup3.jpg" /></div>
  12. <div class="grid-item"><img src="https://www.google.com/images/branding/googlelogo/2x/googlelogo_color_92x30dp.png" data-original="https://i.postimg.cc/vHc0VhBL/cisco3.jpg" /></div>

그리고 url 옵션을 하나 추가합니다.

  1. toolbar : false,
  2. url: 'data-original', //추가
  3. title : function(image, imageData) { return image.title; },

이제 테스트를 하면 됩니다. 모든 이미지가 구글 로고로 바뀌었지만 클릭을 하면 원래의 이미지가 로딩되네요.

정리

viewer.js는 큰 화면의 브라우저뿐 아니라 모바일 브라우저에서도 완벽하게 동작합니다. 적용이 매우 간단할 뿐더러 기능도 좋으므로, 갤러리 기능을 구현할땐 꼭 사용해보는것을 추천합니다.
여기서 언급한것 외에 다른 기능들도 많으므로 데모 홈페이지에서 기능을 확인해보시기 바랍니다.

최종 소스

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <title></title>
  5. <meta charset="utf-8">
  6. <meta name="viewport" content="width=device-width, initial-scale=1">
  7. <script src="https://unpkg.com/masonry-layout@4/dist/masonry.pkgd.min.js"></script>
  8. <script src="https://unpkg.com/imagesloaded@4/imagesloaded.pkgd.min.js"></script>
  9. <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/viewerjs/1.3.7/viewer.min.css" />
  10. <script src="https://cdnjs.cloudflare.com/ajax/libs/viewerjs/1.3.7/viewer.min.js"></script>
  11. <style>
  12. .grid { width:80% }
  13. .grid-sizer, .grid-item { width: 23%; }
  14. .grid-item {border:1px solid #8f99f3; margin-bottom:15px; border-radius: 20px; overflow:hidden;}
  15. .grid-item img {display: block; min-width:100%; max-width: 100%; }
  16. </style>
  17. </head>
  18. <body>
  19. <div class="grid">
  20. <div class="grid-sizer"></div>
  21. <div class="grid-item"><img src="https://www.google.com/images/branding/googlelogo/2x/googlelogo_color_92x30dp.png" data-original="https://i.postimg.cc/gkQkrcf3/4.png" title="이것이 진짜 타이틀" alt="파일이름 보이기 싫어"/></div>
  22. <div class="grid-item"><img src="https://www.google.com/images/branding/googlelogo/2x/googlelogo_color_92x30dp.png" data-original="https://i.postimg.cc/zvyyDzqP/5.png"/></div>
  23. <div class="grid-item"><img src="https://www.google.com/images/branding/googlelogo/2x/googlelogo_color_92x30dp.png" data-original="https://i.postimg.cc/BZz3Jy9S/custom1.png" /></div>
  24. <div class="grid-item"><img src="https://www.google.com/images/branding/googlelogo/2x/googlelogo_color_92x30dp.png" data-original="https://i.postimg.cc/XYTgXByp/calendar1.png" /></div>
  25. <div class="grid-item"><img src="https://www.google.com/images/branding/googlelogo/2x/googlelogo_color_92x30dp.png" data-original="https://i.postimg.cc/T1jKdLr8/design2.png" /></div>
  26. <div class="grid-item"><img src="https://www.google.com/images/branding/googlelogo/2x/googlelogo_color_92x30dp.png" data-original="https://i.postimg.cc/Hs2pxzXv/button1.png" /></div>
  27. <div class="grid-item"><img src="https://www.google.com/images/branding/googlelogo/2x/googlelogo_color_92x30dp.png" data-original="https://i.postimg.cc/xTwC6ChB/part2.png" /></div>
  28. <div class="grid-item"><img src="https://www.google.com/images/branding/googlelogo/2x/googlelogo_color_92x30dp.png" data-original="https://i.postimg.cc/9Qc7S16s/canvas1.png" /></div>
  29. <div class="grid-item"><img src="https://www.google.com/images/branding/googlelogo/2x/googlelogo_color_92x30dp.png" data-original="https://i.postimg.cc/ncT3N5BM/extapp1.png" /></div>
  30. <div class="grid-item"><img src="https://www.google.com/images/branding/googlelogo/2x/googlelogo_color_92x30dp.png" data-original="https://i.postimg.cc/D0XZbr4D/scrolllock1.jpg" /></div>
  31. <div class="grid-item"><img src="https://www.google.com/images/branding/googlelogo/2x/googlelogo_color_92x30dp.png" data-original="https://i.postimg.cc/NFdy16gk/smsbackup3.jpg" /></div>
  32. <div class="grid-item"><img src="https://www.google.com/images/branding/googlelogo/2x/googlelogo_color_92x30dp.png" data-original="https://i.postimg.cc/vHc0VhBL/cisco3.jpg" /></div>
  33. </div>
  34. <script>
  35. var msnry = new Masonry( '.grid', {
  36. itemSelector: '.grid-item',
  37. columnWidth: '.grid-sizer',
  38. percentPosition: true,
  39. gutter : 20,
  40. });
  41. imagesLoaded( '.grid' ).on( 'progress', function() {
  42. msnry.layout();
  43. });
  44. var viewer = new Viewer(document.querySelector('.grid'), {
  45. navbar : false,
  46. toolbar : false,
  47. url: 'data-original',
  48. title : function(image, imageData) { return image.title; },
  49. view(event) {
  50. event.detail.image.title = event.detail.originalImage.title;
  51. },
  52. });
  53. </script>
  54. </body>
  55. </html>