あああああ
ダウンロード
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>高速画像ビューアー(2画面対応)</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Segoe UI', Meiryo, sans-serif;
background: #f5f5f5;
padding: 20px;
}
.main-container {
max-width: 1900px;
margin: 0 auto;
}
.header {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
padding: 20px 30px;
border-radius: 8px 8px 0 0;
margin-bottom: 20px;
}
.header h1 {
font-size: 24px;
margin-bottom: 5px;
}
.panels-container {
display: flex;
gap: 20px;
flex-wrap: wrap;
}
.panel {
flex: 1;
min-width: 600px;
background: white;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
overflow: hidden;
}
.panel.single-mode {
min-width: 100%;
}
.panel-header {
background: #f8f9fa;
padding: 15px 20px;
border-bottom: 2px solid #e0e0e0;
font-weight: bold;
color: #333;
display: flex;
justify-content: space-between;
align-items: center;
}
.panel-number {
background: #667eea;
color: white;
padding: 5px 15px;
border-radius: 20px;
font-size: 14px;
}
.controls {
background: #fafafa;
padding: 15px 20px;
border-bottom: 1px solid #e0e0e0;
}
.control-group {
margin-bottom: 12px;
display: flex;
align-items: center;
gap: 10px;
flex-wrap: wrap;
}
.control-group label {
font-weight: 600;
color: #333;
min-width: 80px;
font-size: 13px;
}
.control-group input[type="file"] {
display: none;
}
.btn {
padding: 8px 16px;
background: #667eea;
color: white;
border: none;
border-radius: 5px;
cursor: pointer;
font-size: 13px;
transition: all 0.3s;
}
.btn:hover {
background: #5568d3;
transform: translateY(-1px);
box-shadow: 0 2px 5px rgba(0,0,0,0.2);
}
.btn-sm {
padding: 5px 10px;
font-size: 12px;
}
.slider-group {
display: flex;
align-items: center;
gap: 8px;
}
.slider-group input[type="range"] {
width: 150px;
}
.slider-group input[type="number"] {
width: 70px;
padding: 4px 8px;
border: 1px solid #ddd;
border-radius: 5px;
font-size: 13px;
}
.slider-group span {
color: #666;
font-size: 13px;
}
.info-bar {
background: #e3f2fd;
padding: 10px 20px;
border-bottom: 1px solid #90caf9;
display: flex;
justify-content: space-between;
align-items: center;
}
.info-bar .path {
color: #1976d2;
font-size: 12px;
word-break: break-all;
flex: 1;
}
.info-bar .count {
color: #666;
font-weight: 600;
font-size: 13px;
white-space: nowrap;
margin-left: 15px;
}
.gallery {
padding: 20px;
display: flex;
flex-wrap: wrap;
gap: 10px;
align-content: flex-start;
min-height: 400px;
}
.image-wrapper {
display: flex;
flex-direction: column;
align-items: center;
}
.image-item {
cursor: pointer;
transition: all 0.2s;
border: 2px solid transparent;
border-radius: 4px;
overflow: hidden;
position: relative;
}
.image-item:hover {
border-color: #667eea;
transform: scale(1.02);
box-shadow: 0 4px 12px rgba(102, 126, 234, 0.3);
}
.image-item img {
display: block;
width: 100%;
height: 100%;
object-fit: cover;
}
.copy-btn {
margin-top: 5px;
padding: 4px 10px;
background: #28a745;
color: white;
border: none;
border-radius: 3px;
cursor: pointer;
font-size: 11px;
transition: all 0.2s;
}
.copy-btn:hover {
background: #218838;
}
.copy-btn.copied {
background: #17a2b8;
}
.loading {
text-align: center;
padding: 50px;
color: #666;
font-size: 16px;
}
.empty-state {
text-align: center;
padding: 80px 20px;
color: #999;
width: 100%;
}
.empty-state .icon {
font-size: 60px;
margin-bottom: 15px;
}
.empty-state p {
font-size: 15px;
margin-bottom: 8px;
}
.modal {
display: none;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0,0,0,0.9);
z-index: 1000;
justify-content: center;
align-items: center;
}
.modal.active {
display: flex;
}
.modal img {
max-width: 95%;
max-height: 95%;
object-fit: contain;
}
.modal-close {
position: absolute;
top: 20px;
right: 30px;
color: white;
font-size: 40px;
cursor: pointer;
z-index: 1001;
}
.modal-nav {
position: absolute;
top: 50%;
transform: translateY(-50%);
color: white;
font-size: 50px;
cursor: pointer;
padding: 20px;
user-select: none;
}
.modal-nav:hover {
background: rgba(255,255,255,0.1);
}
.modal-prev {
left: 20px;
}
.modal-next {
right: 20px;
}
.modal-info {
position: absolute;
bottom: 20px;
left: 50%;
transform: translateX(-50%);
background: rgba(0,0,0,0.7);
color: white;
padding: 10px 20px;
border-radius: 5px;
font-size: 14px;
}
.view-mode-toggle {
text-align: center;
margin-bottom: 20px;
}
.view-mode-toggle button {
margin: 0 5px;
}
.notification {
position: fixed;
top: 20px;
right: 20px;
background: #28a745;
color: white;
padding: 15px 25px;
border-radius: 5px;
box-shadow: 0 4px 12px rgba(0,0,0,0.3);
z-index: 2000;
animation: slideIn 0.3s ease-out;
}
@keyframes slideIn {
from {
transform: translateX(400px);
opacity: 0;
}
to {
transform: translateX(0);
opacity: 1;
}
}
</style>
</head>
<body>
<div class="main-container">
<div class="header">
<h1>🖼️ 高速画像ビューアー(2画面対応)</h1>
<p>フォルダを選択して画像を一覧表示 - ファイル名を簡単コピー</p>
</div>
<div class="view-mode-toggle">
<button class="btn" onclick="setViewMode(1)">1画面表示</button>
<button class="btn" onclick="setViewMode(2)">2画面表示</button>
</div>
<div class="panels-container" id="panelsContainer">
<!-- パネル1 -->
<div class="panel" id="panel1">
<div class="panel-header">
<span>フォルダ 1</span>
<span class="panel-number">Panel 1</span>
</div>
<div class="controls">
<div class="control-group">
<input type="file" id="folderInput1" webkitdirectory directory multiple>
<button class="btn" onclick="document.getElementById('folderInput1').click()">
📁 フォルダを選択
</button>
</div>
<div class="control-group">
<label>縦幅:</label>
<div class="slider-group">
<input type="range" id="heightSlider1" min="50" max="500" value="200" oninput="updateHeight(1)">
<input type="number" id="heightInput1" min="50" max="500" value="200" onchange="updateHeightFromInput(1)">
<span>px</span>
</div>
<label style="margin-left: 15px;">間隔:</label>
<div class="slider-group">
<input type="range" id="paddingSlider1" min="0" max="50" value="10" oninput="updatePadding(1)">
<input type="number" id="paddingInput1" min="0" max="100" value="10" onchange="updatePaddingFromInput(1)">
<span>px</span>
</div>
</div>
</div>
<div class="info-bar">
<div class="path" id="currentPath1">フォルダが選択されていません</div>
<div class="count" id="imageCount1">画像数: 0</div>
</div>
<div class="gallery" id="gallery1">
<div class="empty-state">
<div class="icon">📂</div>
<p>フォルダを選択してください</p>
</div>
</div>
</div>
<!-- パネル2 -->
<div class="panel" id="panel2" style="display: none;">
<div class="panel-header">
<span>フォルダ 2</span>
<span class="panel-number">Panel 2</span>
</div>
<div class="controls">
<div class="control-group">
<input type="file" id="folderInput2" webkitdirectory directory multiple>
<button class="btn" onclick="document.getElementById('folderInput2').click()">
📁 フォルダを選択
</button>
</div>
<div class="control-group">
<label>縦幅:</label>
<div class="slider-group">
<input type="range" id="heightSlider2" min="50" max="500" value="200" oninput="updateHeight(2)">
<input type="number" id="heightInput2" min="50" max="500" value="200" onchange="updateHeightFromInput(2)">
<span>px</span>
</div>
<label style="margin-left: 15px;">間隔:</label>
<div class="slider-group">
<input type="range" id="paddingSlider2" min="0" max="50" value="10" oninput="updatePadding(2)">
<input type="number" id="paddingInput2" min="0" max="100" value="10" onchange="updatePaddingFromInput(2)">
<span>px</span>
</div>
</div>
</div>
<div class="info-bar">
<div class="path" id="currentPath2">フォルダが選択されていません</div>
<div class="count" id="imageCount2">画像数: 0</div>
</div>
<div class="gallery" id="gallery2">
<div class="empty-state">
<div class="icon">📂</div>
<p>フォルダを選択してください</p>
</div>
</div>
</div>
</div>
</div>
<div class="modal" id="modal" onclick="closeModal()">
<span class="modal-close" onclick="closeModal()">×</span>
<span class="modal-nav modal-prev" onclick="event.stopPropagation(); showPrevImage()">‹</span>
<img id="modalImage" onclick="event.stopPropagation()">
<span class="modal-nav modal-next" onclick="event.stopPropagation(); showNextImage()">›</span>
<div class="modal-info" id="modalInfo"></div>
</div>
<script>
// パネルごとのデータ
const panels = {
1: { files: [], folderPath: '', height: 200, padding: 10, currentIndex: 0 },
2: { files: [], folderPath: '', height: 200, padding: 10, currentIndex: 0 }
};
let currentPanel = 1;
let viewMode = 1; // 1画面 or 2画面
// 表示モード切り替え
function setViewMode(mode) {
viewMode = mode;
const panel1 = document.getElementById('panel1');
const panel2 = document.getElementById('panel2');
if (mode === 1) {
panel1.classList.add('single-mode');
panel2.style.display = 'none';
} else {
panel1.classList.remove('single-mode');
panel2.style.display = 'block';
}
}
// 通知表示
function showNotification(message) {
const notification = document.createElement('div');
notification.className = 'notification';
notification.textContent = message;
document.body.appendChild(notification);
setTimeout(() => {
notification.remove();
}, 2000);
}
// フォルダ選択
[1, 2].forEach(panelNum => {
document.getElementById(`folderInput${panelNum}`).addEventListener('change', function(e) {
const files = Array.from(e.target.files);
if (files.length === 0) return;
panels[panelNum].files = files.filter(file => file.type.startsWith('image/'));
if (files[0].webkitRelativePath) {
const pathParts = files[0].webkitRelativePath.split('/');
panels[panelNum].folderPath = pathParts.slice(0, -1).join('/');
} else {
panels[panelNum].folderPath = 'ローカルフォルダ';
}
document.getElementById(`currentPath${panelNum}`).textContent = panels[panelNum].folderPath;
document.getElementById(`imageCount${panelNum}`).textContent = `画像数: ${panels[panelNum].files.length}`;
displayImages(panelNum);
});
});
// 高さ調整
function updateHeight(panelNum) {
panels[panelNum].height = parseInt(document.getElementById(`heightSlider${panelNum}`).value);
document.getElementById(`heightInput${panelNum}`).value = panels[panelNum].height;
displayImages(panelNum);
}
function updateHeightFromInput(panelNum) {
let value = parseInt(document.getElementById(`heightInput${panelNum}`).value);
if (value < 50) value = 50;
if (value > 500) value = 500;
panels[panelNum].height = value;
document.getElementById(`heightSlider${panelNum}`).value = value;
document.getElementById(`heightInput${panelNum}`).value = value;
displayImages(panelNum);
}
// パディング調整
function updatePadding(panelNum) {
panels[panelNum].padding = parseInt(document.getElementById(`paddingSlider${panelNum}`).value);
document.getElementById(`paddingInput${panelNum}`).value = panels[panelNum].padding;
displayImages(panelNum);
}
function updatePaddingFromInput(panelNum) {
let value = parseInt(document.getElementById(`paddingInput${panelNum}`).value);
if (value < 0) value = 0;
if (value > 100) value = 100;
panels[panelNum].padding = value;
document.getElementById(`paddingSlider${panelNum}`).value = value;
document.getElementById(`paddingInput${panelNum}`).value = value;
displayImages(panelNum);
}
// 画像表示
function displayImages(panelNum) {
const gallery = document.getElementById(`gallery${panelNum}`);
const files = panels[panelNum].files;
gallery.innerHTML = '';
if (files.length === 0) {
gallery.innerHTML = `
<div class="empty-state">
<div class="icon">🖼️</div>
<p>画像ファイルがありません</p>
</div>
`;
return;
}
gallery.innerHTML = '<div class="loading">画像を読み込み中...</div>';
setTimeout(() => {
gallery.innerHTML = '';
files.forEach((file, index) => {
const reader = new FileReader();
reader.onload = function(e) {
const img = new Image();
img.onload = function() {
const aspectRatio = this.width / this.height;
const thumbWidth = Math.round(panels[panelNum].height * aspectRatio);
const wrapper = document.createElement('div');
wrapper.className = 'image-wrapper';
const div = document.createElement('div');
div.className = 'image-item';
div.style.width = thumbWidth + 'px';
div.style.height = panels[panelNum].height + 'px';
div.style.margin = (panels[panelNum].padding / 2) + 'px';
div.onclick = () => openModal(panelNum, index);
const imgElement = document.createElement('img');
imgElement.src = e.target.result;
imgElement.alt = file.name;
imgElement.title = file.name;
div.appendChild(imgElement);
// コピーボタン - ファイル名のみコピー
const copyBtn = document.createElement('button');
copyBtn.className = 'copy-btn';
copyBtn.textContent = '📋 名前をコピー';
copyBtn.onclick = (event) => {
event.stopPropagation();
copyFileName(file, copyBtn);
};
wrapper.appendChild(div);
wrapper.appendChild(copyBtn);
gallery.appendChild(wrapper);
};
img.src = e.target.result;
};
reader.readAsDataURL(file);
});
}, 10);
}
// ファイル名をコピー
function copyFileName(file, button) {
const fileName = file.name;
navigator.clipboard.writeText(fileName).then(() => {
button.textContent = '✓ コピーしました!';
button.classList.add('copied');
showNotification('📋 ファイル名をコピーしました: ' + fileName);
setTimeout(() => {
button.textContent = '📋 名前をコピー';
button.classList.remove('copied');
}, 2000);
}).catch(err => {
alert('コピーに失敗しました: ' + err);
});
}
// モーダル表示
function openModal(panelNum, index) {
currentPanel = panelNum;
panels[panelNum].currentIndex = index;
showImageInModal(panelNum, index);
document.getElementById('modal').classList.add('active');
}
function showImageInModal(panelNum, index) {
const file = panels[panelNum].files[index];
const reader = new FileReader();
reader.onload = function(e) {
document.getElementById('modalImage').src = e.target.result;
document.getElementById('modalInfo').textContent =
`${file.name} (${index + 1}/${panels[panelNum].files.length})`;
};
reader.readAsDataURL(file);
}
function closeModal() {
document.getElementById('modal').classList.remove('active');
}
function showPrevImage() {
const panel = panels[currentPanel];
panel.currentIndex = (panel.currentIndex - 1 + panel.files.length) % panel.files.length;
showImageInModal(currentPanel, panel.currentIndex);
}
function showNextImage() {
const panel = panels[currentPanel];
panel.currentIndex = (panel.currentIndex + 1) % panel.files.length;
showImageInModal(currentPanel, panel.currentIndex);
}
// キーボード操作
document.addEventListener('keydown', function(e) {
if (document.getElementById('modal').classList.contains('active')) {
if (e.key === 'Escape') closeModal();
if (e.key === 'ArrowLeft') showPrevImage();
if (e.key === 'ArrowRight') showNextImage();
}
});
</script>
</body>
</html>