image_viewer
last update:
2020/12/19
image viewer
<!doctype html> <title>image_viewer</title> <style> html, body { height: 100%; background: #f1f1f1; } body { margin: 0; padding: 8px 10px 10px; box-sizing: border-box; } .u { display: flex; align-items: flex-end; width: 100%; padding-top: 2px; font-size: 12px; } .u span + span { margin-left: 4px; } .u span + span::before { content: ' --- '; } .u input[type=text] { width: 7px; height: 12px; margin: 0 -2px 0 1px; font-size: 12px; background: #f1f1f1; border: none; outline: 0; } .u input[type=checkbox] { margin: 0; width: 12px; height: 12px; position: relative; top: 2px; } .b { display: flex; justify-content: center; align-items: center; width: 100%; height: 30%; margin-top: 10px; border: #000 1px solid; background: #fff; } .i { display: flex; flex-wrap: wrap; } .i img { height: 80px; margin: 6px 6px 0 0; background-image: repeating-linear-gradient( 180deg, #fff , #fff 1px, transparent 1px, transparent 11px ), repeating-linear-gradient( 90deg, #fff , #fff 1px, #f1f1f1 1px, #f1f1f1 11px ); } .p { position: fixed; top: 0; left: 0; width: 100%; height: 100%; display: none; justify-content: center; align-items: center; background-image: repeating-linear-gradient( 180deg, #fff , #fff 1px, transparent 1px, transparent 11px ), repeating-linear-gradient( 90deg, #fff , #fff 1px, #f1f1f1 1px, #f1f1f1 11px ); } .p.slideShow { background: #000; } .p.cover img { object-fit: cover; width: 100% !important; height: 100% !important; } .p img { max-width: 100%; max-height: 100%; } .p img:hover { border: #f93 1px solid; } .p.slideShow img:hover { border: none; } .p .fadeIn { animation: fadeIn 1s; animation-fill-mode: forwards; } .p .fadeOut { animation: fadeOut 1s; animation-fill-mode: forwards; } @keyframes fadeIn { 0% { opacity: 0; } 100% { opacity: 1; } } @keyframes fadeOut { 0% { opacity: 1; } 100% { opacity: 0; } } </style> <div class='u'> <span>image viewer</span> <span>click - zoom in, out / left - prev / right - next / shift - zoom out / [ - slideshow ( interval<input type="text" value="2">s ) / ] - slideshow off / return - reset / object-fit cover <input type="checkbox"></span> </div> <div class="b">drop imgs</div> <div class="i"></div> <div class="p"></div> <script> const u = document.querySelector('.u'); // usage const b = document.querySelector('.b'); // dropbox const i = document.querySelector('.i'); // imagelist const p = document.querySelector('.p'); // popup const img = document.querySelector('.p img'); // image in popup const files = []; const interval = u.querySelector('input[type=text]'); let intervalSlideShow = interval.value * 1000; interval.addEventListener('change', () => { intervalSlideShow = interval.value * 1000; }); interval.addEventListener('focus', () => { interval.value = ''; }); const cover = u.querySelector('input[type=checkbox]'); cover.addEventListener('change', () => { if (cover.checked) { p.classList.add('cover'); } else { p.classList.remove('cover'); } }); const makeHash = (file) => { const extention = file.name.slice((file.name.lastIndexOf('.') + 1)); const size = file.tag.naturalWidth + 'x' + file.tag.naturalHeight; const name = file.name.substring(0, file.name.lastIndexOf('.')); location.hash = '(' + extention + ',' + size + ')' + name; }; const nullHash = () => { location.hash = null; }; const closeLayer = () => { p.style.display = 'none'; p.classList.remove('slideShow'); p.innerHTML = ''; nullHash(); }; const openLayer = (img) => { p.style.display = 'flex'; p.appendChild(img); }; const callImg = (num) => { return files[num]['tag']; }; const cloneImg = (num) => { return callImg(num).cloneNode(true); }; const recordFilesData = (ev) => { if (!/^image/.test(ev.dataTransfer.files[0].type)) { return false; }; Object.values(ev.dataTransfer.files).forEach((file) => { files.push(file); }); }; const addEventListenerImg = (img, clone, file) => { img.addEventListener('click', (el) => { openLayer(clone); if (clone.naturalWidth < clone.naturalHeight) { clone.style.width = 'auto'; } else { clone.style.height = 'auto'; }; makeHash(file); }); }; const renderImgList = (ev) => { files.forEach((file, index) => { if (index < document.querySelectorAll('img').length) { return false; } const reader = new FileReader(); reader.onload = function (e) { const img = document.createElement('img'); img.src = e.target.result; img.style.order = index; files[index]['tag'] = img; i.appendChild(img); addEventListenerImg(img, cloneImg(index), file); } reader.readAsDataURL(file); }); }; const renderSlideClone = (num) => { const clone = cloneImg(num); clone.classList.add('fadeIn'); openLayer(clone); makeHash(files[num]); }; const renderSlideClonePrev = (num) => { const clone = cloneImg(num); clone.classList.add('fadeOut'); clone.style.position = 'absolute'; p.appendChild(clone); }; let slideId; const clearSlide = (ev, id) => { if (ev.keyCode !== 219) { closeLayer(); } clearInterval(slideId); }; const slideShow = (order) => { if (files.length === 0) { return false; } const backgroundBlack = () => { p.classList.add('slideShow'); }; // first time p.style.display = 'flex'; p.appendChild(cloneImg(order)); backgroundBlack(); let num; if (order === files.length - 1) { num = 0; } else { num = Number(order) + 1; } const id = setInterval(() => { slideId = id; const rewind = () => { num = 0; }; closeLayer(); backgroundBlack(); if (num === files.length) { rewind(); } renderSlideClone(num); if (num === 0) { renderSlideClonePrev(files.length - 1); } else { renderSlideClonePrev(num - 1); } num++; if (num === files.length) { rewind(); } document.addEventListener('keydown', (ev) => { if (ev.keyCode === 221 || ev.keyCode === 16) { clearSlide(ev, id); } }); p.addEventListener('click', (ev) => { clearSlide(ev, id); }); }, intervalSlideShow); }; b.addEventListener('dragover', (ev) => { ev.preventDefault(); }); b.addEventListener('drop', (ev) => { ev.preventDefault(); recordFilesData(ev); renderImgList(ev); }); p.addEventListener('click', () => { closeLayer(); }); document.addEventListener('keydown', (ev) => { const isSlideShow = p.classList.contains('slideShow'); // reset app - return if (ev.keyCode === 13) { nullHash(); location.reload(); }; // slideshow if (ev.keyCode === 219 && !isSlideShow) { let order; if (p.style.display === 'flex') { order = p.querySelector('img').style.order; } else { order = 0; } p.innerHTML = ''; slideShow(order); } if (p.style.display !== 'flex') { return false; } // close layer - shift if (ev.keyCode === 16) { closeLayer(); return false; }; // switch img - left or right const currentImg = p.querySelector('img'); const num = Number(currentImg.style.order); const switchImg = (targetNum) => { const clone = cloneImg(targetNum); p.removeChild(currentImg); p.appendChild(clone); makeHash(files[targetNum]); }; const switchPreview = (ev) => { clearSlide(ev, slideId); openLayer(cloneImg(num)); }; if (ev.keyCode === 37) { if (isSlideShow) { switchPreview(ev); return false } if (num === 0) { switchImg(files.length - 1); return false; } switchImg(num - 1); } if (ev.keyCode === 39) { if (isSlideShow) { switchPreview(ev); return false } if (num === files.length - 1) { switchImg(0); return false; } switchImg(num + 1); } }); </script> code_popup